usdtOFT TON
The capabilities of each entity are as follows.
Permissionless
There are no assertions for the caller of the Layerzero::OP::LZ_RECEIVE_PREPARE
opcode. A description of the function of the opcode is best given by the following comment in handlerOApp.fc:
;; lzReceive follows a two-phase commit pattern in TON
;; first, a permissionless call is made to lzReceivePrepare
;; failure => permissionless retry at endpoint
;; second, the OApp performs a gas assertion and sends a clear request to the Packet
;; failure => permissionless retry from step 1 at endpoint
;; third, the Packet locks itself and notifies the OApp to execute the request
;; failure => OApp must unlock the Packet and retry from step 1
;; finally, the OApp clears and destroys the message
;; failure => can be ignored, but the OApp will be blocklisted by the executor
The opcode ensures the remote peer in the user-controlled packet is correct, asserts that there is enough gas remaining to follow the receive flow, and calls Channel::OP::LZ_RECEIVE_LOCK
on the caller — which is intended to be the channel contract (in LayerZero TON). If the caller is not the channel, there is no effect of the call.
Because the opcode makes no state changes, there is no harm in it being permissionless.
LayerZero channel
The LayerZero channel contract has the ability to call the following opcodes:
Layerzero::OP::LZ_RECEIVE_EXECUTE
Layerzero::OP::CHANNEL_SEND_CALLBACK
Layerzero::OP::BURN_CALLBACK
Layerzero::OP::NILIFY_CALLBACK
For information on the threat model of LayerZero, please refer to our LayerZero Endpoint V2 TON report (unreleased as of the time of this writing).
Planner
The planner has the ability to call the OP::SendCredits
and OP::SetCostAsserts
opcodes. This role typically handles cross-chain liquidity management by moving credits between different chains and updating the cost-related parameters used for gas and price checks.
OP::SendCredits
. The planner can invoke this opcode to transfer credits (liquidity) across supported chains. While the planner does not hold the authority to withdraw actual USDT to an external address (that remains under the owner’s authority), it can reorder and rebalance the distribution of credits among chains. In a worst-case scenario where the planner’s key is compromised or the planner acts maliciously, it could excessively drain or redirect credits from one chain to another, resulting in local liquidity shortages or disruptions for cross-chain operations.OP::SetCostAsserts
. The planner can also modify cost-related assertions; raising these values too high may lead to unjustified transaction failures, whereas setting them too low could allow underpayment of fees and potentially disrupt cross-chain guarantees. When setting cost asserts, the user-input object is sanitized to ensure that only valid integer and structured fields are stored, preventing injection of malformed data structures.
Overall, the planner’s abilities facilitate dynamic adjustments to cross-chain liquidity and cost configurations. However, because the planner cannot directly withdraw USDT to an external address, there is a baseline safeguard against total liquidity theft. The ultimate power to withdraw tokens or reassign the planner resides with the owner, thereby mitigating full control risks associated with the planner role.
Owner
The owner has the ability to call the following opcodes:
OP::WithdrawLocal
. The owner can withdraw USDT from the contract balance provided there are enough credits for the local EID (i.e., ifdepositLocal
is called or sending bounces).
OP::WithdrawRemote
. The owner can send USDT to an arbitrary remote address, limited by the credits for that remote EID.
OP::WithdrawFees
. The owner can withdraw up to thefeeBalance
balance.
OP::RecoverUsdtInit
andOP::RecoverUsdt
. The owner can arbitrarily withdraw USDT from the contract balance after a timelock period. Note that we reported a bypass for this in Finding .
OP::SetPlannerAddress
. Because of this, the owner inherits all of the abilities of the planner.
OP::SetGasAsserts
. The user-input object is sanitized. This opcode allows the owner to configure the gas assertions for sending and receiving specific types of messages.
OP::SetFee
. The user-input object is sanitized. This allows the owner to set an arbitrary fee at any time, provided it is an unsigned integer less than 100%.
The following opcodes are for managing the LayerZero OApp (handled in handlerOApp.fc). They are not specific to USDT OFT, and all functions are typically included in all OApp implementations on TON.
OP::DeployChannel
. This deploys a LayerZero channel contract. This is typically done when intending to support a new path.
OP::DeployConnection
. This instructs the messaging library to deploy a new, sharded LayerZero UlnConnection contract (deployUlnConnection
in ulnManager) or a new SmlConnection contract (deployConnection
in smlManager). This is typically done when intending to support a new path.
OP::ForceAbort
. This aborts a pending send request. This capability is intended to prevent the send queue from being blocked if thesendRequestQueue
(aDeterministicInsertionCircularQueue
structure in LayerZero's channel contract) fills up for any reason. Note that when send requests are aborted, the path will not be refunded credits, and the sender will not be refunded fees or USDT on the local chain.
OP::Burn
. This permanently discards an inbound message that the local OApp no longer intends to execute. Under the hood, the channel marks the message as "executed" (so it cannot be triggered again), effectively burning away that nonce from future processing. This is useful when a message is deemed obsolete or invalid, ensuring it never reenters the execution flow.
OP::Nilify
. This removes an inbound message from the channel's queue without marking it as executed. This effectively frees up the message slot so it no longer blocks subsequent messages, but it does not treat it as having been processed. It is helpful in scenarios where the message must be cleared out (e.g., it became irrelevant) but not formally executed in a business-logic sense.
OP::SetPeer
. This opcode configures valid outbound and inbound peer addresses for cross-chain interactions given an EID.Outbound. For each
eid
, the contract uses an internally maintainedeid
→peerAddress
mapping when sending cross-chain messages, eliminating the need for hardcoded addresses and simplifying updates.Inbound. Upon receiving a message, the contract checks whether the sender matches the locally stored
peerAddress
. If they differ, the message is deemed invalid and rejected.This source verification is crucial for ensuring that cross-chain security-only messages from the recognized
peerAddress
are considered legitimate.OP::SetEnforcedOptions
. ThesetEnforcedOptions
function defines mandatory option configurations for cross-chain messages associated with a specific(msgType,dstEid)
. These enforced options can restrict particular aspects of message handling — for example, requiring a specific options version or disallowing certain advanced features. By enforcing these settings at the OApp contract level, any subsequent logic that handles that message type must comply with these requirements, thereby ensuring that the processing flow aligns with the intended security and functional policies.
OP::SetLzConfig
. The owner may modify the endpoint or messaging-library configuration at any time. A full description of the centralization risks of this ability is documented in our LayerZero Endpoint V2↗ (EVM) report.
Tentative owner
The tentative owner may accept ownership by using the OP::ClaimOwnership
opcode. However, the owner must first call transferOwnership
with the tentative owner's address.