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

Security review · June 2025

Polygon Upgrade

for USDT0

Guardian's review of Polygon Upgrade for USDT0, published June 2025. The report records 25 findings across 2 review rounds, including 5 low and 20 informational.

Published
Review window
May 12 to 29, 2025
Rounds
Main Review, Remediation Review
Language
Solidity
Chains
Ethereum, Arbitrum, Ink, Hyperliquid, Polygon, Monad, Solana, Stellar
Sector
Stablecoins
  • 0 Critical
  • 0 High
  • 0 Medium
  • 5 Low
  • 20 Informational

10 resolved · 1 partially resolved · 14 acknowledged

Scope

15 files in scope · 563 nSLOC
FilenSLOCLines
contracts/common/AccessControlMixin.sol1518
contracts/common/ContextMixin.sol1828
contracts/common/EIP712Base.sol5070
contracts/common/Initializable.sol1215
contracts/common/NativeMetaTransaction.sol6996
contracts/common/Proxy/IERCProxy.sol34
contracts/common/Proxy/Proxy.sol2938
contracts/common/Proxy/UpgradableProxy.sol81102
contracts/child/ChildToken/IChildToken.sol34
contracts/child/ChildToken/UpgradeableChildERC20/ERC20.sol100328
contracts/child/ChildToken/UpgradeableChildERC20/UChildERC20.sol3243
contracts/child/ChildToken/UpgradeableChildERC20/UChildERC20Proxy.sol810
contracts/child/ChildToken/DappTokens/IERC7802.sol520
contracts/child/ChildToken/DappTokens/UChildUSDT0.sol115165
contracts/child/ChildToken/DappTokens/WithBlockedList.sol2351

Findings 25

Main Review

24 findings · May 12 to 19, 2025
  1. L-01 Low Old Signatures Become Invalid Informational Acknowledged
    Location
    contracts/child/ChildToken/DappTokens/UChildUSDT0.sol:49-50
    Round
    Main Review

    Description

    With the USDT0 upgrade, the token name and domain separator will be updated. As a result, signed meta txs according to the old domain separators, will become invalid immediately after the update.

    This could cause UX issues for users utilizing DEXes for gasless swaps, for example.

    If DEXes are not made aware of this domain separator change, they may continue using the old one, resulting in failed swaps for users.

    Recommendation

    Consider documenting this behavior and informing users that they must sign new permits after the USDT0 upgrade.

  2. L-02 Low Missing ERC-165 Support Best Practices Resolved
    Location
    Global
    Round
    Main Review

    Description

    The EIP 7802 states: The inclusion of ERC-165 provides an additional security check for integrators. By providing the interface identifier through the supportsInterface method, callers can programmatically confirm that the token adheres to the IERC7802 interface.

    Current UChildUSDT0 implementation inherits from IERC7802, supporting crosschain ERC20 transfers. However, this interface does not enforce IERC165, different from the current OFT extension deployments in other chains.

    Recommendation

    Consider adding the supportsInterface function to correctly signal the EIP support, similar to the current deployments:

      function supportsInterface(bytes4 interfaceId) external pure override returns (bool) {
        return interfaceId == type(IERC7802).interfaceId || interfaceId == type(IERC165).interfaceId;
      }
    

    See https://eips.ethereum.org/EIPS/eip-7802

  3. L-03 Low Use Of Vanilla ecrecover Allows Signature Malleability Validation Acknowledged
    Round
    Main Review

    Description

    USDT uses vanilla ecrecover for signer recovery, which is susceptible to malleability due to signature variations.

    This does not cause any immediate damage since the signed permit itself does not change. However, this should still be considered for improvement.

    Recommendation

    Consider using OpenZeppelin's ECDSA library for signer recovery to mitigate malleability risks

  4. L-04 Low UChildUSDT0 Not EIP-2612 Compliant Logical Error Resolved
    Location
    contracts/common/NativeMetaTransaction.sol:26
    Round
    Main Review

    Description

    According to the EIP-2612, compliant contracts must implement 3 new functions in addition to EIP-20:

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external
    function nonces(address owner) external view returns (uint)
    function DOMAIN_SEPARATOR() external view returns (bytes32)
    

    However, UChildUSDT0 has a nonces internal mapping, and uses the getNonce public getter instead. Consequently, the expected permit functionality will be prevented for many wallets and tools that rely on the standard nonces(address) getter.

    Recommendation

    Consider making the nonces mapping public to comply with the EIP.

  5. L-05 Low Lack Of Smart Wallet Support In permit Logical Error Acknowledged
    Location
    contracts/child/ChildToken/DappTokens/UChildUSDT0.sol:62
    Round
    Main Review

    Description

    The permit function uses ecrecover for signature verification, which only supports Externally Owned Accounts (EOAs). This excludes smart contract wallets (e.g., Gnosis Safe, DAOs) that rely on ERC-1271 for validating signatures.

    USDT0’s deployments on Arbitrum and Optimism already support ERC-1271 by using OpenZeppelin’s SignatureChecker, which delegates to isValidERC1271SignatureNow when the signer is a contract. The Polygon version has not adopted this approach.

    This discrepancy breaks cross-chain UX. Smart wallet users who can interact with USDT via permit on Arbitrum or Optimism are blocked when bridging to Polygon, where the same flow fails.

    As account abstraction adoption grows (e.g., EIP-7702 in Pectra), supporting contract wallets is critical. The current implementation limits composability and prevents protocols like CoW Swap from offering gasless approvals to smart wallet users.

    Note: Using SignatureChecker also makes the contract EIP-7377 safe, should it be approved in future. See discussion and commit

    Recommendation

    Update the permit function to use SignatureChecker, as done on Arbitrum and Optimism, to enable ERC-1271 compatibility and ensure cross-chain consistency.

  6. I-01 Informational Inconsistent Use Of Revert Messages Best Practices Resolved
    Location
    Global
    Round
    Main Review

    Description

    UChildUSDT0 contains several error messages starting with UChildUSDT0: while some others use TetherToken:(i.e. multiTransfer) and USD₮0: (AccessControlMixin). This may create some confusion to offchain services, as it might imply it's a different contract.

    Recommendation

    Consider maintaining a consistent error message structure throughout the contract

  7. I-02 Informational Missing Event On setOFTContract Update Best Practices Resolved
    Location
    contracts/child/ChildToken/DappTokens/UChildUSDT0.sol:158
    Round
    Main Review

    Description

    The setOFTContract function updates the OFT contract address, a critical change that should be clearly recorded on-chain. However, no dedicated event is emitted to signal this update. The only emitted event is RoleGranted, which is generic and does not specifically indicate that the OFT contract has changed.

    For comparison, USDT0 implementations on other chains like Arbitrum emit a dedicated LogSetOFTContract event when this change is made.

    Recommendation

    Emit a specific event (e.g., LogSetOFTContract) in the setOFTContract function to explicitly record the new OFT contract address for better transparency and traceability.

  8. I-03 Informational Typo In EIP712Base Best Practices Resolved
    Location
    contracts/common/EIP712Base.sol:23
    Round
    Main Review

    Description

    The comment on line 23 of the EIP712Base contract contains a typo: "... the contractsa that inherits ..." should be corrected to "... the contracts that inherit ...".

    Additionally, the comment format is not consistent with the rest of the codebase, which uses the NatSpec style with /** and */.

    Recommendation

    Update the comment.

  9. I-04 Informational Missing SPDX-License Identifier Best Practices Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    All contracts in the scope, except IERC7802.sol, ERC20.sol, and WithBlockedList.sol, are missing an SPDX-License Identifier.

    Recommendation

    Consider adding SPDX license identifiers to these contracts.

  10. I-05 Informational isContract Probably Soon Redundant Informational Acknowledged
    Location
    contracts/common/Proxy/UpgradableProxy.sol:91-101
    Round
    Main Review

    Description

    The isContract check used in the UpgradableProxy.updateImplementation is probably soon redundant with the Pectra update as EOAs can contain code with EIP-7702.

    Currently, there is a proposal to implement Pectra on Polygon although not accepted yet: https://forum.polygon.technology/t/pip-61-pectra-eips/20783

    Recommendation

    As the issue is present in the proxy contract, it can't be updated. If Polygon implements Pectra, extra care should be taken when updating to a new implementation, making sure the correct address is set, as the isContract check will not work as expected.

  11. I-06 Informational Unnecessary Looping Best Practices Acknowledged
    Round
    Main Review

    Description

    As per our discussions with the USDT team, only one assignment is expected to hold true for DEPOSITOR_ROLE

    Therefore, loops in _setOFTContract or oftContract are unnecessary and could be replaced with a single revoke and assignment.

    Recommendation

    Consider removing the loop and adding a single revoke and assignment to improve code readability and eliminate unnecessary complexity.

  12. I-07 Informational No Initialization Protection Validation Resolved
    Round
    Main Review

    Description

    upgradeToUSDT0 is not access-controlled, meaning anyone can call it.

    Since an equivalent to disableInitializers is not implemented, anyone could call the method on the implementation contract itself and create a parallel identity of the USDT0 token.

    For the real USDT0 token, this is not an issue, as the context is via the proxy. However, given the scale of USDT operations, the possibility of such ambiguity should be addressed.

    Recommendation

    Consider adding a disableInitializers equivalent to UChildUSDT0 to prevent misuse of the implementation contract.

  13. I-08 Informational Removal Of Function Can Break Integrations Informational Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    The update of the USDT contract removes the function withdraw which was previously used to bridge out of Polygon. This may cause unexpected behavior for wallets or applications which relied on calling this function in their smart contracts.

    During the review, a check on all addresses that have previously called withdraw was performed and no smart contracts were found.

    Recommendation

    Consider documenting this risk for users.

  14. I-09 Informational Misleading Comment In crosschainMint Best Practices Resolved
    Location
    contracts/child/ChildToken/DappTokens/UChildUSDT0.sol:72
    Round
    Main Review

    Description

    The crosschainMint function includes a comment stating: "Make sure minting is done only by this function."

    However, there is also a separate mint function that can be called by the admin, allowing them to mint any amount of USDT0 tokens to any address. This contradicts the comment above.

    Recommendation

    If the crosschainMint function is intended to be the sole method for minting tokens, the admin-only mint function should be removed.

    If that's not the case, the comment in crosschainMint should be updated to accurately reflect the intended behavior.

  15. I-10 Informational Missing Storage Gaps Informational Resolved
    Location
    https://github.com/GuardianOrg/pos-portalusdt0polygonupgrade-team2/blob/master/contracts/child/ChildToken/UpgradeableChildERC20/UChildERC20.sol, https://github.com/GuardianOrg/pos-portalusdt0polygonupgrade-team2/blob/master/contracts/child/ChildToken/DappTokens/WithBlockedList.sol
    Round
    Main Review

    Description

    The UChildUSDT0 contract inherits from both UChildERC20 and WithBlockedList. However, neither of these parent contracts defines a storage gap. This omission poses a risk for future upgrades.

    If additional storage variables are introduced in UChildERC20 or WithBlockedList, they may overwrite existing storage slots, potentially colliding with variables such as USDT0_VERSION declared in UChildUSDT0.

    Recommendation

    To ensure safe and upgradeable contract design, add storage gaps (e.g., uint256[50] private __gap;) to both UChildERC20 and WithBlockedList.

  16. I-11 Informational Grouped Issues: Inherited Rom Polygon's pos-portal Contracts Informational Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    These issues are present in contracts inherited or utilized by USDT from Polygon’s pos-portal suite. We advise the USDT team to review and address them based on their prioritization.

    Note that issues in UpgradableProxy.sol cannot be fixed without changing the deployment address and are included for awareness only. Other implementation contracts can be modified if deemed important.

    UpgradableProxy.sol

    1. Missing Double-Step Ownership Transfer Ownership transfer lacks a secure two-step pattern. Recommendation: Document current behavior and validate new owner address thoroughly before transfer.
    2. No Return Value Forwarding in updateAndCall The function does not forward return data from the delegate call. Recommendation: Be aware that return values from called functions will be discarded.
    3. updateAndCall Alters msg.sender Context Typically, upgradeToAndCall-style functions directly delegatecall the implementation. However, UpgradableProxy of pos-portal performs a regular call to itself, which routes through the fallback and only then delegatecalls the implementation. This changes msg.sender in the implementation to the proxy contract itself (address(this)), not the proxy owner. Many meta-transaction patterns treat msg.sender == address(this) as an indicator of a meta-tx and attempt to extract the real sender (msgSender) from calldata. Since updateAndCall does not append any such data, msgSender resolves to the zero address. Recommendation: Be aware of this behavior when using updateAndCall. If the implementation expects meta-tx structure, it may misinterpret the sender as address(0).

    EIP712Base.sol

    • Typo in _setDomainSeperator The word “Seperator” is misspelled; should be “Separator.” Recommendation: Rename to _setDomainSeparator. As it's internal, this change won't break external dependencies.

    NativeMetaTransaction.sol

    1. Unnecessary payable Modifier executeMetaTransaction is marked payable but doesn’t handle native currency. Recommendation: Remove payable to prevent accidental loss of native tokens. If retained for gas saving reasons, clearly document the behavior.
    2. Mismatch Between Comment and Code The function comment states that relayer address is appended to calldata, but this isn’t reflected in the implementation. Recommendation: Either update the comment to match current behavior or modify the code accordingly.
    3. No Deadline or Expiry on Meta Transactions Meta-transactions that fail do not increment the user's nonce, leaving it unused and publicly available. Since there's no deadline parameter (like in permit), any party can replay this transaction indefinitely as long as the nonce is unchanged. to calldata, but this isn’t reflected in the implementation. Recommendation: Consider adding a deadline parameter to limit replayability and mitigate long-term replay risks.

    Recommendation

    NA

  17. I-12 Informational OpenZeppelin v3.4.0 Recommendation Informational Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    The current codebase uses Solidity version 0.6.6 and OpenZeppelin Contracts v3.1.0. To improve security and stability, we refer to the official recommendation from OpenZeppelin, which states that version 3.4.0 is the latest and most appropriate version for use with Solidity 0.6.x.

    Recommendation

    Update OpenZeppelin Contracts from v3.1.0 to v3.4.0.

  18. I-13 Informational Unnecessary String Concatenation Gas Optimization Resolved
    Location
    contracts/child/ChildToken/DappTokens/UChildUSDT0.sol:19
    Round
    Main Review

    Description

    The upgradeToUSDT0 updates some configurations with the new USD₮0 name, including the revert message for role access control: _setupContractId(string(abi.encodePacked("USD₮0")));

    However, this line was used in the UChildERC20 to concatenate "Child" string with the token symbol: _setupContractId(string(abi.encodePacked("Child", symbol_)));

    As there is no concatenation needed, the plain string can be passed as an argument to this function.

    Recommendation

    Remove the encoding and send plain "USD₮0" string as parameter: _setupContractId("USD₮0");

  19. I-14 Informational Recover Tokens Stuck In USDT Contract Informational Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    Approximately $800,000 worth of tokens are currently stuck in the USDT contract, likely due to users mistakenly transferring tokens directly to the contract address. Since the contract does not include a mechanism to recover these funds, they remain irretrievable.

    Recommendation

    Consider implementing a recovery mechanism to allow retrieval of mistakenly sent tokens. This could follow the approach taken by USDC, which includes a controlled recoverERC20 function.

  20. I-15 Informational Invalid Test Should Be Removed Informational Resolved
    Location
    test/UChildUSDT0Fork.t.sol:665
    Round
    Main Review

    Description

    In UChildUSDT0Fork.t.sol, the test testInitializeReverts fails to compile because the UChildUSDT0 contract does not implement the function initialize.

    Recommendation

    Remove the test testInitializeReverts.

  21. I-16 Informational Warning About In Flight Bridges Informational Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    In the current implementation, Ethereum to Polygon bridges are completed when the deposit function is called by the childChainManager. After the update, bridges will be completed using the crosschainMint function.

    Bridges cannot be completed on Polygon if the update occurs while there are in-transit bridge requests. The update should only be performed once all requests have been processed.

    Recommendation

    It has been stated that the Polygon team will handle the migration process and ensure that all previous messages are settled.

    USDT Team should also consider consulting with Polygon team over unlocking of USDT locked in root manager on mainnet.

  22. I-17 Informational Floating Pragma Informational Acknowledged
    Location
    contracts/child/ChildToken/DappTokens/IERC7802.sol:2
    Round
    Main Review

    Description

    The IERC7802 contract uses Solidity version ^0.6.0, while all the other contracts in the codebase use the fixed 0.6.6 version.

    Recommendation

    Consider setting the pragma version to 0.6.6 in the IERC7802 contract, similar to the rest of the codebase.

  23. I-18 Informational Missing Validation During Upgrade Validation Partially resolved
    Location
    contracts/child/ChildToken/DappTokens/UChildUSDT0.sol:12
    Round
    Main Review

    Description

    The upgradeToUSDT0 address parameters are crucial for the correct migration to USDT0. Although this function is executed along with the implementation update and called by the proxy owner, some minimal validations should be implemented to guarantee no mistakes are made:

    • oftContract = address(0)
    • newAdmin = address(0)
    • decimals() = oft.sharedDecimals()

    Keep in mind the ArbitrumExtensionV2 also has zero address check for the OFT address, so these validations will be consistent with other migrations.

    Recommendation

    Consider adding the minimal validations when executing the upgrade function.

  24. I-19 Informational LZ Config Block Confirmations Is Too Low Configuration Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    The current Layer Zero configuration requires a minimum amount of block confirmations the OApp needs to wait in order to send or receive messaged. In case of USDT0, this value will be set to 256 blocks.

    However, this value seems low with the fact that there have been 157 block reorgs in the past in Polygon. Additionally, other protocols like Stargate and Abracadabra have this value set to 512 blocks.

    Recommendation

    Consider increasing the Layer Zero block confirmation value to 512.

Remediation Review

1 finding · May 29, 2025
  1. I-01 Informational Events Should Be Grouped Together Acknowledged
    Location
    contracts/child/ChildToken/DappTokens/UChildUSDT0.sol:12
    Round
    Remediation Review

    Description

    The LogSetOFTContract was recently added to the UChildUSDT0 contract. However, it was placed at the top of the contract (Line 12) while the rest of events are at the bottom of the contract.

    Recommendation

    Group the events together at the top of the contract where events are typically found.

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