-
Notifications
You must be signed in to change notification settings - Fork 198
feat: dedust swapper ton implementation #11713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
…to swapper package Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…in types Added dedustSpecific optional property to TradeQuoteStep type with: - poolAddress: DeDust pool contract address - sellAssetAddress: TON native or jetton address for sell asset - buyAssetAddress: TON native or jetton address for buy asset - sellAmount: Amount being sold - minBuyAmount: Minimum amount to receive (with slippage) - gasBudget: Recommended gas for the transaction - quoteTimestamp: When the quote was created Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…specific Add DeDust-specific types for the swapper implementation: - DedustSupportedChainId - type alias for TON mainnet - dedustSupportedChainIds - const array of supported chains - DedustAssetAddress - type for DeDust asset addresses (native/jetton) - DedustAssetValidationResult - discriminated union for asset validation - DedustQuote - quote data from DeDust pool estimation - DedustTradeSpecific - trade-specific metadata for quote steps Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…K client - Add dedustClientManager with singleton pattern for TonClient4 and Factory - Add DEDUST_TON_V4_ENDPOINT and gas budget constants - Factory initialized from MAINNET_FACTORY_ADDR from @dedust/sdk Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ion and rate calculation Add helper utilities for DeDust swapper: - isTonAsset: Check if asset is on TON chain - assetToDedustAddress: Convert ShapeShift asset to DeDust address format - validateTonAssets: Validate both assets are on TON and can be converted - calculateRate: Calculate exchange rate with precision conversion - slippageDecimalToBps: Convert slippage decimal to basis points - calculateMinBuyAmount: Calculate minimum buy amount after slippage Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…te fetch Implements getTradeQuote function for DeDust swapper that: - Validates TON assets using existing helpers - Gets pool from DeDust factory for the trading pair - Fetches estimated swap output from the pool - Calculates rate, slippage, and min buy amount - Returns TradeQuote with dedustSpecific metadata Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… fetching Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…entation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…nsaction Add DedustSwapper.ts following the StonfiSwapper pattern for TON transaction delegation. The swapper implements executeTonTransaction which delegates to signAndBroadcastTransaction for TON chain swap execution. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…appers map Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…itialState Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… and getEnabledSwappers - Add SwapperName.DeDust to isCrossAccountTradeSupported (returns false) - Add DedustSwap to destructured FeatureFlags in getEnabledSwappers - Add DeDust entry to return object in getEnabledSwappers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add the DeDust swapper feature flag to environment configuration: - .env: VITE_FEATURE_DEDUST_SWAP=false (default base) - .env.development: VITE_FEATURE_DEDUST_SWAP=true (enabled for dev) - .env.production: VITE_FEATURE_DEDUST_SWAP=false (disabled for prod) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…d save to SwapperIcon directory Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ran yarn install to install newly added dependencies including @dedust/sdk and @ton/ton packages required for the DeDust swapper integration. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fixed import ordering in endpoints.ts - Fixed object destructuring formatting - Applied ESLint auto-fixes to DedustSwapper files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix VaultNative swap payload: build manually using opcode 0xea06185d (VaultNative doesn't have static createSwapPayload like VaultJetton) - Fix VaultJetton swap: move deadline into swapParams object - Fix pool.getEstimatedSwapOut: open pool with client.open() first - Add type assertion for discriminated union narrowing in validation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes: - Add pool readiness check (ReadinessStatus.READY) in getTradeQuote.ts - Add pool readiness check (ReadinessStatus.READY) in getTradeRate.ts - Remove unused VaultNative import from endpoints.ts Critical security fix: Pool readiness must be verified before any swap operation to prevent potential fund loss as per DeDust SDK requirements. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds DeDust swapper integration for TON blockchain including feature flags, environment configuration, CSP headers, complete swapper implementation with trade routing and multi-hop support, type definitions, UI icons, and state management wiring. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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 |
The quote fetching was failing because jetton addresses were being incorrectly cast as Address types instead of properly parsed. Changed from: dedustAddress.address as unknown as Address Changed to: tonAddress(dedustAddress.address) This uses the @ton/core address() function to properly parse the string address into an Address object that the DeDust SDK requires. Fixes: DeDust quotes not appearing in the Available Quotes list Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds Content-Security-Policy connect-src directive for https://mainnet-v4.tonhubapi.com which is required for DeDust SDK to make on-chain quote calculations. - Created headers/csps/defi/swappers/Dedust.ts - Added dedust CSP to headers/csps/index.ts exports QA Fix Session: 0 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…requested) The DeDust swapper was only querying VOLATILE pools, which caused extremely poor rates for stablecoin pairs like USDE/USDT. VOLATILE pools use the constant product formula (x*y=k) which is not optimal for stable pairs. Changes: - Query both PoolType.STABLE and PoolType.VOLATILE pools in parallel - Compare output amounts and use the pool with the better rate - Store poolType in dedustSpecific for transaction building - Add DedustPoolType type to types For stablecoin pairs, STABLE pools use StableSwap curves optimized for near 1:1 trading, providing much better rates. Fixes: Bad quote rate for USDE→USDT (was 0.000089 instead of ~1.0) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…requested) Addresses QA feedback about selecting the best pool rate by implementing multi-hop routing through TON for jetton-to-jetton swaps. Changes: - Added multi-hop routing through TON (sellAsset → TON → buyAsset) - Compares direct pool rates with multi-hop route and selects the best - Added DedustSwapHop type to track hop details for multi-hop swaps - Multi-hop swaps have 2x gas budget to cover both transactions - Multi-hop swaps have 60s estimated execution time (vs 30s for direct) This improves rates for pairs that don't have direct pools with good liquidity (e.g., USDT → SUSDE can route through TON for better rates). QA Fix Session: 0 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…-requested) Problem: - DeDust multi-hop routing through TON was returning terrible rates for stablecoin pairs (e.g., 2.8 USDE for 10,000 USDT) - This happened when there was no direct pool, and multi-hop gave a catastrophically bad rate Solution: - Add rate sanity check for multi-hop routes without direct pools - Only use multi-hop if output is at least 1% of input value - This prevents users from accidentally accepting terrible rates Fixes: - getTradeQuote.ts: Add rate sanity check before returning multi-hop - getTradeRate.ts: Same fix for rate fetching Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove .auto-claude-security.json, .auto-claude-status, and .claude_settings.json files that were accidentally committed. Also remove .auto-claude/ from .gitignore. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.env.development (1)
95-103: Fix dotenv-linter key ordering.dotenv-linter warns this key should be placed before
VITE_FEATURE_KATANA. Reordering avoids lint noise.🧹 Suggested reorder
VITE_FEATURE_CETUS_SWAP=true VITE_FEATURE_AVNU_SWAP=true +VITE_FEATURE_DEDUST_SWAP=true VITE_FEATURE_NEAR=true VITE_FEATURE_KATANA=true VITE_FEATURE_YIELD_XYZ=true VITE_FEATURE_TON=true VITE_FEATURE_STONFI_SWAP=true -VITE_FEATURE_DEDUST_SWAP=true VITE_FEATURE_YIELDS_PAGE=true VITE_FEATURE_EARN_TAB=true VITE_FEATURE_YIELD_MULTI_ACCOUNT=true.env (1)
79-87: Fix dotenv-linter key ordering.dotenv-linter expects
VITE_FEATURE_DEDUST_SWAPto appear beforeVITE_FEATURE_JUPITER_SWAP.🧹 Suggested reorder
VITE_FEATURE_CHAINFLIP_SWAP=true VITE_FEATURE_CHAINFLIP_SWAP_DCA=true VITE_FEATURE_COWSWAP=true VITE_FEATURE_THOR_SWAP=true VITE_FEATURE_ZRX_SWAP=true +VITE_FEATURE_DEDUST_SWAP=false VITE_FEATURE_JUPITER_SWAP=true VITE_FEATURE_MAYA_SWAP=true -VITE_FEATURE_DEDUST_SWAP=false
🤖 Fix all issues with AI agents
In `@headers/csps/defi/swappers/Dedust.ts`:
- Around line 1-4: The CSP object export named csp in Dedust.ts is missing
required DeDust SDK hosts; update the 'connect-src' array in the exported csp to
include 'https://api.dedust.io' and 'https://hub.dedust.io' so it becomes
['https://mainnet-v4.tonhubapi.com', 'https://api.dedust.io',
'https://hub.dedust.io'] ensuring the SDK can call its API and Developer Hub.
In `@packages/swapper/src/swappers/DedustSwapper/swapperApi/getTradeQuote.ts`:
- Around line 195-239: The current getTradeQuote always attempts multi-hop via
tryGetMultiHopRoute regardless of caller intent; update getTradeQuote to read
the allowMultiHop flag from CommonTradeQuoteInput and only call
tryGetMultiHopRoute when allowMultiHop is true and neither asset is TON (i.e.
replace the current sellIsTon || buyIsTon ? Promise.resolve(null) :
tryGetMultiHopRoute(...) branch with a condition that also checks
allowMultiHop). Ensure the referenced symbols are getTradeQuote,
tryGetMultiHopRoute, sellIsTon, buyIsTon and the input property allowMultiHop so
multi‑hop routing is skipped when the caller disallows it.
- Around line 261-266: The 1% sanity check compares raw base units and ignores
differing token precisions, causing false rejects/accepts; update the logic in
getTradeQuote around minAcceptableRatio/isReasonableRate to normalize amounts by
token precisions before comparison: obtain the input and output token decimals
(e.g., from the route or trade quote input), scale multiHopRoute.amountOut and
amountIn to the same precision (using BigInt pow10 of the decimals difference)
and then apply the minAcceptableRatio check (isReasonableRate = scaledAmountOut
* minAcceptableRatio >= scaledAmountIn); ensure you handle both cases where
output has more or fewer decimals and keep using BigInt arithmetic.
In `@packages/swapper/src/swappers/DedustSwapper/swapperApi/getTradeRate.ts`:
- Around line 83-113: The code in getTradeRate.ts is swallowing exceptions
(catch blocks that return null) when calling factory.getPool, client.open,
pool.getReadinessStatus, and pool.getEstimatedSwapOut; update the try/catch
blocks to catch the error object (catch (err)) and log the error with context
(include poolType, sellDedustAsset, buyDedustAsset and any pool address if
available) before returning null so SDK/network failures are visible; use the
module's existing logger (or console.error if none) and include
ReadinessStatus/PoolType context when logging.
- Around line 250-266: The 1% sanity check currently multiplies
multiHopRoute.amountOut by minAcceptableRatio and compares to amountIn using raw
base units (in functions referencing multiHopRoute, bestDirectQuote, amountIn,
minAcceptableRatio), which mis-handles differing token decimals; instead compute
a normalized rate/amount (use the existing calculateRate or equivalent helper to
convert amounts to a common precision or convert amountOut to the input token's
base) and then apply the 1% threshold against the normalized values (replace the
raw check in the else branch with a normalized isReasonableRate using
calculateRate or converted amountOut before setting useMultiHop).
🧹 Nitpick comments (2)
packages/swapper/src/swappers/DedustSwapper/utils/constants.ts (1)
1-4: Align supported chain IDs with the {sell, buy} shape used by swappers.Keeping this as a simple array can drift from the SupportedChainIds contract used by filterAssetIdsBySellable/filterBuyAssetsBySellAssetId. Consider exporting the structured object to avoid mismatches. As per coding guidelines, align supported chain IDs with the standard SupportedChainIds pattern.
♻️ Proposed refactor
import { KnownChainIds } from '@shapeshiftoss/types' +import type { SupportedChainIds } from '../../../types' -export const DEDUST_SUPPORTED_CHAIN_IDS = [KnownChainIds.TonMainnet] as const +export const DEDUST_SUPPORTED_CHAIN_IDS: SupportedChainIds = { + sell: [KnownChainIds.TonMainnet], + buy: [KnownChainIds.TonMainnet], +}packages/swapper/src/swappers/DedustSwapper/endpoints.ts (1)
20-107: Add explicit return types for swap message builders.The repo guidelines require explicit return types for all TypeScript functions. Rather than defining
TonRawMessagelocally as shown in the diff, import it from the existing type in@shapeshiftoss/chain-adapterswhich already defines this type:♻️ Proposed refactor
import { Asset as DedustAsset, JettonRoot, VaultJetton } from '@dedust/sdk' import { toAddressNList } from '@shapeshiftoss/chain-adapters' +import type { ton } from '@shapeshiftoss/chain-adapters' import { TxStatus } from '@shapeshiftoss/unchained-client' import type { Address } from '@ton/core' import { address as tonAddress, beginCell, toNano } from '@ton/core' -const buildNativeTonSwapMessage = async ( +const buildNativeTonSwapMessage = async ( poolAddress: string, sellAmount: string, minBuyAmount: string, gasBudget: string, -) => { +): Promise<ton.TonRawMessage> => { -const buildJettonSwapMessage = async ( +const buildJettonSwapMessage = async ( from: string, poolAddress: string, sellAssetAddress: string, sellAmount: string, minBuyAmount: string, gasBudget: string, -) => { +): Promise<ton.TonRawMessage> => {
| import type { Csp } from '../../../../types' | ||
|
|
||
| export const csp: Csp = { | ||
| 'connect-src': ['https://mainnet-v4.tonhubapi.com'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Search for DeDust references in the codebase to understand implementation
rg -i "dedust" --type ts --type tsx --type js --type json -A 3 -B 1Repository: shapeshift/web
Length of output: 85
🏁 Script executed:
# Look for other CSP files to understand the pattern
fd -e ts -p "*/csps/*" | head -20Repository: shapeshift/web
Length of output: 345
🏁 Script executed:
# Check if there are any package.json references to dedust
rg "dedust" package.json -iRepository: shapeshift/web
Length of output: 40
🌐 Web query:
DeDust SDK API endpoints required hosts documentation
💡 Result:
Here are the DeDust hosts you should allow/whitelist for the SDK and API to function:
- api.dedust.io (DeDust API endpoints). [1]
- docs.dedust.io (official SDK/API docs). [2]
- hub.dedust.io (Developer Hub / SDK pages). [3]
- mainnet-v4.tonhubapi.com (TON RPC/endpoint used in SDK examples). [2]
Sources:
[1] DeDust API endpoint list (api.dedust.io).
[2] DeDust docs / Getting Started and SDK examples (docs.dedust.io).
[3] DeDust Developer Hub (hub.dedust.io).
🏁 Script executed:
# Search for DeDust implementation files
fd -type f -name "*[Dd]edust*"Repository: shapeshift/web
Length of output: 228
🏁 Script executed:
# Search for any DeDust SDK instantiation or API calls
rg "dedust|DeDust" -i --type ts --type js -A 3Repository: shapeshift/web
Length of output: 50370
🏁 Script executed:
# Search for any API calls to dedust.io domains
rg "dedust\.io|api\.dedust|hub\.dedust|docs\.dedust" -iRepository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Check what the DeDust SDK actually imports and uses
grep -r "@dedust/sdk" --include="*.ts" -A 2 | head -40Repository: shapeshift/web
Length of output: 1546
🏁 Script executed:
# Check for `@dedust/sdk` in package.json
rg "@dedust/sdk" package.json -A 1 -B 1Repository: shapeshift/web
Length of output: 40
🏁 Script executed:
# Look for SDK documentation or README in node_modules-like references
find . -name "dedust*" -o -name "*.md" | grep -i dedust | head -20Repository: shapeshift/web
Length of output: 217
Add missing DeDust SDK hosts to CSP.
The CSP is incomplete. DeDust SDK requires additional hosts beyond mainnet-v4.tonhubapi.com:
https://api.dedust.io(required for SDK API endpoints)https://hub.dedust.io(required for Developer Hub)
Update the CSP entry to:
'connect-src': ['https://mainnet-v4.tonhubapi.com', 'https://api.dedust.io', 'https://hub.dedust.io'],
Without these, the SDK will encounter CSP violations at runtime.
🤖 Prompt for AI Agents
In `@headers/csps/defi/swappers/Dedust.ts` around lines 1 - 4, The CSP object
export named csp in Dedust.ts is missing required DeDust SDK hosts; update the
'connect-src' array in the exported csp to include 'https://api.dedust.io' and
'https://hub.dedust.io' so it becomes ['https://mainnet-v4.tonhubapi.com',
'https://api.dedust.io', 'https://hub.dedust.io'] ensuring the SDK can call its
API and Developer Hub.
| export const getTradeQuote = async (input: CommonTradeQuoteInput): Promise<TradeQuoteResult> => { | ||
| const { | ||
| sellAsset, | ||
| buyAsset, | ||
| sellAmountIncludingProtocolFeesCryptoBaseUnit, | ||
| receiveAddress, | ||
| accountNumber, | ||
| slippageTolerancePercentageDecimal, | ||
| } = input | ||
|
|
||
| const validation = validateTonAssets(sellAsset, buyAsset) | ||
| if (!validation.isValid) { | ||
| return validation.error as TradeQuoteResult | ||
| } | ||
|
|
||
| const { sellAssetAddress, buyAssetAddress } = validation | ||
| const client = dedustClientManager.getClient() | ||
| const factory = dedustClientManager.getFactory() | ||
|
|
||
| try { | ||
| const sellDedustAsset = dedustAddressToAsset(sellAssetAddress) | ||
| const buyDedustAsset = dedustAddressToAsset(buyAssetAddress) | ||
| const amountIn = BigInt(sellAmountIncludingProtocolFeesCryptoBaseUnit) | ||
|
|
||
| // Check if either asset is TON (native) - skip multi-hop if so | ||
| const sellIsTon = sellAssetAddress.type === 'native' | ||
| const buyIsTon = buyAssetAddress.type === 'native' | ||
|
|
||
| // Try direct pools (STABLE and VOLATILE) in parallel with multi-hop route | ||
| // STABLE pools use StableSwap curves optimized for stablecoin pairs | ||
| // VOLATILE pools use standard constant product (x*y=k) formula | ||
| const [stableQuote, volatileQuote, multiHopRoute] = await Promise.all([ | ||
| tryGetPoolQuote(factory, client, PoolType.STABLE, sellDedustAsset, buyDedustAsset, amountIn), | ||
| tryGetPoolQuote( | ||
| factory, | ||
| client, | ||
| PoolType.VOLATILE, | ||
| sellDedustAsset, | ||
| buyDedustAsset, | ||
| amountIn, | ||
| ), | ||
| // Only try multi-hop if neither asset is TON | ||
| sellIsTon || buyIsTon | ||
| ? Promise.resolve(null) | ||
| : tryGetMultiHopRoute(factory, client, sellDedustAsset, buyDedustAsset, amountIn), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Respect allowMultiHop to avoid unexpected routing.
Multi‑hop is attempted even when the caller disallows it. This can override user/flow intent.
🛠️ Proposed fix
- const {
- sellAsset,
- buyAsset,
- sellAmountIncludingProtocolFeesCryptoBaseUnit,
- receiveAddress,
- accountNumber,
- slippageTolerancePercentageDecimal,
- } = input
+ const {
+ sellAsset,
+ buyAsset,
+ sellAmountIncludingProtocolFeesCryptoBaseUnit,
+ receiveAddress,
+ accountNumber,
+ slippageTolerancePercentageDecimal,
+ allowMultiHop,
+ } = input
...
- sellIsTon || buyIsTon
+ !allowMultiHop || sellIsTon || buyIsTon
? Promise.resolve(null)
: tryGetMultiHopRoute(factory, client, sellDedustAsset, buyDedustAsset, amountIn),🤖 Prompt for AI Agents
In `@packages/swapper/src/swappers/DedustSwapper/swapperApi/getTradeQuote.ts`
around lines 195 - 239, The current getTradeQuote always attempts multi-hop via
tryGetMultiHopRoute regardless of caller intent; update getTradeQuote to read
the allowMultiHop flag from CommonTradeQuoteInput and only call
tryGetMultiHopRoute when allowMultiHop is true and neither asset is TON (i.e.
replace the current sellIsTon || buyIsTon ? Promise.resolve(null) :
tryGetMultiHopRoute(...) branch with a condition that also checks
allowMultiHop). Ensure the referenced symbols are getTradeQuote,
tryGetMultiHopRoute, sellIsTon, buyIsTon and the input property allowMultiHop so
multi‑hop routing is skipped when the caller disallows it.
| // No direct pool - check if multi-hop rate is reasonable | ||
| // Reject if output is less than 1% of input (accounting for same-decimal assets) | ||
| // This catches catastrophic routing failures like getting 2.8 for 10,000 | ||
| const minAcceptableRatio = BigInt(100) // 1% = 1/100 | ||
| const isReasonableRate = multiHopRoute.amountOut * minAcceptableRatio >= amountIn | ||
| useMultiHop = isReasonableRate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the 1% sanity check to account for differing precisions.
The guard compares base units directly, so assets with different precisions can be incorrectly rejected (or accepted).
🛠️ Proposed fix
- const minAcceptableRatio = BigInt(100) // 1% = 1/100
- const isReasonableRate = multiHopRoute.amountOut * minAcceptableRatio >= amountIn
+ const precisionDelta = buyAsset.precision - sellAsset.precision
+ const scale = BigInt(10) ** BigInt(Math.abs(precisionDelta))
+ const amountInInBuyPrecision =
+ precisionDelta >= 0 ? amountIn * scale : amountIn / scale
+ const minAcceptableRatio = 100n // 1% = 1/100
+ const isReasonableRate =
+ multiHopRoute.amountOut * minAcceptableRatio >= amountInInBuyPrecision📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // No direct pool - check if multi-hop rate is reasonable | |
| // Reject if output is less than 1% of input (accounting for same-decimal assets) | |
| // This catches catastrophic routing failures like getting 2.8 for 10,000 | |
| const minAcceptableRatio = BigInt(100) // 1% = 1/100 | |
| const isReasonableRate = multiHopRoute.amountOut * minAcceptableRatio >= amountIn | |
| useMultiHop = isReasonableRate | |
| // No direct pool - check if multi-hop rate is reasonable | |
| // Reject if output is less than 1% of input (accounting for same-decimal assets) | |
| // This catches catastrophic routing failures like getting 2.8 for 10,000 | |
| const precisionDelta = buyAsset.precision - sellAsset.precision | |
| const scale = BigInt(10) ** BigInt(Math.abs(precisionDelta)) | |
| const amountInInBuyPrecision = | |
| precisionDelta >= 0 ? amountIn * scale : amountIn / scale | |
| const minAcceptableRatio = 100n // 1% = 1/100 | |
| const isReasonableRate = | |
| multiHopRoute.amountOut * minAcceptableRatio >= amountInInBuyPrecision | |
| useMultiHop = isReasonableRate |
🤖 Prompt for AI Agents
In `@packages/swapper/src/swappers/DedustSwapper/swapperApi/getTradeQuote.ts`
around lines 261 - 266, The 1% sanity check compares raw base units and ignores
differing token precisions, causing false rejects/accepts; update the logic in
getTradeQuote around minAcceptableRatio/isReasonableRate to normalize amounts by
token precisions before comparison: obtain the input and output token decimals
(e.g., from the route or trade quote input), scale multiHopRoute.amountOut and
amountIn to the same precision (using BigInt pow10 of the decimals difference)
and then apply the minAcceptableRatio check (isReasonableRate = scaledAmountOut
* minAcceptableRatio >= scaledAmountIn); ensure you handle both cases where
output has more or fewer decimals and keep using BigInt arithmetic.
| try { | ||
| const poolContract = await factory.getPool(poolType, [sellDedustAsset, buyDedustAsset]) | ||
|
|
||
| if (!poolContract) { | ||
| return null | ||
| } | ||
|
|
||
| const pool = client.open(poolContract) | ||
| const readinessStatus = await pool.getReadinessStatus() | ||
|
|
||
| if (readinessStatus !== ReadinessStatus.READY) { | ||
| return null | ||
| } | ||
|
|
||
| const { amountOut } = await pool.getEstimatedSwapOut({ | ||
| assetIn: sellDedustAsset, | ||
| amountIn, | ||
| }) | ||
|
|
||
| if (amountOut <= 0n) { | ||
| return null | ||
| } | ||
|
|
||
| return { | ||
| amountOut, | ||
| poolAddress: pool.address.toString(), | ||
| poolType: poolType === PoolType.STABLE ? 'STABLE' : 'VOLATILE', | ||
| } | ||
| } catch { | ||
| return null | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Log unexpected pool/route errors instead of swallowing them.
Line 111-112 and Line 190-191 return null without logging, which can hide SDK/network failures during rate discovery. Consider logging the error with pool type/assets before returning null. As per coding guidelines, avoid silently catching exceptions.
Also applies to: 131-192
🤖 Prompt for AI Agents
In `@packages/swapper/src/swappers/DedustSwapper/swapperApi/getTradeRate.ts`
around lines 83 - 113, The code in getTradeRate.ts is swallowing exceptions
(catch blocks that return null) when calling factory.getPool, client.open,
pool.getReadinessStatus, and pool.getEstimatedSwapOut; update the try/catch
blocks to catch the error object (catch (err)) and log the error with context
(include poolType, sellDedustAsset, buyDedustAsset and any pool address if
available) before returning null so SDK/network failures are visible; use the
module's existing logger (or console.error if none) and include
ReadinessStatus/PoolType context when logging.
| // Only use multi-hop if: | ||
| // 1. We have a direct pool quote to compare against, AND multi-hop is better | ||
| // 2. OR if there's no direct pool, the multi-hop rate must be reasonable (>= 1% of input) | ||
| // This prevents returning catastrophically bad routes (e.g., 2.8 USDE for 10,000 USDT) | ||
| let useMultiHop = false | ||
| if (multiHopRoute) { | ||
| if (bestDirectQuote) { | ||
| // Compare multi-hop vs direct - use multi-hop only if it's better | ||
| useMultiHop = multiHopRoute.amountOut > bestDirectQuote.amountOut | ||
| } else { | ||
| // No direct pool - check if multi-hop rate is reasonable | ||
| // Reject if output is less than 1% of input (accounting for same-decimal assets) | ||
| // This catches catastrophic routing failures like getting 2.8 for 10,000 | ||
| const minAcceptableRatio = BigInt(100) // 1% = 1/100 | ||
| const isReasonableRate = multiHopRoute.amountOut * minAcceptableRatio >= amountIn | ||
| useMultiHop = isReasonableRate | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multi-hop sanity check compares raw base units; can reject valid routes.
Line 261-265 compares amounts without normalizing for asset precision, so pairs with different decimals can be incorrectly rejected. Normalize the comparison (e.g., via calculateRate) before applying the 1% threshold.
🐛 Suggested fix (normalize before applying the 1% guard)
- const minAcceptableRatio = BigInt(100) // 1% = 1/100
- const isReasonableRate = multiHopRoute.amountOut * minAcceptableRatio >= amountIn
+ const multiHopRate = Number(
+ calculateRate(
+ multiHopRoute.amountOut.toString(),
+ sellAmountIncludingProtocolFeesCryptoBaseUnit,
+ buyAsset.precision,
+ sellAsset.precision,
+ ),
+ )
+ const isReasonableRate = Number.isFinite(multiHopRate) && multiHopRate >= 0.01
useMultiHop = isReasonableRate🤖 Prompt for AI Agents
In `@packages/swapper/src/swappers/DedustSwapper/swapperApi/getTradeRate.ts`
around lines 250 - 266, The 1% sanity check currently multiplies
multiHopRoute.amountOut by minAcceptableRatio and compares to amountIn using raw
base units (in functions referencing multiHopRoute, bestDirectQuote, amountIn,
minAcceptableRatio), which mis-handles differing token decimals; instead compute
a normalized rate/amount (use the existing calculateRate or equivalent helper to
convert amounts to a common precision or convert amountOut to the input token's
base) and then apply the 1% threshold against the normalized values (replace the
raw check in the else branch with a normalized isReasonableRate using
calculateRate or converted amountOut before setting useMultiHop).
Description
Adds DeDust swapper integration for TON blockchain to enable native TON swaps within ShapeShift.
Key changes:
Issue (if applicable)
closes #
Risk
This is behind a feature flag (
DedustSwap) and only affects TON blockchain swaps. The implementation follows existing swapper patterns.TON blockchain swaps using DeDust protocol. Only activated when feature flag is enabled.
Testing
Engineering
DedustSwapfeature flag via/flagsrouteOperations
Screenshots (if applicable)
https://jam.dev/c/e7c722ac-e2b1-45ea-b5ec-531b3c8e9c0b
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.