fix(swapper): fix Sun.io Tron swaps (INVALID_VERSION_LEN + OUT_OF_ENERGY) - #12459
Conversation
The SmartExchangeRouter swapExactInput reverts with INVALID_VERSION_LEN because versionLen was built as [2, 2, ...]. The router requires sum(versionLen) === path.length: the first pool segment consumes 2 tokens and each subsequent pool reuses the previous output, consuming 1 new token (e.g. [2, 1] for a 3-token / 2-pool route). Verified against mainnet via triggerconstantcontract and against successful sun.io frontend txs. Also removes the unused buildSwapTransaction.ts (dead code, no imports). Fixes SS-5705 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 29 minutes and 46 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR threads a TRON Grid API key through TRON clients and app requests, adds Sunio swap-call and fee helpers, updates Sunio transaction execution, changes trade status handling, and removes old TRON docs plus an obsolete builder. ChangesTRON Grid API-key propagation and Sunio swap flow
Sequence Diagram(s)sequenceDiagram
participant Config as src/config.ts
participant Plugin as src/plugins/tron/index.tsx
participant TronApi as packages/unchained-client/src/tron/api.ts
participant ChainAdapter as packages/chain-adapters/src/tron/TronChainAdapter.ts
participant Sunio as packages/swapper/src/swappers/SunioSwapper/*
Config->>Plugin: VITE_TRON_GRID_API_KEY
Plugin->>TronApi: apiKey
Plugin->>ChainAdapter: apiKey
TronApi->>Sunio: TRON-PRO-API-KEY headers
ChainAdapter->>Sunio: TRON-PRO-API-KEY headers
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Sun.io swap fees hardcoded the user paying ~2000 energy (~0.2 TRX), assuming the router sponsors the rest. On-chain the router sponsors only ~1% (origin_energy_usage); the user pays the rest (native sells ~180-245k energy, TRC20 sells ~350-415k due to the extra transferFrom). The under-estimate showed misleading fees and let users broadcast swaps without enough TRX for energy, passing the balance check and reverting OUT_OF_ENERGY. Estimate energy by simulating the actual swapExactInput call against the SmartExchangeRouter (triggerConstantContract.energy_used) with a 1.2x margin, whenever an address is available (rate or quote). Without an address (rate preview, no wallet) or when the simulation reverts (e.g. a TRC20 sell before approval), fall back to a conservative per-sell-type constant (native 250k, TRC20 430k) sized to observed worst-case routes, so the fee is realistic rather than missing. Network prices default if the node is unavailable, so a fee is always produced. The estimate isn't part of Tron tx construction (no embedded gas price/limit; feeLimit is a fixed ceiling) - it drives fee display and the balance gate in validateTradeQuote, which is what prevents the doomed broadcast. Extract the swapExactInput call-parameter builder into a shared util so fee estimation and execution encode the call identically. Also removes stale Tron integration/fee docs whose claims no longer match the code (TRON_FEE_ESTIMATION_ISSUES.md, THORCHAIN_TRON_INTEGRATION.md, SunioSwapper/INTEGRATION.md). Fixes SS-5705 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4f15855 to
da63e00
Compare
checkTradeStatus only mapped contractRet === 'REVERT' to Failed; every other Tron failure code (OUT_OF_ENERGY, OUT_OF_TIME, TRANSFER_FAILED, ...) fell through to Pending, leaving failed swaps stuck pending forever in the action center. Treat any non-SUCCESS contractRet as a terminal failure; a missing contractRet still means not-yet-mined (Pending). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
packages/swapper/src/swappers/SunioSwapper/utils/buildSwapContractCall.ts (1)
23-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd an explicit return type for the shared contract parameter builder.
This exported helper is now the contract-call boundary for both simulation and execution, so avoid relying on an inferred array-union shape.
♻️ Proposed type annotation
+type TronContractCallParameter = { + type: string + value: unknown +} + -export const buildSwapExactInputParameters = (routeParams: SwapRouteParameters) => [ +export const buildSwapExactInputParameters = ( + routeParams: SwapRouteParameters, +): TronContractCallParameter[] => [As per coding guidelines, "
**/*.{ts,tsx}: ALWAYS use explicit types for function parameters and return values in TypeScript" and "ALWAYS use explicit types for object shapes using interfaces or type aliases in TypeScript".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/swapper/src/swappers/SunioSwapper/utils/buildSwapContractCall.ts` around lines 23 - 37, The exported helper buildSwapExactInputParameters currently relies on an inferred array-union return shape, which should be made explicit since it is the shared contract-call boundary. Add a concrete return type annotation for buildSwapExactInputParameters in buildSwapContractCall.ts, using a named interface or type alias for the parameter tuple/object shape so both simulation and execution consume a stable contract parameter type.Source: Coding guidelines
packages/swapper/src/swappers/SunioSwapper/utils/buildSwapRouteParameters.ts (1)
29-34: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winEnforce the route-shape invariant before building calldata.
Line 29 documents the router invariant, but malformed Sunio route arrays can still produce invalid
versionLen/feesparameters. Validatetokens,poolVersions, andpoolFeeslengths before returning route params.🛡️ Proposed validation
const poolVersion = route.poolVersions + if (path.length !== poolVersion.length + 1 || route.poolFees.length !== poolVersion.length) { + throw new Error( + `[Sun.io] Invalid route shape: tokens=${path.length}, poolVersions=${poolVersion.length}, poolFees=${route.poolFees.length}`, + ) + } + // The SmartExchangeRouter expects sum(versionLen) === path.length: the firstAs per coding guidelines, "
packages/swapper/src/swappers/**/*.ts: Validate inputs and log errors for debugging in Swapper system implementations".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/swapper/src/swappers/SunioSwapper/utils/buildSwapRouteParameters.ts` around lines 29 - 34, The buildSwapRouteParameters path is constructing calldata without enforcing the SmartExchangeRouter route-shape invariant, so malformed Sunio route data can still produce invalid versionLen/fees arrays. Add validation in buildSwapRouteParameters (and/or the route assembly it uses) to verify tokens, poolVersions, and poolFees have the expected compatible lengths before returning route params, and log a clear error when validation fails so bad input is rejected early.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/swapper/src/swappers/SunioSwapper/utils/getQuoteOrRate.ts`:
- Around line 134-166: The account activation fee logic in getQuoteOrRate should
only apply when the output is native TRX, not for TRC20 buys. Update the
activation-fee branch near the receiveAddress check so it is gated by
isSellingNativeTrx or the equivalent native-output condition, and keep the
shared fee estimate path unchanged for non-native swaps. Ensure the fee
calculation still uses TronWeb and the recipient activation lookup, but returns
0 unless the swap is for native TRX.
- Around line 194-203: The TRC20 sell estimation in getQuoteOrRate is using
triggerConstantContract’s energy_used even when the simulation failed, which can
undercount and cause OUT_OF_ENERGY. Update the try path in getQuoteOrRate to
inspect the TronWeb 6.1.0 result.result flag from triggerConstantContract; only
return result.energy_used when that nested boolean is true, and otherwise fall
back to fallbackEnergy. Keep the existing catch fallback, and make sure the
logic around SUNIO_SMART_ROUTER_CONTRACT, SUNIO_SWAP_EXACT_INPUT_SELECTOR, and
buildSwapExactInputParameters uses the simulation success state before trusting
energy_used.
---
Nitpick comments:
In `@packages/swapper/src/swappers/SunioSwapper/utils/buildSwapContractCall.ts`:
- Around line 23-37: The exported helper buildSwapExactInputParameters currently
relies on an inferred array-union return shape, which should be made explicit
since it is the shared contract-call boundary. Add a concrete return type
annotation for buildSwapExactInputParameters in buildSwapContractCall.ts, using
a named interface or type alias for the parameter tuple/object shape so both
simulation and execution consume a stable contract parameter type.
In
`@packages/swapper/src/swappers/SunioSwapper/utils/buildSwapRouteParameters.ts`:
- Around line 29-34: The buildSwapRouteParameters path is constructing calldata
without enforcing the SmartExchangeRouter route-shape invariant, so malformed
Sunio route data can still produce invalid versionLen/fees arrays. Add
validation in buildSwapRouteParameters (and/or the route assembly it uses) to
verify tokens, poolVersions, and poolFees have the expected compatible lengths
before returning route params, and log a clear error when validation fails so
bad input is rejected early.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2acef590-540e-41a1-ad82-8214ed44146f
📒 Files selected for processing (10)
packages/chain-adapters/src/tron/TRON_FEE_ESTIMATION_ISSUES.mdpackages/chain-adapters/src/tron/TronChainAdapter.tspackages/swapper/src/swappers/SunioSwapper/INTEGRATION.mdpackages/swapper/src/swappers/SunioSwapper/endpoints.tspackages/swapper/src/swappers/SunioSwapper/utils/buildSwapContractCall.tspackages/swapper/src/swappers/SunioSwapper/utils/buildSwapRouteParameters.tspackages/swapper/src/swappers/SunioSwapper/utils/buildSwapTransaction.tspackages/swapper/src/swappers/SunioSwapper/utils/constants.tspackages/swapper/src/swappers/SunioSwapper/utils/getQuoteOrRate.tspackages/swapper/src/thorchain-utils/tron/THORCHAIN_TRON_INTEGRATION.md
💤 Files with no reviewable changes (4)
- packages/chain-adapters/src/tron/TRON_FEE_ESTIMATION_ISSUES.md
- packages/swapper/src/swappers/SunioSwapper/INTEGRATION.md
- packages/swapper/src/thorchain-utils/tron/THORCHAIN_TRON_INTEGRATION.md
- packages/swapper/src/swappers/SunioSwapper/utils/buildSwapTransaction.ts
Sized to observed average energy so the 1.2x estimate-time margin covers the worst case (native ~245k, TRC20 ~415k) without double-counting safety, instead of overstating pre-approval TRC20 fees. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
TronGrid rate-limits unauthenticated requests; add VITE_TRON_GRID_API_KEY and send it as the TRON-PRO-API-KEY header on every TronWeb instance and raw TronGrid fetch (chain adapter, unchained client, swapper Tron paths, and the src-level allowance/approve/activation/status helpers). https://developers.tron.network/reference/select-network#api-key Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The quote-time fee was reused at confirm time, so a TRC20 sell whose simulation reverted pre-approval kept showing the fallback (~46 TRX) even after approval, when the real cost is ~38 TRX. Extract the estimator into a shared util and add a Sun.io getTronTransactionFees that re-runs it at confirm time (mirroring getEvmTransactionFees), so a granted allowance yields the true cost; falls back to the stored quote fee on failure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A spent (e.g. exact) approval was served stale-while-revalidate to the next trade, so the following swap skipped the approval step and reverted on-chain. Drop cached allowances on swap completion so the next swap's approval/balance check reads fresh on-chain state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
packages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.ts (2)
92-99: 🎯 Functional Correctness | 🟠 MajorIgnore
energy_usedwhen the constant-call simulation failed.Line 99 trusts
energy_usedeven whentriggerConstantContract()reports a failed simulation. On TronWeb, that nested success flag is separate from the returned energy fields, so this can undercount TRC20 sell fees and recreate theOUT_OF_ENERGYfailure this PR is trying to eliminate.Proposed fix
const result = await tronWeb.transactionBuilder.triggerConstantContract( SUNIO_SMART_ROUTER_CONTRACT, SUNIO_SWAP_EXACT_INPUT_SELECTOR, { callValue }, buildSwapExactInputParameters(routeParams), address, ) - return result?.energy_used || fallbackEnergy + if (result?.result?.result !== true || result.energy_used == null) { + return fallbackEnergy + } + + return result.energy_used🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.ts` around lines 92 - 99, The fee estimation in estimateSunioNetworkFee currently returns result.energy_used even when triggerConstantContract() reports a failed simulation, which can undercount the required fee. Update the logic in estimateSunioNetworkFee to check the simulation success flag from the result of triggerConstantContract() and only use energy_used when the constant call actually succeeded; otherwise fall back to fallbackEnergy. Keep the change localized to the triggerConstantContract result handling and preserve the existing fallback behavior for failed simulations.
17-24: 🎯 Functional Correctness | 🟠 MajorSplit sender and recipient inputs in this estimator.
This helper uses
addressfor three different roles: the account-activation lookup, the encoded swap recipient, and thetriggerConstantContract()caller.getQuoteOrRate()passesreceiveAddress, whilegetSunioTransactionFees()passesfrom, so send-to-different-address trades are wrong in one phase or the other. It also means Line 69 adds 1 TRX for inactive TRC20 recipients because the helper has no buy-side context. Split this intofromAddress,receiveAddress, andisBuyingNativeTrx, then only apply activation fees to native TRX outputs.Suggested direction
type EstimateSunioNetworkFeeArgs = { rpcUrl: string apiKey: string route: SunioRoute sellAmountCryptoBaseUnit: string isSellingNativeTrx: boolean - address: string | undefined + fromAddress: string | undefined + receiveAddress: string | undefined + isBuyingNativeTrx: boolean slippageTolerancePercentageDecimal: string | undefined } - if (!address) return 0 + if (!isBuyingNativeTrx || !receiveAddress) return 0 try { const recipientInfoResponse = await fetch(`${rpcUrl}/wallet/getaccount`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...tronGridHeaders }, - body: JSON.stringify({ address, visible: true }), + body: JSON.stringify({ address: receiveAddress, visible: true }), }) @@ - if (!address) return fallbackEnergy + if (!fromAddress) return fallbackEnergy try { const routeParams = buildSwapRouteParameters( route, sellAmountCryptoBaseUnit, '0', - address, + receiveAddress ?? fromAddress, slippageTolerancePercentageDecimal ?? DEFAULT_SLIPPAGE_PERCENTAGE, ) @@ - address, + fromAddress, )Also applies to: 59-73, 82-98
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.ts` around lines 17 - 24, The estimator currently reuses one address for sender, recipient, and activation checks, which breaks swap quotes for different send/receive addresses. Update estimateSunioNetworkFee to accept separate fromAddress, receiveAddress, and isBuyingNativeTrx inputs, then pass the correct one into getSunioTransactionFees, getQuoteOrRate, and triggerConstantContract caller handling. Also restrict the extra 1 TRX activation fee logic to native TRX output cases only, using the new isBuyingNativeTrx flag.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.env:
- Line 236: The tracked `.env` currently contains a live
`VITE_TRON_GRID_API_KEY`, which should not be committed because it ends up in
git history and client builds. Remove the real value from the checked-in
environment file, replace it with a placeholder for local setup, and update the
app so `VITE_TRON_GRID_API_KEY` is injected from deployment secrets or local
untracked env configuration instead. Rotate the exposed key and ensure any
references to this variable continue to work through the existing env-loading
path.
In
`@src/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsx`:
- Line 124: The allowance polling flow in useAllowanceApproval should not let
401/403 responses from the TRON gettransactionbyid request count as a successful
approval. Update the polling/error handling around the apiKey usage and the
approval wait loop so unauthorized responses are detected explicitly, stop the
flow as a failure, and prevent the setAllowanceApprovalTxComplete dispatch from
running unless a real on-chain confirmation is observed. Use the
useAllowanceApproval hook and the TRON transaction polling path to locate the
affected logic.
In
`@src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx`:
- Around line 372-375: The allowance cache refresh in useTradeExecution should
not use queryClient.removeQueries because it only clears cached data and does
not trigger a fresh post-swap read. Update the queryClient call for the
['allowanceCryptoBaseUnit'] query key to use invalidateQueries(), or
refetchQueries() if the updated allowance is needed immediately, so the next
approval/balance check in the trade flow reads current on-chain state.
In `@src/lib/utils/tron.ts`:
- Around line 33-40: The TRON transaction lookup in the helper currently turns
any non-OK response into TxStatus.Unknown, which can leave sends polling
forever. Update the logic in tron.ts around the gettransactionbyid fetch so
rejected/invalid API-key responses are mapped to a terminal failure status
instead of Unknown, and make sure useSendActionSubscriber continues polling only
for genuinely pending states while stopping on that failure result.
---
Duplicate comments:
In `@packages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.ts`:
- Around line 92-99: The fee estimation in estimateSunioNetworkFee currently
returns result.energy_used even when triggerConstantContract() reports a failed
simulation, which can undercount the required fee. Update the logic in
estimateSunioNetworkFee to check the simulation success flag from the result of
triggerConstantContract() and only use energy_used when the constant call
actually succeeded; otherwise fall back to fallbackEnergy. Keep the change
localized to the triggerConstantContract result handling and preserve the
existing fallback behavior for failed simulations.
- Around line 17-24: The estimator currently reuses one address for sender,
recipient, and activation checks, which breaks swap quotes for different
send/receive addresses. Update estimateSunioNetworkFee to accept separate
fromAddress, receiveAddress, and isBuyingNativeTrx inputs, then pass the correct
one into getSunioTransactionFees, getQuoteOrRate, and triggerConstantContract
caller handling. Also restrict the extra 1 TRX activation fee logic to native
TRX output cases only, using the new isBuyingNativeTrx flag.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fc367daa-03f7-4d69-b3d3-d3e2eb293574
📒 Files selected for processing (19)
.envpackages/chain-adapters/src/tron/TronChainAdapter.tspackages/swapper/src/swappers/SunioSwapper/endpoints.tspackages/swapper/src/swappers/SunioSwapper/utils/constants.tspackages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.tspackages/swapper/src/swappers/SunioSwapper/utils/getQuoteOrRate.tspackages/swapper/src/swappers/SunioSwapper/utils/getSunioTransactionFees.tspackages/swapper/src/thorchain-utils/getL1RateOrQuote.tspackages/swapper/src/thorchain-utils/tron/getTronTransactionFees.tspackages/swapper/src/types.tspackages/unchained-client/src/tron/api.tssrc/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsxsrc/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsxsrc/config.tssrc/hooks/useIsTronAddressActivated/useIsTronAddressActivated.tssrc/lib/utils/tron.tssrc/lib/utils/tron/approve.tssrc/lib/utils/tron/getAllowance.tssrc/plugins/tron/index.tsx
✅ Files skipped from review due to trivial changes (1)
- packages/swapper/src/types.ts
SwapperConfig.VITE_TRON_GRID_API_KEY is required, so the public-api server config failed to type-check without it. Add it to the env schema + server config, and pass the key to the public-api Tron ChainAdapter and TronApi so server-side Tron requests are also authenticated. Also documents the per-hop poolVersions assumption behind the Sun.io versionLen formula. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Only add the 1 TRX account-activation fee when the swap buys native TRX (matches the chain adapter; TRC20 buys fold activation into energy). - Ignore triggerConstantContract energy_used unless result.result.result is true, so a reverted/partial simulation falls back instead of under-estimating. - Document that non-OK TRON poll responses are intentionally swallowed: TronGrid reuses 403 for rate-limiting, so failing on it would break under throttling. - Order the .env TronGrid key per dotenv-linter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Description
Fixes the cluster of issues making Tron (Sun.io) swaps fail, plus the TronGrid rate-limiting and a cross-cutting allowance bug surfaced while testing.
Linear: https://linear.app/shapeshift-dao/issue/SS-5705/unable-to-do-trades-on-tron
1.
INVALID_VERSION_LEN— wrongversionLenencodingbuildSwapRouteParametersbuiltversionLenas[2, 2, …]; the router requiressum(versionLen) === path.length(first segment 2 tokens, each subsequent pool +1), i.e.[2, 1]. Verified on mainnet and against Sun.io's own frontend txs. (Old formula was coincidentally right for single-pool routes, so only multi-hop reverted.)2.
OUT_OF_ENERGY— fee under-estimationFees hardcoded ~2000 energy (~0.2 TRX); the router sponsors only ~1%, the user pays ~180–245k (native) / ~350–415k (TRC20) energy. We now simulate the real
swapExactInputviatriggerConstantContract(1.2× margin), with per-sell-type conservative fallbacks (native 250k→tuned 215k, TRC20 430k→tuned 375k) when the sim can't run (no wallet, or TRC20 pre-approval). Drives fee display + thevalidateTradeQuotebalance gate, which is what blocks the doomed broadcast.3. Failed swaps stuck "pending"
checkTradeStatusonly mappedREVERTto failed;OUT_OF_ENERGY/OUT_OF_TIME/TRANSFER_FAILED/… fell through to pending forever. Now any non-SUCCESScontractRetis terminal-failed.4. Confirm-time fee re-estimation
The quote-time fee was reused at confirm time, so a TRC20 sell whose sim reverted pre-approval kept showing the fallback (~46 TRX) after approval when the real cost is ~38 TRX. Added a Sun.io
getTronTransactionFeesthat re-runs the estimator at confirm time (mirroringgetEvmTransactionFees); a granted allowance now yields the true cost.5. Stale allowance → skipped approval (cross-cutting)
After an exact approval was consumed, the next swap read a stale-cached allowance and skipped the approval step, reverting
Transfer failed. Now we drop cached allowances on swap completion so the next swap's approval/balance check reads fresh state. Applies to all swappers/chains.6. TronGrid API key plumbing
TronGrid rate-limits unauthenticated requests. Added
VITE_TRON_GRID_API_KEY, sent asTRON-PRO-API-KEYon every TronWeb instance and raw TronGrid fetch (chain adapter, unchained client, swapper Tron paths, src helpers).Cleanup
swapExactInputcall-parameter builder and the fee estimator.Issue (if applicable)
closes #12456
Risk
Medium–High. Alters Sun.io on-chain swap calldata (
versionLen) and Tron fee estimation, adds a global allowance-cache invalidation on trade completion (all swappers), and adds an auth header to all Tron requests. No EVM swap-calldata changes.Sun.io (Tron) swaps; Tron chain adapter + unchained client; THORChain Tron fee paths; the shared trade approval/allowance flow (allowance-cache invalidation affects every swapper).
Testing
Engineering
versionLen, realistic fee (native ~25–30 TRX, TRC20 ~45 TRX pre-approval correcting to ~38 post-approval), executes withoutINVALID_VERSION_LEN/OUT_OF_ENERGY.VITE_TRON_GRID_API_KEYset, Tron requests carryTRON-PRO-API-KEY(no 429s under load).Operations
Functional test: perform Sun.io Tron swaps (TRX→USDT, USDT→TRX) including the approve-then-swap-twice flow; confirm fees are realistic, failed txs show as failed, and back-to-back token swaps re-prompt approval.
Screenshots (if applicable)
Summary by CodeRabbit