Skip to content

[API-515] feat: add SafeTransferLibTron for Tron USDT compatibility#123

Open
reednaa wants to merge 5 commits intomainfrom
feature/api-515-handle-tron-usdt-broken-transfer-return-value-via-solady
Open

[API-515] feat: add SafeTransferLibTron for Tron USDT compatibility#123
reednaa wants to merge 5 commits intomainfrom
feature/api-515-handle-tron-usdt-broken-transfer-return-value-via-solady

Conversation

@reednaa
Copy link
Copy Markdown
Member

@reednaa reednaa commented May 4, 2026

Summary

Tron USDT's transfer() returns false (32 bytes of zeros) on success. Solady's safeTransfer checks eq(mload(0x00), 1) and reverts — making USDT transfers through our escrow settler impossible on Tron. approve() and transferFrom() return true correctly, so the fix replaces safeTransfer with an approve-then-transferFrom pattern via a new Tron-scoped library. All existing chains are unaffected.

Linear: https://linear.app/lifi-linear/issue/API-515/handle-tron-usdt-broken-transfer-return-value-via-solady-transfer

Files touched

Ticket-named scope

  • src/libs/SafeTransferLib.tron.sol

    • before: did not exist
    • after: SafeTransferLibTron library; safeTransfer checks self-allowance via inline assembly and calls safeApproveWithRetry + safeTransferFrom to bypass transfer()
  • src/input/escrow/InputSettlerEscrowLIFI.tron.sol

    • before: did not exist
    • after: InputSettlerEscrowLIFITron extends InputSettlerEscrowLIFI; overrides _resolveLock() to use SafeTransferLibTron in place of Solady's safeTransfer at both the fee and destination transfer sites
  • test/mocks/MockUSDT.tron.sol

    • before: did not exist
    • after: MockTronUSDT extends OIF's MockERC20; overrides transfer() to execute normally but return false
  • test/libs/SafeTransferLib.tron.t.sol

    • before: did not exist
    • after: 9 unit tests covering normal token, Tron mock, fuzz, zero-amount, insufficient-balance revert, max-approval reuse, and a negative test confirming Solady's safeTransfer reverts with TransferFailed on the mock
  • test/input/escrow/InputSettlerEscrowLIFI.tron.t.sol

    • before: did not exist
    • after: InputSettlerEscrowLIFITronTest extends OIF's InputSettlerEscrowTest (24 inherited tests pass with MockTronUSDT); adds fee-distribution and no-fee cases

Out-of-ticket-scope

  • src/input/escrow/InputSettlerEscrowLIFI.sol_domainName() made virtual

    • Note: required to allow InputSettlerEscrowLIFI.tron.sol to override and set a distinct EIP712 domain ("OIFEscrowLIFITron"), preventing cross-chain signature replay
  • snapshots/inputSettler.json — gas snapshots updated

    • Note: auto-updated by forge test; new Tron test snapshots added

Out of scope (untouched, per ticket)

  • Output settler — ticket explicitly excludes output settler changes for Tron
  • InputSettlerCompactLIFI — ticket notes it "may need similar treatment if it ever does direct transfers on Tron"; it currently uses ERC6909 batchClaim, no direct ERC20 transfers
  • Solady upstream — ticket explicitly excludes upstream PR

How it works

SafeTransferLibTron.safeTransfer avoids calling transfer() entirely. It reads self-allowance via a memory-safe assembly staticcall to allowance(self, self), approves type(uint256).max if needed (via safeApproveWithRetry, which handles the reset-to-zero pattern), then calls safeTransferFrom(self, to, amount). Both approve and transferFrom return true on Tron USDT, so all Solady checks pass.

How to validate

  • forge build — clean
  • forge test — 77/77 pass
  • Manual: deploy InputSettlerEscrowLIFITron to Tron testnet, submit an order with USDT, confirm _resolveLock executes without revert

Risk & rollback

  • Rollback plan: InputSettlerEscrowLIFITron is a new contract; existing deployments on all other chains use InputSettlerEscrowLIFI and are untouched. No rollback needed for non-Tron chains.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant