Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · January 2025

OFT on Ink

for USDT0

USDT0 engaged Guardian to review the security of their Tether OFT token on Ink. From the 2nd of January to the 7th of January, a team of 3 auditors reviewed the source code in scope.

Published
Review window
January 2 to 7, 2025
Language
Solidity
Chains
Ink
Sector
Stablecoins
  • 0 Critical
  • 0 High
  • 0 Medium
  • 7 Low
  • 7 Informational

5 resolved · 9 acknowledged

Scope

Overview

USDT0 engaged Guardian to review the security of their Tether OFT token on Ink. From the 2nd of January to the 7th of January, a team of 3 auditors reviewed the source code in scope.

Findings 14

  1. L-01 Low Transfers To Tether Contract Block Messages Validation Resolved
    Location
    Global

    Description

    The Tether token does not allow transfers or mints to the token address in the _beforeTokenTransfer function. However in the OUpgradeable contract there is no validation which prevents users from initiating cross-chain OFT transfers which would send tokens to the destination chain Tether token contract.

    As a result any initiated OFT transfers with the to address as the Tether token on the destination chain will be stuck in the destination chain inbox and be un-executable, effectively trapping the users funds.

    Recommendation

    This may be resolved by storing the address of the Tether token contract on each corresponding dstEid in a mapping and validating that the to address is not the destination’s Tether contract by overriding the send function.

    Alternatively, the _credit function could include logic to send the bridged tokens to a trusted holding address if the to address is the Tether token contract.

    If no code changes should be implemented for this issue, be sure to clearly document and warn users of this risk.

    Resolution

    USDT0 Team: Fixed with frontend validation.

  2. L-02 Low OFT Transfers Avoid Fees Warning Acknowledged
    Location
    OAdapterUpgradeable.sol

    Description

    In the default _debit implementation for the OFTAdapterUpgradeable the amountReceivedLD is the same as the amountSentLD which is transferred from the user.

    However if fees are enabled for the Tether token implementation at 0xdac17f958d2ee523a2206206994597c13d831ec7 on Ethereum then the amount received by the Adapter contract on Ethereum will be less than the amountReceivedLD which the user is credited with on the destination chain.

    Firstly, this allows users to avoid the transfer tax on Ethereum. Secondly this can create balance underflow issues for users transferring their Tether back to Ethereum.

    Consider the following scenario:

    • The transfer tax is assigned as 1% on Ethereum
    • User A uses OFT send to transfer 100 USDT from Ethereum to Arbitrum
    • The Adapter contract receives 99 USDT and the 1 USDT fee is sent to the Tether owner address
    • The Adapter contract balance is now 99 USDT
    • User A receives 100 USDT on Arbitrum
    • User A sends their 100 USDT back to Ethereum
    • User A’s message execution on Ethereum reverts and is stuck in the message channel because the

    Adapter contract only holds 99 USDT

    • User A’s 100 USDT are effectively lost until more USDT is added to the Adapter contract allowing

    them to execute the message

    Recommendation

    Consider overriding the _debit and _credit functions of the OFTAdapterUpgradeable to account for the Tether token transfer fees if they are enabled by using pre and post transfer balance checks. Otherwise ensure that the fees are never enabled for the USDT token on Ethereum.

    Resolution

    USDT0 Team: Acknowledged.

  3. L-03 Low EOA Signatures Unexpectedly Become Invalid Unexpected Behavior Acknowledged
    Location
    SignatureChecker.sol: 47

    Description

    In the isValidSignatureNow function of the SignatureChecker library an isContract bytecode check is performed to determine whether a normal EOA ECDSA signature validation should occur with ECRECOVER or a ERC1271 contract signature validation should be performed.

    However in the future, with the implementation of eip 7702 or eip 7377 it may be possible for EOAs to house bytecode, in which case the EOA ECDSA signature validation can no longer be performed for these EOAs.

    It is likely that many EOAs which opt to house bytecode will not have functionality to support ERC1271 contract signatures and are therefore rendered unable to sign messages for the USDT token.

    Furthermore, existing signatures that have been issued from EOAs and remain unused become unexpectedly invalid as soon as bytecode is written to them.

    Recommendation

    Consider always performing the ECDSA signature validation and opting to check the ERC1271 signature if the ECDSA signature verification fails, similarly to the OpenZeppelin SignatureChecker library.

    Resolution

    USDT0 Team: Acknowledged.

  4. L-04 Low Incorrect Decimal Configuration Allowed Best Practices Resolved
    Location
    OUpgradeable.sol: 23

    Description

    In the OUpgradeable contract the constructor accepts a decimals parameter which determines the conversion from shared decimals to local chain decimals during the reception of tokens on the destination chain.

    There is no validation which ensures that the configured token has matching decimals to the one provided.

    If a misconfiguration were to occur during deployment then a Critical issue could arise where USDT amounts are delivered on the affected destination chain at orders of magnitude higher than the amount sent.

    Recommendation

    Consider querying the token decimals directly instead of specifying them in the constructor similar to how it is done in the OFTAdapterUpgradeable LayerZero contract with IERC20Metadata(_token).decimals().

    Resolution

    USDT0 Team: The issue was resolved in commit 7dd2007.

  5. L-05 Low Burned OFTExtension Tokens Leaves USDT In Adapter Unexpected Behavior Acknowledged
    Location
    Global

    Description

    The OFTExtension token has methods to burn token amounts outside of OFT transfers such as the redeem and destroyBlockedFunds onlyOwner functions.

    When OFTExtension tokens are burned on remote chains, the corresponding USDT tokens which back these burned tokens remain in the OAdapterUpgradeable contract on Ethereum.

    This behavior can be misleading as the totalSupply of USDT on Ethereum can misrepresent all of the USDT which is able to be bridged back to Ethereum.

    Recommendation

    If this behavior is not desired than be sure to burn the corresponding amount of USDT from the OAdapterUpgradeable contract on Ethereum when burning OFTExtension amounts on remote chains.

    Otherwise be aware of this discrepancy when relying on the totalSupply of USDT or reading the balance of the OAdapterUpgradeable contract on Ethereum.

    Resolution

    USDT0 Team: Acknowledged.

  6. L-06 Low Msg.value Is Lost With lzReceive Unexpected Behavior Acknowledged
    Location
    OAdapterUpgradeable.sol

    Description

    If users specify msg.value in the message execution options, this value will be passed by the executor while calling lzReceive on the destination chain. The value will be lost as it just ends up as a balance of OUpgradeable or OAdapterUpgradeable with no way of retrieving it.

    Recommendation

    The balance of OUpgradeable or OAdapterUpgradeable can be retrieved by adding an admin function to transfer the balance to the owner. Otherwise, make sure to document this behavior in the docs.

    Resolution

    USDT0 Team: Acknowledged.

  7. L-07 Low USDT Pause Causes Cross-chain Messages To Fail Unexpected Behavior Acknowledged
    Location
    Global

    Description

    The USDT contract on Ethereum can pause transfers. If this happens, any in-flight messages will fail due to the inability to transfer USDT from the OAdapterUpgradeable contract.

    Recommendation

    If USDT is paused on Ethereum, cross-chain messages and bridged USDT should be paused on all the connected chains.

    Resolution

    USDT0 Team: Acknowledged.

  8. I-01 Informational Unnecessary Imports Best Practices Resolved
    Location
    OUpgradeable.sol: 4

    Description

    The OUpgradeable contract file includes several unnecessary imports from the @layerzerolabs/oft-evm-upgradeable package which are currently unused:

    • MessagingFee
    • SendParam
    • OFTReceipt

    Recommendation

    Remove these unnecessary imports.

    Resolution

    USDT0 Team: The issue was resolved in commit 85787a7.

  9. I-02 Informational Missing disableInitializers Best Practices Resolved
    Location
    Global

    Description

    In the OFTAdapterUpgradeable and OUpgradeable contracts the constructors do not include calls to _disableInitializers to disable the initializer functions from being called on the implementation contract directly.

    This is not a significant risk as the upgradeable contracts are not UUPS proxies, however it is a best practice to disable the initializers for upgradeable contracts.

    Recommendation

    Consider including a call to the _disableInitializers function in the constructor for the OFTAdapterUpgradeable and OUpgradeable contracts.

    Resolution

    USDT0 Team: The issue was resolved in commit 18c2b4d.

  10. I-03 Informational Memory Arguments Can Be Calldata Optimization Acknowledged
    Location
    OFTExtension.sol: 50

    Description

    The updateNameAndSymbol function accepts two string memory parameters which are never modified and only written to storage. These parameters do not need to be copied into memory and can instead remain as references to calldata.

    Recommendation

    Consider using the calldata location for the _name and _string parameters in the updateNameAndSymbol function.

    Resolution

    USDT0 Team: Acknowledged.

  11. I-04 Informational Style Inconsistencies Informational Resolved
    Location
    OFTExtension.sol

    Description

    Indentation of functions in the TetherTokenOFTExtension contract is inconsistent. Inconsistent parameter naming convention in TetherTokenOFTExtension contract: mint function uses _destination while burn uses from without underscore.

    Recommendation

    Fix the style inconsistencies by formatting the code properly and having a consistent naming convention.

    Resolution

    USDT0 Team: The issue was resolved in commit f241b88.

  12. I-05 Informational ERC20PermitUpgradeable Draft Used Best Practices Acknowledged
    Location
    ERC20PermitUpgradeable.sol

    Description

    The version of ERC20PermitUpgradeable used for the TetherToken contract is an outdated draft state version from OpenZeppelin.

    Though no issues have been identified with this draft version of the ERC20PermitUpgradeable contract, the most up to date non-draft version of the OpenZeppelin ERC20PermitUpgradeable contract should ideally be used, accompanied by a Solidity version bump to a more recent version of Solidity.

    Recommendation

    Consider updating the version of the OpenZeppelin ERC20PermitUpgradeable contract that is used as well as the version of Solidity that is used.

    Resolution

    USDT0 Team: Acknowledged.

  13. I-06 Informational Enforced Execution Options Recommendations Best Practices Acknowledged
    Location
    layerzero.config.ts.sol

    Description

    LayerZero configuration files set the gas value for ExecutorOptionType.LZ_RECEIVE and ExecutorOptionType.COMPOSE options to 80k. msgType equal to 1 with option type ExecutorOptionType.LZ_RECEIVE is the case when sendCompose is not called on the destination chain and will have lower gas requirements than the msgType equal to 2 with option type ExecutorOptionType.LZ_RECEIVE which does call sendCompose.

    The maximum message bytes size in the default LayerZero send library is 10_000 bytes. The worst case scenario is when sendCompose is called on the destination chain within the lzReceive function with the maximum composed message size -- which is 9980 bytes as each message encodes the amount, msg.sender and receiver. This will add significantly to the total gas due to manipulating bytes in memory and saving the messages in the LayerZero contracts for later execution.

    With very long messages, the total gas requirements for lzReceive can be as high as ~230k gas. msgType equal to 2 with option type ExecutorOptionType.COMPOSE is the case when lzCompose is called on the destination chain in a separate transaction. Anyone is free to define their own contract that implements lzCompose and setting gas to 80k might be too high if the logic inside lzCompose is very simple.

    Recommendation

    Benchmark the gas requirements for each option and message type on mainnet and enforce the gas limit to ensure there are no failed messages. The 80k gas limit for ExecutorOptionType.LZ_RECEIVE with composed messages is insufficient, as testing showed longer messages can exceed this threshold. Consider increasing the limit while taking into account the trade-off between ensuring there are no pending messages and the increased gas costs for users that are sending short messages for compose.

    For example, with 100 bytes of data for a composed message the lzReceive for the non-adapter OFT was measured at over 83,000 gas in testing. Considering that it may be common for users to include messages of around or over 100 bytes with composed messages, you might consider adjusting the enforced gas requirement for composed messages to be 100,000 units to be safest and cover longer composed messages by default.

  14. I-07 Informational OFTExtension Minted Can Be Unbacked Best Practices Acknowledged
    Location
    OFTExtension.sol: 34

    Description

    The system was reviewed with the assumption that USDT tokens get locked on Ethereum and are bridged to other chains, Ink and Berachain.

    If we assume that bridging is allowed in all directions the system should work fine as every USDT on Ink/Berachain must have originated from Ethereum USDT being locked first.

    If there is bridging between Ink/Berachain, tokens are burned on source and minted on destination.

    As the admin of the OFTExtension contract can set any address to mint its tokens, there is a risk of minting tokens that are not backed by USDT on Ethereum.

    Recommendation

    Make sure that the OFTExtension contract only mints tokens that are backed by USDT on Ethereum.

    Resolution

    USDT0 Team: Acknowledged.

More from USDT0

All 20 reports
  1. Stellar Deployment

    2 findings 2 findings: 2 informational
  2. Corn Network Delisting

    1 finding 1 finding: 1 low
  3. Canary Chain Configuration Verification

    4 findings 4 findings: 4 informational
  4. Solana Transaction Verification

    4 findings 4 findings: 1 medium, 3 informational

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote