Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b54f829
feat: display current nonce in WalletConnect transaction advanced par…
gomesalexandre Dec 2, 2025
9ba6676
feat: massively enhance swapper-integration skill with deep research …
gomesalexandre Dec 2, 2025
42d05a6
feat: integrate Sun.io swapper for TRON blockchain
gomesalexandre Dec 2, 2025
e574713
refactor: use getInputOutputRate helper and add TRON-specific gotchas…
gomesalexandre Dec 2, 2025
3bbe0ab
fix: use TronWeb contract interface instead of triggerSmartContract
gomesalexandre Dec 2, 2025
f5c1b4a
fix: clone arrays before passing to TronWeb to avoid immutability errors
gomesalexandre Dec 2, 2025
d22626e
fix: convert TRON addresses to hex format for triggerSmartContract
gomesalexandre Dec 2, 2025
daf9514
docs: add node -e testing tip and TRON triggerSmartContract gotchas
gomesalexandre Dec 2, 2025
f6bc403
fix: use tuple with type signature and array values for SwapData struct
gomesalexandre Dec 2, 2025
d69db51
fix: pass TRON addresses as Base58 strings, not hex format
gomesalexandre Dec 2, 2025
40c99e7
fix: remove unused SUNSWAP_ROUTER_ABI import
gomesalexandre Dec 2, 2025
3057bde
debug: add extensive logging to Sun.io transaction builder
gomesalexandre Dec 2, 2025
6c89450
fix: set default address on TronWeb instance before building transaction
gomesalexandre Dec 2, 2025
35b1a6e
fix: convert TRON addresses to EVM format for tuple parameters in Tro…
gomesalexandre Dec 2, 2025
fc7eeda
fix: use correct Smart Router contract and set call_value for native …
gomesalexandre Dec 2, 2025
af21d3e
debug: add extensive JSON logging to Sun.io quote and transaction bui…
gomesalexandre Dec 2, 2025
2188b9b
debug: add wallet signing logs to track TRON address derivation
gomesalexandre Dec 2, 2025
ad408bb
fix: use hardened derivation path for TRON wallet signing
gomesalexandre Dec 2, 2025
a04b2a8
feat: implement proper TRON transaction status polling for Sun.io
gomesalexandre Dec 2, 2025
5aa44b8
fix: add TronSwapperDeps to CheckTradeStatusInput and use TxStatus enum
gomesalexandre Dec 2, 2025
a2cd6cb
fix: use tx.status directly instead of tx.ret for TRON status check
gomesalexandre Dec 2, 2025
4fa3f9a
fix: check tx.confirmations for TRON status instead of tx.status
gomesalexandre Dec 2, 2025
6e2aaf5
fix: import TxStatus enum for status polling
gomesalexandre Dec 2, 2025
11b8875
chore: remove debug console.logs from Sun.io swapper
gomesalexandre Dec 2, 2025
b473448
fix: pass assertGetTronChainAdapter to checkTradeStatus in tradeExecu…
gomesalexandre Dec 2, 2025
6f27973
fix: call TRON API directly for status to check ret.contractRet
gomesalexandre Dec 2, 2025
bca5812
feat: spank @neomaking
gomesalexandre Dec 2, 2025
57ed8a8
refactor: use toAddressNList helper instead of manual HARDENED calcul…
gomesalexandre Dec 2, 2025
a4c6860
chore: merge develop into feat_swapper_sun_io
gomesalexandre Dec 3, 2025
5d52301
feat: add TRON TRC-20 approval support for swappers
gomesalexandre Dec 3, 2025
9cdc425
fix: skip EVM contract data generation for TRON approvals
gomesalexandre Dec 3, 2025
e9e0d87
docs: update RPC URL access pattern in swapper-integration skill
gomesalexandre Dec 3, 2025
27a30e8
debug: add logging for TRON approval accountNumber issue
gomesalexandre Dec 3, 2025
2e8c655
debug: add isFetchStep and shouldFetchTradeQuotes logging
gomesalexandre Dec 3, 2025
6eb1ae0
fix: set accountNumber to 0 in Sun.io rate quotes for approval compat…
gomesalexandre Dec 3, 2025
083204c
fix: improve TRON approval broadcast error handling
gomesalexandre Dec 3, 2025
662dd4d
feat: implement TRON TRC-20 approval flow with transaction broadcast
gomesalexandre Dec 3, 2025
2f489c1
Merge remote-tracking branch 'origin/develop' into feat_swapper_sun_io
gomesalexandre Dec 3, 2025
3f8d499
chore: remove debug console.warn from TRON approval polling
gomesalexandre Dec 3, 2025
6fb19c0
refactor: remove 'as any' casts and use constants in Sun.io swapper
gomesalexandre Dec 3, 2025
6ca415b
feat: expand Sun.io DEX type coverage for better route discovery
gomesalexandre Dec 3, 2025
80b8be0
refactor: unify Sun.io rate and quote logic to eliminate duplication
gomesalexandre Dec 3, 2025
4bd9a36
fix: validate receiveAddress is defined for quote fee estimation
gomesalexandre Dec 3, 2025
6e835ed
feat: tackle @coderabbitai comments
gomesalexandre Dec 3, 2025
61a3b18
Merge branch 'develop' into feat_swapper_sun_io
gomesalexandre Dec 4, 2025
e22d259
Merge branch 'develop' into feat_swapper_sun_io
NeOMakinG Dec 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 254 additions & 0 deletions .claude/skills/swapper-integration/common-gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,4 +469,258 @@ After implementation, verify:

---

## 13. TRON-Specific: Human-Readable Amounts ⚠️

**Problem**: TRON aggregator APIs (like Sun.io) return amounts in human-readable format, not base units.

**Real Example** (Sun.io):
```json
{
"amountIn": "1.000000", // Human-readable (1 USDT)
"amountOut": "1.071122" // Human-readable (1.071122 USDC)
}
```

**Solution**: Must multiply by `10^precision` to convert to crypto base units:
```typescript
const buyAmountCryptoBaseUnit = bn(route.amountOut)
.times(bn(10).pow(buyAsset.precision))
.toFixed(0)
```

**Affected Chains**: TRON swappers using aggregator APIs

---

## 14. TRON-Specific: Smart Contract Transaction Building ⚠️⚠️

**Problem**: TRON swappers require calling smart contracts (not simple sends), which is different from deposit-to-address swappers.

**Wrong Assumption**: Can use generic `getUnsignedTronTransaction` from tron-utils
**Reality**: Generic util only handles simple TRC-20 sends, not smart contract calls

**Solution**: Build custom TRON transaction using TronWeb:
```typescript
import { TronWeb } from 'tronweb'

const tronWeb = new TronWeb({ fullHost: rpcUrl })

const txData = await tronWeb.transactionBuilder.triggerSmartContract(
contractAddress,
functionSelector,
options,
parameters,
from
)

const rawDataHex = typeof txData.transaction.raw_data_hex === 'string'
? txData.transaction.raw_data_hex
: (txData.transaction.raw_data_hex as Buffer).toString('hex')
```

**Affected Files**: `endpoints.ts` (custom `getUnsignedTronTransaction`)

---

## 15. TRON-Specific: Address Format (Not EVM!) ⚠️

**Problem**: TRON addresses use Base58 encoding (start with 'T'), not EVM hex with checksum.

**Wrong**: Using `getAddress()` from viem for TRON addresses
**Right**: TRON addresses are already in correct format, no checksumming needed

**Native TRX Address**: `T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb`

**Solution**:
```typescript
export const assetIdToTronToken = (assetId: AssetId): string => {
if (isToken(assetId)) {
const { assetReference } = fromAssetId(assetId)
return assetReference // Already in Base58 format
}
return 'T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb' // Native TRX
}
```

**Affected Files**: `utils/helpers/helpers.ts`

---

## 16. TRON-Specific: RPC URL Access ⚠️

**Problem**: SwapperConfig doesn't have VITE_UNCHAINED_TRON_HTTP_URL

**Solution**: Access RPC URL from TRON chain adapter instance:
```typescript
const adapter = assertGetTronChainAdapter(chainId)
const rpcUrl = adapter.httpProvider.getRpcUrl()
```

**Note**: Use `httpProvider.getRpcUrl()` for type-safe access (matches pattern in src/lib/utils/tron.ts).

**Affected Files**: `endpoints.ts` (getUnsignedTronTransaction), approval utilities

---

## 17. TRON-Specific: Address Format for triggerSmartContract ⚠️⚠️

**Problem**: triggerSmartContract requires addresses in **hex format**, not Base58.

**Error**: `invalid address (argument="address", value="TRwyik9Fb6HNjNhThJP3KJv4MAr1o7mCVv", code=INVALID_ARGUMENT)`

**Root Cause**: Even though TRON addresses are Base58, `triggerSmartContract` internally validates them as EVM addresses and needs hex format.

**Solution**: Convert all addresses to hex using `tronWeb.address.toHex()`:
```typescript
const parameters = [
{
type: 'address[]',
value: routeParams.path.map(addr => tronWeb.address.toHex(addr))
},
{
type: 'tuple',
value: {
to: tronWeb.address.toHex(recipientBase58),
// ... other fields
}
}
]

const txData = await tronWeb.transactionBuilder.triggerSmartContract(
contractAddress,
functionSelector,
options,
parameters,
tronWeb.address.toHex(fromAddress) // issuerAddress also needs hex
)
```

**Test with `node -e`**:
```bash
node -e "
const TronWeb = require('tronweb');
const tw = new TronWeb({ fullHost: 'https://api.trongrid.io' });
console.log(tw.address.toHex('TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'));
"
```

**Affected Files**: `endpoints.ts` (getUnsignedTronTransaction)

---

## 18. TRON-Specific: Immutable Array Parameters ⚠️

**Problem**: TronWeb mutates parameter arrays internally, causing errors if arrays are frozen/immutable.

**Error**: `Cannot assign to read only property '0' of object '[object Array]'`

**Root Cause**: TronWeb's `encodeArgs` function mutates the arrays when converting addresses. If arrays come from API responses or const declarations, they may be frozen.

**Solution**: Clone arrays before passing to TronWeb:
```typescript
const parameters = [
{ type: 'address[]', value: [...addressArray].map(addr => tronWeb.address.toHex(addr)) },
{ type: 'string[]', value: [...poolVersions] },
{ type: 'uint256[]', value: [...versionLengths] },
]
```

**Affected Files**: `endpoints.ts` (triggerSmartContract calls)

---

---

## 19. TRON-Specific: TronWeb 6.x Tuple Address Bug ⚠️⚠️⚠️

**Problem**: TronWeb 6.x doesn't convert TRON Base58 addresses to EVM format when they appear inside tuple parameters, causing ethers.js AbiCoder to reject them.

**Error**: `invalid address (argument="address", value="TRwyik9Fb6HNjNhThJP3KJv4MAr1o7mCVv", code=INVALID_ARGUMENT, version=6.13.5)`

**Root Cause**:
- TronWeb 6.x uses ethers.js internally for ABI parameter encoding
- It auto-converts addresses for `address` and `address[]` types: `TRwyik9...` → `41xxx...` → `0xxx...`
- BUT it forgets to convert addresses inside tuples!
- ethers.js AbiCoder expects EVM format (`0x...`), rejects TRON Base58 (`T...`)

**Real Example** (Sun.io swapper):
```typescript
// ✗ FAILS
{
type: 'tuple(uint256,uint256,address,uint256)',
value: ['100000', '95000', 'TRwyik9Fb6HNjNhThJP3KJv4MAr1o7mCVv', 1234567890]
}
// Error: invalid address

// ✓ WORKS
{
type: 'tuple(uint256,uint256,address,uint256)',
value: ['100000', '95000', '0xaf46828a4d975381e62bdb9f272388d97daf14b6', 1234567890]
}
```

**Solution**: Manually convert TRON addresses in tuple values to EVM format:

```typescript
/**
* Converts TRON Base58 addresses to EVM hex format (0x-prefixed).
* Required for TronWeb 6.x tuple parameters containing addresses.
*/
const convertAddressesToEvmFormat = (value: unknown): unknown => {
if (Array.isArray(value)) {
return value.map(v => convertAddressesToEvmFormat(v))
}

if (typeof value === 'string' && value.startsWith('T') && TronWeb.isAddress(value)) {
const hex = TronWeb.address.toHex(value) // TRwyik... → 41af46828a...
return hex.replace(/^41/, '0x') // 41af... → 0xaf...
}

return value
}

// Apply to tuple values before passing to triggerSmartContract
{
type: 'tuple(uint256,uint256,address,uint256)',
value: convertAddressesToEvmFormat([
amountIn,
amountOutMin,
recipientAddress, // Will be converted if TRON Base58
deadline,
])
}
```

**Why EVM format for TRON?**: TronWeb uses ethers.js (Ethereum library) internally for ABI encoding. The conversion is only for parameter encoding - the actual TRON transaction still uses TRON addresses.

**Test with `node -e`**:
```bash
node -e "
const { TronWeb } = require('tronweb');
const addr = 'TRwyik9Fb6HNjNhThJP3KJv4MAr1o7mCVv';
const hex = TronWeb.address.toHex(addr);
const evm = hex.replace(/^41/, '0x');
console.log('TRON Base58:', addr);
console.log('TRON Hex:', hex);
console.log('EVM Format:', evm);
// Output: EVM Format: 0xaf46828a4d975381e62bdb9f272388d97daf14b6
"
```

**Affected Scenarios**:
- Any TRON swapper using triggerSmartContract with tuple parameters
- Smart contract functions with struct parameters containing addresses
- Multi-parameter contract calls with addresses in complex types

**NOT Affected**:
- Simple `address` type parameters ✅ TronWeb handles
- `address[]` array parameters ✅ TronWeb handles
- TRC20 token transfers ✅ Use simple address type

**Affected Files**: Any file calling `triggerSmartContract` with tuple/struct parameters

---

**Remember**: Most bugs come from assumptions about API behavior. Always verify with actual API calls and responses!

**PROTIP**: Use `node -e` to quickly test library behavior, address conversions, and API parsing before writing full code!
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,5 @@ VITE_NOTIFICATIONS_SERVER_URL=https://shapeshiftnotifications-service-production
VITE_FEATURE_TRON=false
VITE_SUI_NODE_URL=https://fullnode.mainnet.sui.io:443
VITE_FEATURE_CETUS_SWAP=false
VITE_FEATURE_SUNIO_SWAP=false
VITE_FEATURE_MONAD=false
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,4 @@ VITE_FEATURE_WC_DIRECT_CONNECTION=true
VITE_FEATURE_TRON=true
VITE_FEATURE_MONAD=true
VITE_FEATURE_CETUS_SWAP=true
VITE_FEATURE_SUNIO_SWAP=true
5 changes: 5 additions & 0 deletions headers/csps/defi/swappers/Sunio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { Csp } from '../../../types'

export const csp: Csp = {
'connect-src': ['https://rot.endjgfsv.link', 'https://openapi.sun.io'],
}
2 changes: 2 additions & 0 deletions headers/csps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { csp as cowSwap } from './defi/swappers/CowSwap'
import { csp as nearIntents } from './defi/swappers/NearIntents'
import { csp as oneInch } from './defi/swappers/OneInch'
import { csp as portals } from './defi/swappers/Portals'
import { csp as sunio } from './defi/swappers/Sunio'
import { csp as thor } from './defi/swappers/Thor'
import { csp as discord } from './discord'
import { csp as banxa } from './fiatRamps/banxa'
Expand Down Expand Up @@ -121,6 +122,7 @@ export const csps = [
nearIntents,
oneInch,
portals,
sunio,
thor,
butterSwap,
foxPage,
Expand Down
9 changes: 9 additions & 0 deletions packages/swapper/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { portalsApi } from './swappers/PortalsSwapper/endpoints'
import { portalsSwapper } from './swappers/PortalsSwapper/PortalsSwapper'
import { relaySwapper } from './swappers/RelaySwapper'
import { relayApi } from './swappers/RelaySwapper/endpoints'
import { sunioApi } from './swappers/SunioSwapper/endpoints'
import { sunioSwapper } from './swappers/SunioSwapper/SunioSwapper'
import { thorchainApi } from './swappers/ThorchainSwapper/endpoints'
import { thorchainSwapper } from './swappers/ThorchainSwapper/ThorchainSwapper'
import { zrxApi } from './swappers/ZrxSwapper/endpoints'
Expand Down Expand Up @@ -92,6 +94,10 @@ export const swappers: Record<SwapperName, (SwapperApi & Swapper) | undefined> =
...cetusSwapper,
...cetusApi,
},
[SwapperName.Sunio]: {
...sunioSwapper,
...sunioApi,
},
[SwapperName.Test]: undefined,
}

Expand All @@ -106,6 +112,7 @@ const DEFAULT_ARBITRUM_BRIDGE_SLIPPAGE_DECIMAL_PERCENTAGE = '0' // no slippage f
const DEFAULT_CHAINFLIP_SLIPPAGE_DECIMAL_PERCENTAGE = '0.02' // 2%
const DEFAULT_BUTTERSWAP_SLIPPAGE_DECIMAL_PERCENTAGE = '0.015' // 1.5%
const DEFAULT_CETUS_SLIPPAGE_DECIMAL_PERCENTAGE = '0.005' // .5%
const DEFAULT_SUNIO_SLIPPAGE_DECIMAL_PERCENTAGE = '0.005' // .5%

export const getDefaultSlippageDecimalPercentageForSwapper = (
swapperName: SwapperName | undefined,
Expand Down Expand Up @@ -138,6 +145,8 @@ export const getDefaultSlippageDecimalPercentageForSwapper = (
return DEFAULT_NEAR_INTENTS_SLIPPAGE_DECIMAL_PERCENTAGE
case SwapperName.Cetus:
return DEFAULT_CETUS_SLIPPAGE_DECIMAL_PERCENTAGE
case SwapperName.Sunio:
return DEFAULT_SUNIO_SLIPPAGE_DECIMAL_PERCENTAGE
default:
return assertUnreachable(swapperName)
}
Expand Down
1 change: 1 addition & 0 deletions packages/swapper/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './swappers/ArbitrumBridgeSwapper'
export * from './swappers/BebopSwapper'
export * from './swappers/CetusSwapper'
export * from './swappers/ChainflipSwapper'
export * from './swappers/SunioSwapper'
export * from './swappers/CowSwapper'
export * from './swappers/JupiterSwapper'
export * from './swappers/PortalsSwapper'
Expand Down
Loading