Skip to content

fix(swapper): fix Sun.io Tron swaps (INVALID_VERSION_LEN + OUT_OF_ENERGY) - #12459

Merged
kaladinlight merged 10 commits into
developfrom
fix/ss-5705-unable-to-do-trades-on-tron
Jun 24, 2026
Merged

fix(swapper): fix Sun.io Tron swaps (INVALID_VERSION_LEN + OUT_OF_ENERGY)#12459
kaladinlight merged 10 commits into
developfrom
fix/ss-5705-unable-to-do-trades-on-tron

Conversation

@kaladinlight

@kaladinlight kaladinlight commented Jun 24, 2026

Copy link
Copy Markdown
Member

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 — wrong versionLen encoding

buildSwapRouteParameters built versionLen as [2, 2, …]; the router requires sum(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-estimation

Fees 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 swapExactInput via triggerConstantContract (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 + the validateTradeQuote balance gate, which is what blocks the doomed broadcast.

3. Failed swaps stuck "pending"

checkTradeStatus only mapped REVERT to failed; OUT_OF_ENERGY/OUT_OF_TIME/TRANSFER_FAILED/… fell through to pending forever. Now any non-SUCCESS contractRet is 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 getTronTransactionFees that re-runs the estimator at confirm time (mirroring getEvmTransactionFees); 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 as TRON-PRO-API-KEY on every TronWeb instance and raw TronGrid fetch (chain adapter, unchained client, swapper Tron paths, src helpers).

Cleanup

  • Extracted the shared swapExactInput call-parameter builder and the fee estimator.
  • Removed three stale Tron docs whose claims no longer matched the code.

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.

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

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

  • Sun.io Tron swaps both directions: quote builds valid versionLen, realistic fee (native ~25–30 TRX, TRC20 ~45 TRX pre-approval correcting to ~38 post-approval), executes without INVALID_VERSION_LEN/OUT_OF_ENERGY.
  • A failed Tron swap resolves to Failed in the action center, not stuck pending.
  • Exact-approve + swap, then a second same-pair swap: approval is required again (no stale skip).
  • With VITE_TRON_GRID_API_KEY set, Tron requests carry TRON-PRO-API-KEY (no 429s under load).
  • Regression: EVM/other-chain approvals and quotes unaffected.

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

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

  • New Features
    • Added support for a TRON Grid API key (configurable) to authenticate TRON transaction building, swap fee estimation, activation checks, and transaction status polling.
  • Bug Fixes
    • Improved Sunio/TRON swap network fee estimation (bandwidth/energy), including account activation handling and safer fallbacks for TRX vs TRC20.
    • Refined trade status mapping (Pending/Confirmed/Failed) based on transaction results and confirmations.
  • Chores
    • Refresh allowance data after trade execution to prevent stale approval/balance checks.

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>
@kaladinlight
kaladinlight requested a review from a team as a code owner June 24, 2026 17:05
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@kaladinlight, we couldn't start this review because you've reached your PR review rate limit.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 82d7903d-3700-48d4-8674-60b1b2e61fbf

📥 Commits

Reviewing files that changed from the base of the PR and between 28f865c and 19b49ea.

📒 Files selected for processing (3)
  • .env
  • packages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.ts
  • src/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsx
📝 Walkthrough

Walkthrough

This 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.

Changes

TRON Grid API-key propagation and Sunio swap flow

Layer / File(s) Summary
Shared TRON config and client headers
src/config.ts, .env, src/plugins/tron/index.tsx, packages/public-api/src/env.ts, packages/public-api/src/config.ts, packages/public-api/src/swapperDeps.ts, packages/unchained-client/src/tron/api.ts, packages/chain-adapters/src/tron/TronChainAdapter.ts, packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts, packages/swapper/src/thorchain-utils/tron/getTronTransactionFees.ts
Adds VITE_TRON_GRID_API_KEY, threads it into Tron provider setup, and conditionally includes TRON-PRO-API-KEY headers in TronGrid-backed clients and fee helpers.
Sunio swap call builder
packages/swapper/src/swappers/SunioSwapper/utils/buildSwapContractCall.ts, packages/swapper/src/swappers/SunioSwapper/utils/buildSwapRouteParameters.ts
Adds the shared swapExactInput selector and ABI parameter builder, converts Tron addresses for EVM ABI inputs, and changes versionLen construction for route parameters.
Sunio fee estimation helpers
packages/swapper/src/swappers/SunioSwapper/utils/constants.ts, packages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.ts, packages/swapper/src/swappers/SunioSwapper/utils/getQuoteOrRate.ts, packages/swapper/src/swappers/SunioSwapper/utils/getSunioTransactionFees.ts, packages/swapper/src/thorchain-utils/tron/getTronTransactionFees.ts
Adds fallback energy constants, estimates Sunio network fees from chain parameters and simulation, replaces inline quote/rate fee logic, and routes Sunio fee selection through the new helper.
Sunio endpoint execution and status
packages/swapper/src/swappers/SunioSwapper/endpoints.ts, packages/swapper/src/swappers/SunioSwapper/utils/buildSwapTransaction.ts
Updates unsigned TRON transaction building to use the shared call builder and fee helper, changes trade status derivation, and removes the old transaction builder module.
App-side TRON requests and allowance refresh
src/hooks/useIsTronAddressActivated/useIsTronAddressActivated.ts, src/lib/utils/tron.ts, src/lib/utils/tron/approve.ts, src/lib/utils/tron/getAllowance.ts, src/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsx, src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx
Adds the API key to TRON polling, activation checks, allowance reads, approval broadcasts, transaction status requests, and allowance cache invalidation after swap completion.
Removed TRON docs
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
Removes the TRON fee-estimation, Sunio integration, and Thorchain TRON integration markdown documents.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • shapeshift/web#11217: This PR also updates packages/chain-adapters/src/tron/TronChainAdapter.ts with TRON request/header plumbing.
  • shapeshift/web#12446: This PR touches the same TRON fee and transaction call sites in packages/chain-adapters/src/tron/TronChainAdapter.ts.
  • shapeshift/web#12451: This PR updates TRON fee wiring in packages/swapper/src/thorchain-utils/tron/getTronTransactionFees.ts.

Suggested reviewers

  • gomesalexandre

Poem

🐇 The carrot-code hums, the headers all gleam,
TRON swaps now hop through a steadier stream.
A little API key, a lot less confusion,
This bunny approves the new swapusion.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly names the Sun.io Tron swap fix and the two key failure modes.
Linked Issues check ✅ Passed Changes address TRX trading failures by fixing versionLen encoding, fee estimation, pending status handling, and stale allowance caching.
Out of Scope Changes check ✅ Passed The remaining changes align with the Tron swap fix scope, including API-key plumbing and doc removals noted in the PR objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ss-5705-unable-to-do-trades-on-tron

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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>
@kaladinlight
kaladinlight force-pushed the fix/ss-5705-unable-to-do-trades-on-tron branch from 4f15855 to da63e00 Compare June 24, 2026 17:13
@kaladinlight

Copy link
Copy Markdown
Member Author

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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/swapper/src/swappers/SunioSwapper/utils/buildSwapContractCall.ts (1)

23-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add 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 win

Enforce the route-shape invariant before building calldata.

Line 29 documents the router invariant, but malformed Sunio route arrays can still produce invalid versionLen/fees parameters. Validate tokens, poolVersions, and poolFees lengths 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 first

As 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

📥 Commits

Reviewing files that changed from the base of the PR and between 88ebef7 and eb50a4e.

📒 Files selected for processing (10)
  • packages/chain-adapters/src/tron/TRON_FEE_ESTIMATION_ISSUES.md
  • packages/chain-adapters/src/tron/TronChainAdapter.ts
  • packages/swapper/src/swappers/SunioSwapper/INTEGRATION.md
  • packages/swapper/src/swappers/SunioSwapper/endpoints.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/buildSwapContractCall.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/buildSwapRouteParameters.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/buildSwapTransaction.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/constants.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/getQuoteOrRate.ts
  • packages/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

Comment thread packages/swapper/src/swappers/SunioSwapper/utils/getQuoteOrRate.ts Outdated
Comment thread packages/swapper/src/swappers/SunioSwapper/utils/getQuoteOrRate.ts Outdated
kaladinlight and others added 4 commits June 24, 2026 15:03
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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (2)
packages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.ts (2)

92-99: 🎯 Functional Correctness | 🟠 Major

Ignore energy_used when the constant-call simulation failed.

Line 99 trusts energy_used even when triggerConstantContract() 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 the OUT_OF_ENERGY failure 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 | 🟠 Major

Split sender and recipient inputs in this estimator.

This helper uses address for three different roles: the account-activation lookup, the encoded swap recipient, and the triggerConstantContract() caller. getQuoteOrRate() passes receiveAddress, while getSunioTransactionFees() passes from, 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 into fromAddress, receiveAddress, and isBuyingNativeTrx, 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

📥 Commits

Reviewing files that changed from the base of the PR and between eb50a4e and a0b7341.

📒 Files selected for processing (19)
  • .env
  • packages/chain-adapters/src/tron/TronChainAdapter.ts
  • packages/swapper/src/swappers/SunioSwapper/endpoints.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/constants.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/estimateSunioNetworkFee.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/getQuoteOrRate.ts
  • packages/swapper/src/swappers/SunioSwapper/utils/getSunioTransactionFees.ts
  • packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts
  • packages/swapper/src/thorchain-utils/tron/getTronTransactionFees.ts
  • packages/swapper/src/types.ts
  • packages/unchained-client/src/tron/api.ts
  • src/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsx
  • src/components/MultiHopTrade/components/TradeConfirm/hooks/useTradeExecution.tsx
  • src/config.ts
  • src/hooks/useIsTronAddressActivated/useIsTronAddressActivated.ts
  • src/lib/utils/tron.ts
  • src/lib/utils/tron/approve.ts
  • src/lib/utils/tron/getAllowance.ts
  • src/plugins/tron/index.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/swapper/src/types.ts

Comment thread .env
Comment thread src/lib/utils/tron.ts
kaladinlight and others added 2 commits June 24, 2026 15:21
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>
@kaladinlight
kaladinlight enabled auto-merge (squash) June 24, 2026 21:37
@kaladinlight
kaladinlight merged commit 3c3b7f0 into develop Jun 24, 2026
4 checks passed
@kaladinlight
kaladinlight deleted the fix/ss-5705-unable-to-do-trades-on-tron branch June 24, 2026 21:47
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.

Unable to do trades on Tron

1 participant