Type: Stablecoins
Timeline: June 18, 2025 → July 11, 2025
Languages: Solidity
Findings
Total issues: 7 (2 resolved)
Critical: 0 (0 resolved)
High: 0 (0 resolved)
Medium: 1 (1 resolved)
Low: 1 (0 resolved)
Notes & Additional Information
5 notes raised (1 resolved)
OpenZeppelin audited the Everdawn-Labs/xaut-ton-everdawn repository at commit 1d36237.
In scope were the following files:
 src
├── BamOFT
│   ├── handler.fc
│   └── main.fc
├── Token
│   ├── gas.fc
│   ├── jetton-minter.fc
│   ├── jetton-utils.fc
│   ├── jetton-wallet.fc
│   ├── jetton.tlb
│   ├── op-codes.fc
│   ├── stdlib.fc
│   └── workchain.fc
├── TokenAdmin
│   ├── handler.fc
│   ├── interface.fc
│   ├── main.fc
│   └── storage.fc
├── modules
│   ├── OFT
│   │   ├── consts.fc
│   │   ├── handlerOFT.fc
│   │   ├── interface.fc
│   │   └── storage.fc
│   └── oApp
│       ├── handlerOApp.fc
│       ├── interface.fc
│       └── storage.fc
└── oftStorages
    ├── classAutoload.fc
    └── main.fc
XAUt0 is a bridged version of Tether’s XAUt, designed to extend XAUt availability to blockchains where it is not natively issued. Powered by LayerZero’s Omnichain Fungible Token (OFT) standard, it maintains a 1:1 relationship between locked XAUt on Ethereum and the minted tokens on supported chains. On TON, XAUt0 is implemented as a Jetton using the Jetton fungible‑token standard for local accounting and integrates LayerZero’s Omnichain Fungible Token (OFT) framework. This allows the Jetton supply on TON to expand or contract as users bridge XAUt0 to and from other LayerZero‑enabled networks.
The Ton‑specific design splits the logic across three main smart contracts:
| Contract | Description | 
|---|---|
| Jetton Wallet | Modified jetton-wallet that supports OFT transfers | 
| Jetton Minter | Modified version of a jetton-minter contract that supports OFT transfers | 
| BamOFT | Bridges to LayerZero: it receives transfer_notification events from the jetton-minter, packages them into LayerZero messages, and mints or releases XAUt0 when the lzReceive callbacks arrive. | 
Send (TON ➜ other chains):
transfer_notification to BamOFT.lzSend.Receive (other chains ➜ TON):
lzReceive message to BamOFT, which on TON uses a two‑phase commit (prepare then execute) instead of the single‑phase flow used on EVM chains.mint message to the Jetton Minter.The following key differences between the standard Jetton Minter and the Modified Jetton Minter were identified:
mint_to_authority address (BamOFT) is stored alongside admin_address.receive_jettons function. It receives incoming Jettons from wallets and sends a transfer_notification to BamOFT.mint opcode now accepts calls from BamOFT as well as the admin.burn_jettons in the wallet can only be invoked by the master (minter), not by the wallet owner.status field lets the minter lock outgoing (bit 0) or incoming (bit 1) transfers. The master address can still move funds even when a lock has been set.During the audit, the following observations and trust assumptions were made:
Forwarding success: Every transfer_notification sent from the Jetton Minter to BamOFT is expected to clear the TVM compute phase. If BamOFT reverts during that phase because of an invalid payload, a contract pause, a rate‑limit check, or an out‑of‑gas condition, the message is discarded after total_supply has already been reduced, so the user’s tokens are permanently burned.
Client‑side preparation and fee coverage: Messages that are malformed or under‑funded revert, and the protocol does not add missing fees or refund them. Off‑chain tooling (front end or SDK) must:
Admin authority: The admin of the Jetton Minter and the admin of the OFT contract can upgrade code and data without on‑chain guards. A compromised or malicious admin could deploy arbitrary logic or change critical parameters. The security model assumes these administrators act honestly and will perform multiple sanity checks before upgrading code or data.
LayerZero dependency: Correct operation across chains depends on LayerZero endpoint contracts and libraries behaving as specified. The model assumes these external components work as intended.
Throughout the codebase, the following privileged roles were identified:
call_to from any user wallet, lock or unlock wallets, change metadata URI, change or drop the admin, and upgrade minter code and data.The claimTon admin function pushes a payment action to the list of actions that has type action::payment::name. However, the _executeAction function of the TokenAdmin handler does not include logic to process this action type and will instead throw an invalidActionType error. This makes it impossible for the admin to retrieve any excess TON stored in the contract.
Consider updating the _executeAction function to account for the payment action type.
Update: Resolved in pull request #2 at commit f897c88. The Everdawn team stated:
Fixed. We have updated
_executeActionto handleaction::payment::name. Note that this is an admin-only contract that is currently not deployed as the jetton’s admin. So, no action is required.
forward_ton_amount in jetton-minter::receive_jettonsThe receive_jettons function silently burns tokens whenever the incoming transfer sets forward_ton_amount to 0. The minter wallet credits itself with the received jetton_amount and updates total_supply. However, because no transfer_notification is emitted, the OFT contract never receives the tokens, and the function simply saves state and returns.
Therefore, a transfer becomes equivalent to a burn if the sender omits the forwarding TON, even though explicit burning is restricted to the Jetton master and should not be available to users. This silent‑burn path not only leaves users at risk of permanently losing their Jettons but also clouds the protocol’s supply guarantees, since tokens can be burned without the master’s explicit burn routine. If users are not meant to burn tokens, the wallet should bounce any transfer with a zero forward_ton_amount.
Consider bouncing any internal transfer whose forward_ton_amount is 0 so that the transaction fails visibly instead of silently burning tokens. Alternatively, consider clearly documenting this behavior. Doing so will help integrators understand that a forward_ton_amount of 0 turns a transfer into a user‑initiated burn.
Update: Acknowledged, not resolved. The Everdawn team stated:
We have decided not to fix this as it only affects the UX.
When a transfer_notification is sent to the BamOFT handler, the message type must be OFTSend::NAME for the sendOFT call to be performed. If not, no actions are performed, which results in any remaining message value being refunded to the origin. However, this behavior is not correctly documented, with the comment stating that the empty actions return will not ever be hit.
Consider clarifying the comment to properly describe the intended process when a transfer_notification is sent with an incorrect message type.
Update: Acknowledged, not resolved.
jetton‑wallet::burn_jettonsThe burn_jettons function validates that the caller is the Jetton minter by throwing error::not_owner when the check fails. This conflates two separate concepts: wallet ownership and master‑minter authority. Since the codebase already defines error::not_minter for “only master may call” situations, reusing error::not_owner obscures the real cause of the reversion and may cause confusion for integrators.
Consider replacing error::not_owner with the semantically correct error::not_minter.
Update: Acknowledged, not resolved. The Everdawn team stated:
This implementation matches the stablecoin-contract standard. We do not want to change the behavior.
The checkPermissions() function returns without any validation when op equals BaseInterface::OP::EVENT. As a result, the subsequent logic in main() always reaches the logic that emits the event. Therefore, any external account can send a message with op = EVENT and force the contract to emit an event, even if the contract is uninitialized or the caller has no special privileges. Allowing anyone to emit events lets attackers spam the event log, potentially triggering off‑chain systems that rely on authentic contract events and undermining trust in emitted data.
Consider adding an authorization check (e.g., verifying the caller is the OFT contract or another whitelisted entity) before processing OP::EVENT. Alternatively, consider clearly documenting the behavior and explaining how integrators can distinguish legitimate events (e.g., by validating the sender address).
Update: Resolved. The Everdawn team stated:
This is by design. Events are indexed by src and dst, so it is only an off-chain concern if clients index by dst alone.
When a setter function does not check if the value being set is different from the existing one, it becomes possible to set the same value repeatedly, creating a possibility for event spamming. Repeated emission of identical events can also confuse off-chain clients.
Throughout the codebase, multiple instances of code allowing for duplicate event emissions were identified:
Consider adding a check to revert the transaction if the value being set is identical to the existing value.
Update: Acknowledged, not resolved. The Everdawn team stated:
All affected functions are admin-only. This means that the admin can event-spam its own listeners.
The receive_jettons function in the Jetton‑Minter subtracts the received Jettons from total_supply and forwards them to OFT contract in a non‑bounceable transfer_notification message. The selected send‑mode only bounces when OFT fails during its action phase.
Any compute‑phase error, such as an invalid payload, rate‑limit rejection, contract pause, or out‑of‑gas condition, reverts the transfer without a bounce and effectively burns the tokens. Since the supply has already been reduced, the user’s tokens are lost forever and the on‑chain supply of TON no longer matches the USDT locked on Ethereum. The risk is amplified by TON’s asynchronous execution: a pause or rate‑limit update sent after the user’s transfer can still be processed first if it lands in an earlier block, triggering the failure scenario.
Consider sending the transfer_notification as a bounceable message. This way, if OFT were to fail in its compute phase, the value is returned automatically, letting the Minter restore total_supply and refund the Jettons to the user.
Update: Acknowledged, not resolved. The compute phase extends beyond the try/catch block. As such, out-of-gas or invalid-payload errors can occur before the try/catch block executes. The Everdawn team stated:
sendOFTis wrapped in atry/catchblock. Failures trigger refund and user funds are not lost.
XAUt0 implements a LayerZero OFT‑enabled Jetton on TON that mirrors Ethereum XAUt via cross‑chain minting and burning. The audit uncovered one medium‑severity issue where excess TON can become irretrievable in the admin contract. In addition, several low‑severity findings and informational notes were also reported. While the codebase was found to be well-structured, it could benefit from more descriptive code comments. The Everdawn team is appreciated for being highly responsive and providing valuable support throughout the audit process.