Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
46cc758
chore: extract cetus + mysten from build (#11280)
0xApotheosis Dec 3, 2025
03f3708
chore: fix CI, take 2 (#11282)
0xApotheosis Dec 4, 2025
6107811
feat: regenerate asset data 12/03/2025 (#11272)
github-actions[bot] Dec 4, 2025
7eee9a6
fix: production build size (#11284)
NeOMakinG Dec 4, 2025
06c2f8a
feat: sun.io swapper (#11261)
gomesalexandre Dec 4, 2025
ac0fb48
feat: thorchain tron support (#11266)
gomesalexandre Dec 4, 2025
c570cf4
Merge branch 'main' into develop
0xApotheosis Dec 5, 2025
d9dab95
chore: update rfox ipfs hash (#11291)
kaladinlight Dec 5, 2025
7de6e91
fix: correct daemon endpoints for thorchain and mayachain (#11292)
kaladinlight Dec 5, 2025
b1e6835
chore(deps): bump jws from 4.0.0 to 4.0.1 (#11286)
dependabot[bot] Dec 5, 2025
1b8b174
fix: dedupe SUI tokens (#11287)
gomesalexandre Dec 5, 2025
f61da34
fix: cetus use best rate (#11273)
gomesalexandre Dec 5, 2025
6f6b9cd
fix: better tron estimates (#11274)
gomesalexandre Dec 5, 2025
463f4b4
feat: disable THOR and Relay rates for GridPlus UTXO sells (#11268)
gomesalexandre Dec 5, 2025
4a11c07
fix: implement proper TRON getTransaction and fix Sun.io status check…
gomesalexandre Dec 5, 2025
df842b9
feat: add TRON gas estimates for all swappers (#11288)
gomesalexandre Dec 5, 2025
1750d14
feat: butter avalanche support (#11296)
gomesalexandre Dec 5, 2025
77bb0d7
feat: zcash support (#11290)
kaladinlight Dec 5, 2025
0739b04
feat: regenerate asset data 12/06/2025 (#11301)
github-actions[bot] Dec 7, 2025
d49d274
feat: near intents ZEC support (#11298)
gomesalexandre Dec 7, 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!
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ VITE_FEATURE_SUI=false
VITE_FEATURE_MAYACHAIN=true
VITE_FEATURE_BASE=true
VITE_FEATURE_OPTIMISM=true
VITE_FEATURE_ZCASH=false
VITE_FEATURE_SAVERS_VAULTS=true
VITE_FEATURE_SAVERS_VAULTS_DEPOSIT=false
VITE_FEATURE_SAVERS_VAULTS_WITHDRAW=false
Expand Down Expand Up @@ -230,4 +231,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
4 changes: 4 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
VITE_FEATURE_THORCHAIN_TCY_ACTIVITY=true
VITE_FEATURE_ADDRESS_BOOK=true
VITE_FEATURE_VULTISIG_WALLET=true
VITE_FEATURE_ZCASH=true

# mixpanel
VITE_MIXPANEL_TOKEN=a867ce40912a6b7d01d088cf62b0e1ff
Expand Down Expand Up @@ -50,6 +51,8 @@ VITE_UNCHAINED_MAYACHAIN_HTTP_URL=https://dev-api.mayachain.shapeshift.com
VITE_UNCHAINED_MAYACHAIN_WS_URL=wss://dev-api.mayachain.shapeshift.com
VITE_UNCHAINED_SOLANA_HTTP_URL=https://dev-api.solana.shapeshift.com
VITE_UNCHAINED_SOLANA_WS_URL=wss://dev-api.solana.shapeshift.com
VITE_UNCHAINED_ZCASH_HTTP_URL=https://dev-api.zcash.shapeshift.com
VITE_UNCHAINED_ZCASH_WS_URL=wss://dev-api.zcash.shapeshift.com

# nodes
VITE_ETHEREUM_NODE_URL=https://dev-api.ethereum.shapeshift.com/api/v1/jsonrpc
Expand Down Expand Up @@ -92,3 +95,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
2 changes: 2 additions & 0 deletions .env.private
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ VITE_UNCHAINED_MAYACHAIN_HTTP_URL=https://api.mayachain.shapeshift.com
VITE_UNCHAINED_MAYACHAIN_WS_URL=wss://api.mayachain.shapeshift.com
VITE_UNCHAINED_SOLANA_HTTP_URL=https://api.solana.shapeshift.com
VITE_UNCHAINED_SOLANA_WS_URL=wss://api.solana.shapeshift.com
VITE_UNCHAINED_ZCASH_HTTP_URL=https://api.zcash.shapeshift.com
VITE_UNCHAINED_ZCASH_WS_URL=wss://api.zcash.shapeshift.com

# nodes
VITE_ETHEREUM_NODE_URL=https://api.ethereum.shapeshift.com/api/v1/jsonrpc
Expand Down
2 changes: 2 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ VITE_UNCHAINED_MAYACHAIN_HTTP_URL=https://api.mayachain.shapeshift.com
VITE_UNCHAINED_MAYACHAIN_WS_URL=wss://api.mayachain.shapeshift.com
VITE_UNCHAINED_SOLANA_HTTP_URL=https://api.solana.shapeshift.com
VITE_UNCHAINED_SOLANA_WS_URL=wss://api.solana.shapeshift.com
VITE_UNCHAINED_ZCASH_HTTP_URL=https://api.zcash.shapeshift.com
VITE_UNCHAINED_ZCASH_WS_URL=wss://api.zcash.shapeshift.com

# nodes
VITE_ETHEREUM_NODE_URL=https://api.ethereum.shapeshift.com/api/v1/jsonrpc
Expand Down
2 changes: 2 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ VITE_UNCHAINED_MAYACHAIN_HTTP_URL=https://api.mayachain.shapeshift.com
VITE_UNCHAINED_MAYACHAIN_WS_URL=wss://api.mayachain.shapeshift.com
VITE_UNCHAINED_SOLANA_HTTP_URL=https://api.solana.shapeshift.com
VITE_UNCHAINED_SOLANA_WS_URL=wss://api.solana.shapeshift.com
VITE_UNCHAINED_ZCASH_HTTP_URL=https://api.zcash.shapeshift.com
VITE_UNCHAINED_ZCASH_WS_URL=wss://api.zcash.shapeshift.com

# nodes
VITE_ETHEREUM_NODE_URL=https://api.ethereum.shapeshift.com/api/v1/jsonrpc
Expand Down
10 changes: 10 additions & 0 deletions headers/csps/chains/zcash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { loadEnv } from 'vite'

import type { Csp } from '../../types'

const mode = process.env.MODE ?? process.env.NODE_ENV ?? 'development'
const env = loadEnv(mode, process.cwd(), '')

export const csp: Csp = {
'connect-src': [env.VITE_UNCHAINED_ZCASH_HTTP_URL, env.VITE_UNCHAINED_ZCASH_WS_URL],
}
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'],
}
4 changes: 4 additions & 0 deletions headers/csps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { csp as solana } from './chains/solana'
import { csp as sui } from './chains/sui'
import { csp as thorchain } from './chains/thorchain'
import { csp as tron } from './chains/tron'
import { csp as zcash } from './chains/zcash'
import { csp as chatwoot } from './chatwoot'
import { csp as customTokenImport } from './customTokenImport'
import { csp as foxy } from './defi/foxy'
Expand All @@ -36,6 +37,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 @@ -104,6 +106,7 @@ export const csps = [
sui,
thorchain,
tron,
zcash,
mayachain,
monad,
trustwallet,
Expand All @@ -121,6 +124,7 @@ export const csps = [
nearIntents,
oneInch,
portals,
sunio,
thor,
butterSwap,
foxPage,
Expand Down
Loading
Loading