-
Notifications
You must be signed in to change notification settings - Fork 198
feat: add TON blockchain support #11625
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
- Add TON chain adapter with address derivation, transaction building, signing, broadcasting - Add TON plugin with feature flag - Add TON to CoinGecko adapter for asset discovery - Add TON asset generation script - Add VITE_TON_NODE_URL and VITE_FEATURE_TON config - Add TON base asset definition and utility functions TON Chain specs: - SLIP44 Coin Type: 607 - Derivation Path: m/44'/607'/<account>' (3-level hardened, Ed25519) - Address Format: User-friendly base64 (WalletV4R2 StateInit) - Decimals: 9 - CoinGecko Platform: the-open-network
📝 WalkthroughWalkthroughAdds TON chain and STON.fi swapper support across the monorepo: new TON chain-adapter, CAIP constants, asset generation, Stonfi Omniston integration, UI/hooks, feature flags, CSP entries, plugin registration, and numerous type and state updates to wire TON into swap/trade flows. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as User/UI
participant App as ShapeShift App
participant Stonfi as Stonfi Swapper (Omniston)
participant TonAdapter as TON Chain Adapter
participant Wallet as Wallet (HDWallet)
participant RPC as TON RPC Node
UI->>App: request getTradeQuote(sell,buy)
App->>Stonfi: getTradeQuote(...)
Stonfi->>Stonfi: waitForQuote (retry up to 3)
Stonfi->>Omniston: subscribe / request quote
Omniston-->>Stonfi: quote response
Stonfi-->>App: TradeQuote (stonfiSpecific metadata)
UI->>App: request execute swap
App->>Stonfi: getUnsignedTonTransaction(...)
Stonfi->>TonAdapter: getSeqno / fee estimates
TonAdapter->>RPC: RPC calls (seqno, balances, fees)
RPC-->>TonAdapter: seqno / fee data
TonAdapter-->>Stonfi: seqno / fees
Stonfi-->>App: unsigned TonSignTx (rawMessages, seqno)
App->>Wallet: sign TonSignTx
Wallet-->>App: signed tx (boc/hex)
App->>TonAdapter: broadcastTransaction(signedBoc)
TonAdapter->>RPC: sendBoc
RPC-->>TonAdapter: txHash
TonAdapter-->>App: txHash
App->>Stonfi: checkTradeStatus(txHash)
Stonfi->>Omniston: poll/track trade status
Omniston-->>Stonfi: trade state updates
Stonfi-->>App: mapped TxStatus (Pending/Confirmed/Failed)
sequenceDiagram
participant App as ShapeShift App
participant TAC as TON Chain Adapter
participant RPC as TON RPC Node
participant Wallet as Wallet
App->>TAC: initialize(RPC_URL)
App->>TAC: getAccount(address)
TAC->>RPC: getAddressBalance
RPC-->>TAC: balance
TAC->>RPC: jettons/getBalances (HTTP)
RPC-->>TAC: token balances
TAC-->>App: account + tokens
App->>TAC: buildSendTransaction(to, amount)
TAC->>RPC: getAddressSeqno
RPC-->>TAC: seqno
TAC->>RPC: estimateFee
RPC-->>TAC: fee breakdown
TAC-->>App: TonSignTx payload
App->>Wallet: sign(TonSignTx)
Wallet-->>App: signed boc/hex
App->>TAC: broadcastTransaction(signedBoc)
TAC->>RPC: sendBoc
RPC-->>TAC: txHash
TAC-->>App: txHash
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
closes #11646 |
- Create quoteHelpers.ts with shared waitForQuote function - Move assetToOmnistonAddress, validateTonAssets, calculateRate, slippageDecimalToBps to helpers.ts - Add OmnistonAssetAddress, TonAssetValidationResult, QuoteResult types to types.ts - Simplify omnistonManager to lazy-load connection (fixes WebSocket timeout) - Refactor getTradeQuote.ts and getTradeRate.ts to use shared utilities - Remove async from waitForFirstTradeStatus (lint fix)
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: 8
🤖 Fix all issues with AI agents
In @.yarnrc.yml:
- Line 5: Revert the accidental local registry change by removing or restoring
the npmRegistryServer entry in .yarnrc.yml (the line "npmRegistryServer:
\"http://127.0.0.1:4873\""); either delete that key entirely so Yarn uses its
default registry or replace it with the public registry URL (e.g., the npm/Yarn
public registry) to avoid forcing developers/CI to use a local Verdaccio
instance.
In `@packages/chain-adapters/src/ton/TonChainAdapter.ts`:
- Around line 113-125: The code currently returns data.result as T even when
data.ok is true but data.result is undefined; update the response handling in
TonChainAdapter (the block using variables data, lastError, this.formatTonError,
this.isRetryableError) to detect when data.ok === true but data.result ===
undefined and treat it as an error: throw a descriptive Error (including the raw
response or context) instead of casting undefined to T, so callers won't receive
unexpected undefined values; preserve existing retry/backoff logic for retryable
errors and only return data.result when it is a defined value.
- Around line 353-374: The validateAddress method currently accepts
user-friendly TON addresses via the userFriendlyRegex without verifying the
embedded CRC16 checksum; update validateAddress to, after the regex matches,
decode the URL-safe base64-like string (handle '-' -> '+' and '_' -> '/' and add
padding), ensure the decoded buffer is 36 bytes, compute the CRC16-CCITT over
the first 34 bytes, compare it against the last 2 bytes of the decoded buffer,
and only return the valid result if the checksum matches (otherwise return
invalid); keep the rawAddressRegex branch unchanged and implement or call a
CRC16-CCITT helper referenced from validateAddress for the checksum computation.
In `@packages/chain-adapters/src/ton/types.ts`:
- Around line 41-48: The TonSignTx type declares an unused optional pubKey
field; either remove pubKey from the TonSignTx type declaration or thread it
into the signing call by extracting pubKey from the TonSignTx parameter and
passing it to wallet.tonSignTx in TonChainAdapter (where addressNList, message,
rawMessages, seqno, expireAt are passed). Locate the TonSignTx type and the
wallet.tonSignTx usage, then choose one fix: delete the pubKey property from the
TonSignTx type, or add pubKey to the argument object passed to wallet.tonSignTx
(ensuring the wallet API actually accepts a pubKey) and update any call sites
accordingly.
In `@packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts`:
- Line 143: Update the allowanceContract value in getTradeQuote (inside the
StonfiSwapper implementation) to use the non-EVM shorthand '0x0' instead of the
full Ethereum zero address '0x0000000000000000000000000000000000000000' so it
matches other non-EVM swappers (Jupiter, Cetus, Chainflip, Avnu); locate the
object/return that sets allowanceContract and replace the string value
accordingly.
In `@packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts`:
- Around line 82-96: The calculateRate function currently uses Number()
arithmetic which can lose precision; replace the Number/Math.pow logic with
BigNumber ops using bn (and bnOrZero for safety): convert
buyAmountCryptoBaseUnit and sellAmountCryptoBaseUnit via
bn(...).shiftedBy(-buyAssetPrecision) and
bnOrZero(...).shiftedBy(-sellAssetPrecision), check sellAmount is non-zero and
then return buyAmount.dividedBy(sellAmount).toFixed() (or toString()) so the
rate uses arbitrary-precision math; update imports to include bn and bnOrZero
and keep the original zero-return behavior when inputs are zero.
In `@src/components/Modals/Send/utils.ts`:
- Around line 452-467: The TEN transaction builder call is passing an unused
gasPrice into adapter.buildSendTransaction; remove the gasPrice property from
the chainSpecific payload so TON adapter receives only the fields it actually
consumes (e.g. memo/contractAddress). Locate the TON branch where
fromChainId(...).chainNamespace === CHAIN_NAMESPACE.Ton, and in the
buildSendTransaction call produced by assertGetTonChainAdapter(chainId) stop
reading fees.chainSpecific.gasPrice and omit chainSpecific.gasPrice from the
BuildSendTxInput<KnownChainIds.TonMainnet> object (keep other props like
accountNumber, sendMax, memo/contractAddress if present).
In `@src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts`:
- Around line 226-227: The TON branch currently returns supportsTon(wallet)
without checking the TON feature flag; update the tonChainId case to gate
support with the TON feature flag (e.g., use the existing selector/variable
isTonEnabled) so it returns isTonEnabled && supportsTon(wallet), mirroring how
nearChainId (isNearEnabled && supportsNear) and starknetChainId
(isStarknetEnabled && supportsStarknet) are implemented.
🧹 Nitpick comments (5)
src/lib/utils/ton.ts (1)
14-17: Type guard may throw on invalid input.The guard checks if
chainAdapteris truthy and an object, but then callsgetChainId()without verifying the method exists. If a non-adapter object is passed, this could throw a runtime error.Suggested defensive check
export const isTonChainAdapter = (chainAdapter: unknown): chainAdapter is ton.ChainAdapter => { if (!chainAdapter || typeof chainAdapter !== 'object') return false + if (!('getChainId' in chainAdapter) || typeof (chainAdapter as any).getChainId !== 'function') return false return (chainAdapter as ton.ChainAdapter).getChainId() === tonChainId }packages/chain-adapters/src/ton/TonChainAdapter.ts (1)
532-550: Consider extracting magic numbers as named constants.The hardcoded gas estimates and fallback fees would be clearer as named constants:
♻️ Optional refactor
+const DEFAULT_BASE_FEE_NANOTON = '5000000' +const DEFAULT_JETTON_BASE_FEE_NANOTON = '15000000' +const ESTIMATED_GAS_NATIVE = BigInt(50000) +const ESTIMATED_GAS_JETTON = BigInt(100000) +const JETTON_FORWARD_FEE_NANOTON = '10000000' +const JETTON_STORAGE_FEE_NANOTON = '5000000' - let baseFee = '5000000' + let baseFee = DEFAULT_BASE_FEE_NANOTON let forwardFee = '0' let storageFee = '0' // ... in try block: - const estimatedGas = contractAddress ? BigInt(100000) : BigInt(50000) + const estimatedGas = contractAddress ? ESTIMATED_GAS_JETTON : ESTIMATED_GAS_NATIVE // ... in catch block: - baseFee = contractAddress ? '15000000' : '5000000' + baseFee = contractAddress ? DEFAULT_JETTON_BASE_FEE_NANOTON : DEFAULT_BASE_FEE_NANOTONsrc/lib/account/ton.ts (1)
1-6: Minor: Consider grouping imports consistently.The blank line on line 3 separates the external imports, but line 6 has another local import after the type import. This is minor but could be consolidated:
import { toAccountId, tonChainId } from '@shapeshiftoss/caip' import type { AccountMetadataById } from '@shapeshiftoss/types' - -import type { DeriveAccountIdsAndMetadata } from './account' - import { assertGetTonChainAdapter, supportsTon } from '@/lib/utils/ton' + +import type { DeriveAccountIdsAndMetadata } from './account'packages/swapper/src/swappers/StonfiSwapper/endpoints.ts (1)
181-198: Consider extracting account address parsing to avoid brittle string splitting.The
split(':')[2]pattern for extracting the address fromsellAccountIdassumes a specific CAIP format. If the format changes or varies, this could fail silently with an empty string.Consider using a CAIP parsing utility
- address: swap.sellAccountId.split(':')[2] ?? '', + address: fromAccountId(swap.sellAccountId).account,This would require importing
fromAccountIdfrom@shapeshiftoss/caip.packages/swapper/src/swappers/StonfiSwapper/types.ts (1)
27-31: Consider renaming to avoid shadowing the globalQuoteResulttype.This
QuoteResulttype shadows the globalQuoteResultexported frompackages/swapper/src/types.ts(which isResult<TradeQuote[], SwapErrorRight> & { swapperName: SwapperName }). While the two types serve different purposes and are used in different contexts, the name collision could cause confusion during imports.Suggested rename for clarity
-export type QuoteResult = +export type OmnistonQuoteResult = | { type: 'success'; quote: Quote } | { type: 'noQuote' } | { type: 'timeout' } | { type: 'error'; error: unknown }This would require updating the import in
helpers.ts.
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
Outdated
Show resolved
Hide resolved
package.json
Outdated
| }, | ||
| "packageManager": "yarn@3.5.0", | ||
| "resolutions": { | ||
| "@shapeshiftoss/hdwallet-native": "1.62.39", |
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.
ser why is this a resolution
src/config.ts
Outdated
| VITE_BOARDROOM_APP_BASE_URL: url({ default: 'https://boardroom.io/shapeshift/' }), | ||
| VITE_THORCHAIN_MIDGARD_URL: url({ default: 'https://midgard.thorchain.info/v2' }), | ||
| VITE_MAYACHAIN_MIDGARD_URL: url({ default: 'https://midgard.mayachain.info/v2' }), | ||
| VITE_BOARDROOM_APP_BASE_URL: url({ |
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.
revert fluff here
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.
ditto
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.
ditto
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.
ditto
| } | ||
|
|
||
| export const isTonChainAdapter = (chainAdapter: unknown): chainAdapter is ton.ChainAdapter => { | ||
| if (!chainAdapter || typeof chainAdapter !== 'object') return false |
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.
the fuark
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.
ditto revert hunks
gomesalexandre
left a comment
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.
Assuming diff is cleaned up, get in boi shapeshift/hdwallet#782 (review)
- Fix isTonChainAdapter defensive pattern - Add assertGetTonChainAdapter helper functions - Add TON feature flag checks to AssetService and useWalletSupportsChain - Fix TonChainAdapter undefined result handling - Update allowanceContract to '0x0' for native TON swaps - Fix calculateRate to use BigNumber for precision - Remove unused gasPrice from Send utils - Clean up portfolioSlice TON case handling - Update hdwallet dependencies to 1.62.40-ton-chain.1
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
🤖 Fix all issues with AI agents
In `@packages/chain-adapters/src/ton/TonChainAdapter.ts`:
- Around line 81-144: The rpcRequest method currently retries all thrown errors
in the catch because non-retryable TON RPC errors thrown from the data.error
branch don't get recognized; update rpcRequest so non-retryable errors are
rethrown without triggering the outer retry logic—either by throwing a distinct
error that includes a 'TON RPC' marker or by using a custom NonRetryableTonError
class; specifically, in the branch where you set lastError = new
Error(this.formatTonError(data.error)) and detect
!this.isRetryableError(data.error), change the throw to throw new Error(`TON
RPC: ${this.formatTonError(data.error)}`) (or throw new
NonRetryableTonError(...)) so the outer catch sees the 'TON RPC' marker and
rethrows immediately instead of applying exponential backoff and retrying.
In `@packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts`:
- Around line 81-88: The returned SwapError currently drops the underlying
Stonfi error (quoteResult.error) and only logs it; update the error returned by
Err(makeSwapErrorRight(...)) inside getTradeQuote (where quoteResult.type ===
'error') to include the original error details—either append quoteResult.error
to the message or add it as a new field (e.g., cause or details) on the object
produced by makeSwapErrorRight so callers receive the actual Stonfi error
alongside the TradeQuoteError.QueryFailed code.
- Around line 18-40: Import the StonfiTradeSpecific type from '../types' and add
an explicit return type annotation to the buildStonfiSpecific function so its
signature reads returning StonfiTradeSpecific; update the declaration of
buildStonfiSpecific (the one taking Quote, OmnistonAssetAddress bidAssetAddress
and askAssetAddress) to use that return type and keep the same returned object
shape, and apply the same import + annotation change to the identical
buildStonfiSpecific in getTradeRate.ts to ensure consistent typing.
In `@packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts`:
- Around line 100-107: The slippageDecimalToBps function can return NaN if
parseFloat receives a malformed string; update slippageDecimalToBps to parse and
validate the value (e.g., trim the input, call parseFloat on
slippageTolerancePercentageDecimal) and then check Number.isFinite(parsed)
before converting to BPS, falling back to defaultSlippageBps if the parsed value
is not a finite number or the input is undefined/empty; ensure you still
Math.round(parseFloat(...) * 10000) only for valid finite numbers.
In `@src/components/Modals/Send/utils.ts`:
- Around line 187-198: The TON fee estimation block (case CHAIN_NAMESPACE.Ton)
builds getFeeDataInput without including the token contract address, which
TonChainAdapter.getFeeData expects to adjust fees for jetton transfers; update
the getFeeDataInput.chainSpecific object (in the code path using
assertGetTonChainAdapter and getFeeData) to include contractAddress (e.g., set
contractAddress: asset.contractAddress or the appropriate asset token
identifier) so token transfers correctly bump fees.
♻️ Duplicate comments (1)
packages/chain-adapters/src/ton/TonChainAdapter.ts (1)
357-378: User‑friendly TON address validation still skips checksum.Line 372-374 only checks length/charset. User‑friendly addresses with invalid CRC16 will still pass. Consider decoding and validating the checksum.
| const buildStonfiSpecific = ( | ||
| quote: Quote, | ||
| bidAssetAddress: OmnistonAssetAddress, | ||
| askAssetAddress: OmnistonAssetAddress, | ||
| ) => ({ | ||
| quoteId: quote.quoteId, | ||
| resolverId: quote.resolverId, | ||
| resolverName: quote.resolverName, | ||
| tradeStartDeadline: quote.tradeStartDeadline, | ||
| gasBudget: quote.gasBudget, | ||
| bidAssetAddress: quote.bidAssetAddress ?? bidAssetAddress, | ||
| askAssetAddress: quote.askAssetAddress ?? askAssetAddress, | ||
| bidUnits: quote.bidUnits, | ||
| askUnits: quote.askUnits, | ||
| referrerAddress: quote.referrerAddress, | ||
| referrerFeeAsset: quote.referrerFeeAsset, | ||
| referrerFeeUnits: quote.referrerFeeUnits, | ||
| protocolFeeAsset: quote.protocolFeeAsset, | ||
| protocolFeeUnits: quote.protocolFeeUnits, | ||
| quoteTimestamp: quote.quoteTimestamp, | ||
| estimatedGasConsumption: quote.estimatedGasConsumption, | ||
| params: quote.params, | ||
| }) |
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:
#!/bin/bash
# Find existing Stonfi-specific types to use for the return type
echo "=== Searching for Stonfi-specific types ==="
rg -n "stonfiSpecific|StonfiSpecific" packages/swapper/src/swappers/StonfiSwapper -g'*.ts'
echo ""
echo "=== Searching for type/interface definitions ending with Specific ==="
rg -n "type.*Specific|interface.*Specific" packages/swapper/src/swappers/StonfiSwapper -g'*.ts'
echo ""
echo "=== Checking the full getTradeQuote.ts file to see function signature ==="
head -50 packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tsRepository: shapeshift/web
Length of output: 4868
🏁 Script executed:
#!/bin/bash
# Check the StonfiTradeSpecific type definition
sed -n '46,80p' packages/swapper/src/swappers/StonfiSwapper/types.tsRepository: shapeshift/web
Length of output: 581
🏁 Script executed:
#!/bin/bash
# Check the imports in getTradeQuote.ts to see if StonfiTradeSpecific is imported
head -20 packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts | grep -E "import|StonfiTradeSpecific"Repository: shapeshift/web
Length of output: 621
🏁 Script executed:
#!/bin/bash
# Check if getTradeRate.ts has the same pattern and if there's any explicit return type usage
echo "=== Checking buildStonfiSpecific in getTradeRate.ts ==="
sed -n '18,35p' packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeRate.ts
echo ""
echo "=== Checking for other functions with explicit return types in this swapper ==="
rg -n "const \w+.*=>.*:\s*\{" packages/swapper/src/swappers/StonfiSwapper -g'*.ts' | head -5Repository: shapeshift/web
Length of output: 854
🏁 Script executed:
#!/bin/bash
# Check where StonfiTradeSpecific is defined and if it's exported
grep -n "export type StonfiTradeSpecific" packages/swapper/src/swappers/StonfiSwapper/types.ts
echo ""
# Verify the exact line of buildStonfiSpecific and its full context
sed -n '18,42p' packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tsRepository: shapeshift/web
Length of output: 1025
Add explicit return type : StonfiTradeSpecific to buildStonfiSpecific.
The StonfiTradeSpecific type already exists in types.ts and matches the returned object shape. Import it and annotate the function to align with explicit typing standards:
Import addition
import type { StonfiTradeSpecific } from '../types'Then update the function signature:
const buildStonfiSpecific = (
quote: Quote,
bidAssetAddress: OmnistonAssetAddress,
askAssetAddress: OmnistonAssetAddress,
): StonfiTradeSpecific => ({The same applies to the identical buildStonfiSpecific function in getTradeRate.ts.
🤖 Prompt for AI Agents
In `@packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts`
around lines 18 - 40, Import the StonfiTradeSpecific type from '../types' and
add an explicit return type annotation to the buildStonfiSpecific function so
its signature reads returning StonfiTradeSpecific; update the declaration of
buildStonfiSpecific (the one taking Quote, OmnistonAssetAddress bidAssetAddress
and askAssetAddress) to use that return type and keep the same returned object
shape, and apply the same import + annotation change to the identical
buildStonfiSpecific in getTradeRate.ts to ensure consistent typing.
- Remove unused pubKey field from TonSignTx type - Add CRC16 checksum validation for user-friendly TON addresses - Improve non-retryable error handling (insufficient balance, invalid tx, etc.) - Include contractAddress in TON fee estimation for jetton transfers - Add explicit StonfiTradeSpecific return type to buildStonfiSpecific - Preserve Omniston error details in SwapError with cause field - Guard against NaN/invalid slippage inputs - Fix isTonChainAdapter to match starknet pattern
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: 3
🤖 Fix all issues with AI agents
In `@packages/chain-adapters/src/ton/TonChainAdapter.ts`:
- Around line 569-583: The method broadcastTransaction currently returns an
empty string when result.hash is missing; change it to treat a missing hash as
an error by checking if result.hash is falsy after the rpcRequest and then throw
ErrorHandler(new Error('Missing transaction hash'), { translation:
'chainAdapters.errors.broadcastTransaction', details: result }) instead of
returning ''; update the logic in broadcastTransaction (which calls rpcRequest
and uses ErrorHandler) so callers always get a thrown error on missing hash
rather than an empty string.
In `@packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts`:
- Around line 42-161: The stored slippageDecimal in the returned TradeQuote can
differ from the actual BPS used because getTradeQuote calls slippageDecimalToBps
(producing slippageBps) but continues to store the original
slippageTolerancePercentageDecimal; update getTradeQuote to derive a canonical
decimal string from slippageBps (e.g., slippageBps / 10000 as a string, using
STONFI_DEFAULT_SLIPPAGE_BPS when the input was invalid) and assign that
canonical value to tradeQuote.slippageTolerancePercentageDecimal so the returned
quote always reflects the BPS actually used.
In `@packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts`:
- Around line 116-145: The code in waitForQuote can throw synchronously from
omniston.requestForQuote(), leaving subscription undefined and causing the
timeout handler to throw when calling subscription.unsubscribe(); fix by
declaring subscription variable before calling omniston.requestForQuote(), wrap
the call in try/catch, and in the catch clear the timer and resolve with an
error (or appropriate result), and ensure subscription is always a safe object
(or checked) before calling unsubscribe; apply the same pattern to the
omniston.trackTrade() usage in endpoints.ts to guard against synchronous throws.
🧹 Nitpick comments (3)
src/lib/utils/ton.ts (1)
14-17: Type guard may throw ifgetChainIdmethod doesn't exist.The guard checks
typeof chainAdapter !== 'object'but then directly calls(chainAdapter as ton.ChainAdapter).getChainId(). IfchainAdapteris an object but not a chain adapter (i.e., lacksgetChainIdmethod), this will throw.🛠️ Suggested safer implementation
export const isTonChainAdapter = (chainAdapter: unknown): chainAdapter is ton.ChainAdapter => { if (!chainAdapter || typeof chainAdapter !== 'object') return false - return (chainAdapter as ton.ChainAdapter).getChainId() === tonChainId + if (typeof (chainAdapter as ton.ChainAdapter).getChainId !== 'function') return false + return (chainAdapter as ton.ChainAdapter).getChainId() === tonChainId }packages/chain-adapters/src/ton/TonChainAdapter.ts (2)
193-224: Consider wrapping JSON parsing in try-catch for clearer error handling.If the API returns non-JSON (e.g., HTML error page),
response.json()throws a syntax error that gets retried. While the retry logic handles this, distinguishing between network errors and malformed responses could improve debugging.
291-298: Consider logging balance fetch failures for debugging.The catch block silently defaults to '0' balance. While this prevents failures from breaking the account fetch, adding a log would help diagnose RPC connectivity issues.
🛠️ Suggested tweak
try { const accountInfo = await this.rpcRequest<TonAccountInfo>('getAddressInformation', { address: pubkey, }) balance = accountInfo.balance ?? '0' - } catch { + } catch (err) { + console.warn('[TON] Failed to fetch balance, defaulting to 0:', err) balance = '0' }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
packages/chain-adapters/src/ton/TonChainAdapter.tspackages/chain-adapters/src/ton/types.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.tssrc/components/Modals/Send/utils.tssrc/lib/utils/ton.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/chain-adapters/src/ton/types.ts
- src/components/Modals/Send/utils.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Never assume a library is available - always check imports/package.json first
Prefer composition over inheritance
Write self-documenting code with clear variable and function names
Keep functions small and focused on a single responsibility
Avoid deep nesting - use early returns instead
Prefer procedural and easy to understand code
Never expose, log, or commit secrets, API keys, or credentials
Validate all inputs, especially user inputs
Handle errors gracefully with meaningful messages
Don't silently catch and ignore exceptions
Log errors appropriately for debugging
Provide fallback behavior when possible
Use appropriate data structures for the task
Never add code comments unless explicitly requested
When modifying code, do not add comments that reference previous implementations or explain what changed. Comments should only describe the current logic and functionality.
Use meaningful names for branches, variables, and functions
Always runyarn lint --fixandyarn type-checkafter making changes
Avoidletvariable assignments - preferconstwith inline IIFE switch statements or extract to functions for conditional logic
Files:
src/lib/utils/ton.tspackages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Avoid useEffect where practical - use it only when necessary and following best practices
Avoid 'any' types - use specific type annotations instead
For default values with user overrides, use computed values (useMemo) instead of useEffect - pattern:userSelected ?? smartDefault ?? fallback
When function parameters are unused due to interface requirements, refactor the interface or implementation to remove them rather than prefixing with underscore
Sanitize data before displaying to prevent XSS
Memoize aggressively - wrap component variables inuseMemoand callbacks inuseCallbackwhere possible
For static JSX icon elements (e.g.,<TbCopy />) that don't depend on state/props, define them as constants outside the component to avoid re-renders instead of using useMemo
Account for light/dark mode usinguseColorModeValuehook
Account for responsive mobile designs in all UI components
When applying styles, use the existing standards and conventions of the codebase
Use Chakra UI components and conventions
All copy/text must use translation keys - never hardcode strings
Use the translation hook:useTranslate()fromreact-polyglot
UseuseFeatureFlag('FlagName')hook to access feature flag values in components
Prefertypeoverinterfacefor type definitions
Use strict typing - avoidany
UseNominaltypes for domain identifiers (e.g.,WalletId,AccountId)
Import types from@shapeshiftoss/caipfor chain/account/asset IDs
UseuseAppSelectorfor Redux state
UseuseAppDispatchfor Redux actions
Memoize expensive computations withuseMemo
Memoize callbacks withuseCallback
**/*.{ts,tsx}: UseResult<T, E>pattern for error handling in swappers and APIs; ALWAYS useOk()andErr()from@sniptt/monads; AVOID throwing within swapper API implementations
ALWAYS use custom error classes from@shapeshiftoss/errorswith meaningful error codes for internationalization and relevant details in error objects
ALWAYS wrap async op...
Files:
src/lib/utils/ton.tspackages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)
**/*.{js,jsx,ts,tsx}: Use camelCase for variables, functions, and methods with descriptive names that explain the purpose
Use verb prefixes for functions that perform actions (e.g., fetch, validate, execute, update, calculate)
Use UPPER_SNAKE_CASE for constants and configuration values with descriptive names
Usehandleprefix for event handlers with descriptive names in camelCase
Use descriptive boolean variable names withis,has,can,shouldprefixes
Use named exports for components, functions, and utilities instead of default exports
Use descriptive import names and avoid renaming imports unless necessary
Avoid non-descriptive variable names likedata,item,obj, and single-letter variable names except in loops
Avoid abbreviations in names unless they are widely understood
Avoid generic function names likefn,func, orcallback
Files:
src/lib/utils/ton.tspackages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
**/swapper{s,}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)
ALWAYS use
makeSwapErrorRightfor swapper errors withTradeQuoteErrorenum for error codes and provide detailed error information
Files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
packages/swapper/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/swapper.mdc)
packages/swapper/**/*.ts: Use TypeScript with explicit types (e.g., SupportedChainIds) for all code in the Swapper system
Use camelCase for variable and function names in the Swapper system
Use PascalCase for types, interfaces, and enums in the Swapper system
Use kebab-case for filenames in the Swapper system
Files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
packages/swapper/src/swappers/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/swapper.mdc)
packages/swapper/src/swappers/**/*.ts: Adhere to the Swapper directory structure: each swapper resides in packages/swapper/src/swappers// with required files (SwapperName.ts, endpoints.ts, types.ts, utils/constants.ts, utils/helpers.ts)
Validate inputs and log errors for debugging in Swapper system implementations
Swapper files must be located in packages/swapper/src/swappers/ directory structure and not placed outside this location
Avoid side effects in swap logic; ensure swap methods are deterministic and stateless
Files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
🧠 Learnings (53)
📓 Common learnings
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/**/*.ts : Use TypeScript with explicit types (e.g., SupportedChainIds) for all code in the Swapper system
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterBuyAssetsBySellAssetId method to filter assets by supported chain IDs in the buy property
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterAssetIdsBySellable method to filter assets by supported chain IDs in the sell property
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/utils/constants.ts : Define supported chain IDs for each swapper in utils/constants.ts with both 'sell' and 'buy' properties following the pattern: SupportedChainIds type
Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : Import types from `shapeshiftoss/caip` for chain/account/asset IDs
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Reuse executeEvmTransaction utility for EVM-based swappers instead of implementing custom transaction execution
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10128
File: .cursor/rules/error-handling.mdc:266-274
Timestamp: 2025-07-29T10:35:22.059Z
Learning: NeOMakinG prefers less nitpicky suggestions on documentation and best practices files, finding overly detailed suggestions on minor implementation details (like console.error vs logger.error) too granular for cursor rules documentation.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/pages/Dashboard/components/AccountList/AccountTable.tsx:60-0
Timestamp: 2025-09-02T08:34:08.157Z
Learning: NeOMakinG prefers code review comments to focus only on actual PR changes, not pre-existing code issues, unless there are critical security or correctness concerns directly related to the new functionality.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10234
File: src/components/MultiHopTrade/hooks/useGetTradeQuotes/hooks/useTrackTradeQuotes.ts:42-86
Timestamp: 2025-08-08T11:41:22.794Z
Learning: NeOMakinG prefers not to include refactors in move-only PRs; such suggestions should be deferred to follow-up issues instead of being applied within the same PR.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/Table/Table.theme.ts:177-180
Timestamp: 2025-09-02T12:38:46.940Z
Learning: NeOMakinG prefers to defer technical debt and CSS correctness issues (like improper hover selectors) to follow-up PRs when the current PR is already large and focused on major feature implementation, even when the issues are valid from a usability/technical perspective.
📚 Learning: 2025-12-17T14:50:01.629Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11449
File: packages/chain-adapters/src/tron/TronChainAdapter.ts:570-596
Timestamp: 2025-12-17T14:50:01.629Z
Learning: In packages/chain-adapters/src/tron/TronChainAdapter.ts, the parseTx method uses `unknown` type for the txHashOrTx parameter intentionally. TRON is a "second-class chain" that works differently from other chains - it accepts either a string hash (to fetch TronTx via unchained client) or a TronTx object directly. The base chain-adapter interface is strongly typed and doesn't accommodate this flexible signature, so `unknown` is used as an appropriate escape hatch rather than a type safety issue.
Applied to files:
src/lib/utils/ton.tspackages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/**/*.ts : Use TypeScript with explicit types (e.g., SupportedChainIds) for all code in the Swapper system
Applied to files:
src/lib/utils/ton.tspackages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-11-24T21:20:04.979Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : Import types from `shapeshiftoss/caip` for chain/account/asset IDs
Applied to files:
src/lib/utils/ton.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/utils/constants.ts : Define supported chain IDs for each swapper in utils/constants.ts with both 'sell' and 'buy' properties following the pattern: SupportedChainIds type
Applied to files:
src/lib/utils/ton.tspackages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-11-19T16:59:50.569Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11012
File: src/context/WalletProvider/Vultisig/components/Connect.tsx:24-59
Timestamp: 2025-11-19T16:59:50.569Z
Learning: In src/context/WalletProvider/*/components/Connect.tsx files across the ShapeShift web codebase, the established pattern for handling null/undefined adapter from getAdapter() is to simply check `if (adapter) { ... }` without an else clause. All wallet Connect components (Coinbase, Keplr, Phantom, Ledger, MetaMask, WalletConnectV2, KeepKey, Vultisig) follow this pattern—they reset loading state after the if block but do not show error messages when adapter is null. This is an intentional design decision and should be maintained for consistency.
Applied to files:
src/lib/utils/ton.tspackages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-08-11T09:45:51.174Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10219
File: src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx:175-180
Timestamp: 2025-08-11T09:45:51.174Z
Learning: gomesalexandre prefers truthy checks over explicit boolean comparisons (e.g., `walletSupportsSellAssetChain` instead of `walletSupportsSellAssetChain === true`) when dealing with tri-state values (boolean | null) in TypeScript, as the falsy behavior for null/undefined is intentional and acceptable.
Applied to files:
src/lib/utils/ton.ts
📚 Learning: 2025-12-04T22:57:50.850Z
Learnt from: kaladinlight
Repo: shapeshift/web PR: 11290
File: packages/chain-adapters/src/utxo/zcash/ZcashChainAdapter.ts:48-51
Timestamp: 2025-12-04T22:57:50.850Z
Learning: In packages/chain-adapters/src/**/*ChainAdapter.ts files, the getName() method uses the pattern `const enumIndex = Object.values(ChainAdapterDisplayName).indexOf(ChainAdapterDisplayName.XXX); return Object.keys(ChainAdapterDisplayName)[enumIndex]` to reverse-lookup the enum key from its value. This is the established pattern used consistently across almost all chain adapters (Bitcoin, Ethereum, Litecoin, Dogecoin, Polygon, Arbitrum, Cosmos, etc.) and should be preserved for consistency when adding new chain adapters.
Applied to files:
src/lib/utils/ton.tspackages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-10-23T14:27:19.073Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10857
File: src/plugins/walletConnectToDapps/eventsManager/useWalletConnectEventsHandler.ts:101-104
Timestamp: 2025-10-23T14:27:19.073Z
Learning: In WalletConnect wallet_switchEthereumChain and wallet_addEthereumChain requests, the chainId parameter is always present as per the protocol spec. Type guards checking for missing chainId in these handlers (like `if (!evmNetworkIdHex) return`) are solely for TypeScript compiler satisfaction, not real runtime edge cases.
Applied to files:
src/lib/utils/ton.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterBuyAssetsBySellAssetId method to filter assets by supported chain IDs in the buy property
Applied to files:
src/lib/utils/ton.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2026-01-05T23:24:54.841Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11569
File: packages/chain-adapters/src/evm/katana/KatanaChainAdapter.ts:44-46
Timestamp: 2026-01-05T23:24:54.841Z
Learning: In packages/chain-adapters/src/evm/**/[chain]ChainAdapter.ts files, second-class EVM chain adapters (those extending SecondClassEvmAdapter, such as Monad, Plasma, HyperEVM, and Katana) use hardcoded strings in their getName() method (e.g., `return 'Monad'`), NOT the enum reverse-lookup pattern. The enum reverse-lookup pattern (`Object.values(ChainAdapterDisplayName).indexOf(...)`) only applies to first-class chain adapters.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-11-24T21:20:17.804Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-11-24T21:20:17.804Z
Learning: Applies to **/*.{ts,tsx} : Use `Result<T, E>` pattern for error handling in swappers and APIs; ALWAYS use `Ok()` and `Err()` from `sniptt/monads`; AVOID throwing within swapper API implementations
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-09-12T09:58:50.671Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/EIP155SignTypedDataConfirmation.tsx:14-15
Timestamp: 2025-09-12T09:58:50.671Z
Learning: In EIP155SignTypedDataConfirmation.tsx, gomesalexandre is comfortable with null returns for missing message data as TypeScript type safety guards, viewing them as compile-time protections rather than runtime scenarios that need explicit error handling and user feedback. He considers these TypeScript-level safeguards sufficient and prefers not to add defensive runtime error handling for what should be type-guaranteed scenarios.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-08-29T07:07:49.332Z
Learnt from: premiumjibles
Repo: shapeshift/web PR: 10386
File: src/components/MultiHopTrade/components/VerifyAddresses/VerifyAddresses.tsx:272-294
Timestamp: 2025-08-29T07:07:49.332Z
Learning: In UTXO sell address verification flow in VerifyAddresses.tsx, the user wants address verification to be marked as "verified/complete" before starting the change address fetch, not after. The verification step and change address fetch should be treated as separate sequential operations in the UI flow.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-09-12T11:56:19.437Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:0-0
Timestamp: 2025-09-12T11:56:19.437Z
Learning: gomesalexandre rejected verbose try/catch error handling for address validation in Tenderly integration (PR `#10461`), calling the approach "ugly" but still implemented safety measures in commit ad7e424b89, preferring cleaner safety implementations over defensive programming patterns.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-12-12T16:20:33.904Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11377
File: src/lib/referral/api.ts:30-57
Timestamp: 2025-12-12T16:20:33.904Z
Learning: In shapeshift/web referral feature, the backend expects an EVM address (0x… viem Address) as the owner identifier, not a CAIP AccountId. Update useReferral to derive the first connected EVM address via fromAccountId + getAddress, and do not URL-encode it in the API path.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-08-17T21:53:03.806Z
Learnt from: 0xApotheosis
Repo: shapeshift/web PR: 10290
File: scripts/generateAssetData/color-map.json:41-47
Timestamp: 2025-08-17T21:53:03.806Z
Learning: In the ShapeShift web codebase, native assets (using CAIP-19 slip44 namespace like eip155:1/slip44:60, bip122:.../slip44:..., cosmos:.../slip44:...) are manually hardcoded and not generated via the automated asset generation script. Only ERC20/BEP20 tokens go through the asset generation process. The validation scripts should only validate generated assets, not manually added native assets.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-11-19T22:20:25.661Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10767
File: package.json:324-324
Timestamp: 2025-11-19T22:20:25.661Z
Learning: In shapeshift/web package.json, the resolution "gridplus-sdk/bs58check": "2.1.2" is intentional and must not be removed. It forces gridplus-sdk's transitive bs58check dependency from 4.0.0 down to 2.1.2 because bs58check 4.0.0 breaks legacy address validation (due to bs58 v6.0.0 and noble/hash vs 2.1.2's bs58 v4.0.0 and create-hash).
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-09-10T15:34:54.593Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/components/modals/EIP712MessageDisplay.tsx:46-59
Timestamp: 2025-09-10T15:34:54.593Z
Learning: After extensive testing by gomesalexandre in PR `#10458`, dApps do not send EIP-712 domain.chainId as hex or bigint values in practice. The simple String(domain.chainId) conversion is sufficient for real-world usage in WalletConnect dApps structured signing.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-11-20T12:00:45.005Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11078
File: src/setupVitest.ts:11-15
Timestamp: 2025-11-20T12:00:45.005Z
Learning: In shapeshift/web, src/setupVitest.ts must redirect 'ethers' to 'ethers5' for shapeshiftoss/hdwallet-trezor (and -trezor-connect), same as ledger and shapeshift-multichain. Removing 'trezor' from the regex causes CI/Vitest failures due to ethers v6 vs v5 API differences.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-12-03T23:21:16.985Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11261
File: src/lib/utils/tron/getAllowance.ts:14-59
Timestamp: 2025-12-03T23:21:16.985Z
Learning: In src/lib/utils/tron/getAllowance.ts, gomesalexandre is not concerned about adding comprehensive error handling (try-catch blocks, custom error classes) for the getTrc20Allowance utility function, because it is used close to the view layer. He prefers simpler error handling for view-layer utilities, letting errors propagate naturally rather than adding defensive guards.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-09-12T13:44:17.019Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/hooks/useSimulateEvmTransaction.ts:0-0
Timestamp: 2025-09-12T13:44:17.019Z
Learning: gomesalexandre prefers letting chain adapter errors throw naturally in useSimulateEvmTransaction rather than adding explicit error handling for missing adapters, consistent with his fail-fast approach and dismissal of defensive validation as "stale" in WalletConnect transaction simulation flows.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-10-01T07:42:40.195Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10596
File: src/components/Layout/Header/NavBar/WalletConnectedMenu.tsx:77-99
Timestamp: 2025-10-01T07:42:40.195Z
Learning: In WalletConnectedMenu.tsx's handleReconnectWallet handler, gomesalexandre prefers throwing an error for unsupported wallet types in the default case rather than gracefully handling with a fallback. His reasoning: "if we have a problem here, we have bigger problems" - only supported wallets (KeepKey, Ledger, MetaMask, Coinbase, Phantom) should reach the reconnect flow when disconnected/locked, so encountering an unsupported type indicates a larger architectural issue that should be surfaced explicitly rather than masked with graceful degradation.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-12-09T21:06:15.748Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11335
File: packages/swapper/src/swappers/CetusSwapper/endpoints.ts:66-68
Timestamp: 2025-12-09T21:06:15.748Z
Learning: In packages/swapper/src/swappers/CetusSwapper/endpoints.ts, gomesalexandre is comfortable with throwing errors directly in getUnsignedSuiTransaction and similar transaction preparation methods, rather than using the Result pattern. The Result pattern with makeSwapErrorRight/TradeQuoteError is primarily for the main swapper API methods (getTradeQuote, getTradeRate), while helper/preparation methods can use throws.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-11-24T21:20:17.804Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-11-24T21:20:17.804Z
Learning: Applies to **/swapper{s,}/**/*.{ts,tsx} : ALWAYS use `makeSwapErrorRight` for swapper errors with `TradeQuoteError` enum for error codes and provide detailed error information
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.tspackages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-12-03T23:16:28.342Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11261
File: src/components/MultiHopTrade/components/TradeConfirm/hooks/useAllowanceApproval.tsx:117-172
Timestamp: 2025-12-03T23:16:28.342Z
Learning: In TRON transaction confirmation polling (e.g., approval flows in useAllowanceApproval.tsx), gomesalexandre is comfortable with optimistic completion when polling times out after the configured duration (e.g., 60 seconds). He considers the timeout a "paranoia" safety net for unlikely scenarios, expecting normal transactions to complete much faster. He prefers to defer more sophisticated timeout/failure handling as a separate follow-up concern rather than expanding PR scope.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-09-12T10:44:46.723Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/content/SendTransactionContent.tsx:0-0
Timestamp: 2025-09-12T10:44:46.723Z
Learning: gomesalexandre dismissed a clipboard error handling suggestion in PR `#10461` for SendTransactionContent.tsx, demonstrating that the current navigator.clipboard.writeText implementation works as expected and preferring to keep it simple without additional try/catch error handling.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-08-26T19:04:38.672Z
Learnt from: kaladinlight
Repo: shapeshift/web PR: 10369
File: packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts:167-176
Timestamp: 2025-08-26T19:04:38.672Z
Learning: In packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts, when processing assets from data.assets.reduce(), the team prefers using empty catch blocks to gracefully skip any assets that fail processing, rather than specific error type handling, to avoid useless noise and ensure robust asset filtering.
Applied to files:
packages/chain-adapters/src/ton/TonChainAdapter.ts
📚 Learning: 2025-11-03T22:31:30.786Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10985
File: packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeQuote/getPortalsTradeQuote.ts:0-0
Timestamp: 2025-11-03T22:31:30.786Z
Learning: In packages/swapper/src/swappers/PortalsSwapper, the rate and quote files intentionally use different approaches for calculating buyAmountBeforeSlippageCryptoBaseUnit: getPortalsTradeRate.tsx uses minOutputAmount / (1 - buffer) for conservative estimates, while getPortalsTradeQuote.ts uses outputAmount / (1 - buffer) for final quote display. This difference is validated by on-chain simulation testing and is intentional.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-08-11T09:46:41.060Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10219
File: src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx:167-172
Timestamp: 2025-08-11T09:46:41.060Z
Learning: In the shapeshift/web repository, the display cache logic for trade quotes (using `selectUserAvailableTradeQuotes` and `selectUserUnavailableTradeQuotes`) is intentionally kept the same between `TradeInput.tsx` and `TradeQuotes.tsx` components. The `hasQuotes` computation in `TradeInput.tsx` uses these display cache selectors by design, matching the pattern used in `TradeQuotes.tsx`.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-12-04T11:05:01.146Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11281
File: packages/swapper/src/swappers/PortalsSwapper/utils/fetchSquidStatus.ts:98-106
Timestamp: 2025-12-04T11:05:01.146Z
Learning: In packages/swapper/src/swappers/PortalsSwapper/utils/fetchSquidStatus.ts, getSquidTrackingLink should return blockchain explorer links (using Asset.explorerTxLink) rather than API endpoints. For non-GMP Squid swaps: return source chain explorer link with sourceTxHash when pending/failed, and destination chain explorer link with destinationTxHash when confirmed.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterAssetIdsBySellable method to filter assets by supported chain IDs in the sell property
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Reuse executeEvmTransaction utility for EVM-based swappers instead of implementing custom transaction execution
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-12-22T23:36:06.927Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11513
File: src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx:293-308
Timestamp: 2025-12-22T23:36:06.927Z
Learning: In the swapper types (TradeQuote/TradeRate), the `steps` property is defined as a tuple type that guarantees at least one element: `steps: [TradeQuoteStep] | [TradeQuoteStep, TradeQuoteStep] | [TradeRateStep] | [TradeRateStep, TradeRateStep]`. Therefore, runtime guards checking for empty arrays when accessing steps elements are unnecessary - TypeScript's type system already prevents this scenario.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-11-12T12:49:17.895Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11016
File: packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts:109-125
Timestamp: 2025-11-12T12:49:17.895Z
Learning: In packages/chain-adapters/src/evm/utils.ts, the getErc20Data function already includes a guard that returns an empty string when contractAddress is undefined (line 8: `if (!contractAddress) return ''`). This built-in handling means callers don't need to conditionally invoke getErc20Data—it safely handles both ERC20 tokens and native assets.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-10-15T15:57:39.956Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10810
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:212-0
Timestamp: 2025-10-15T15:57:39.956Z
Learning: gomesalexandre uses discriminated union patterns (e.g., `isEIP1559 ? { max_fee_per_gas, max_priority_fee_per_gas } : { gas_price }`) in WalletConnect flows without additional validation guards, trusting that the runtime data structure ensures mutual exclusivity between EIP-1559 and legacy gas pricing fields.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-09-12T12:04:59.556Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/content/SendTransactionContent.tsx:0-0
Timestamp: 2025-09-12T12:04:59.556Z
Learning: The ShapeShift codebase's fromBaseUnit function correctly handles hex strings (like WalletConnect transaction.value) without manual conversion because bnOrZero -> bn -> new BigNumber() automatically detects and parses hex strings starting with "0x". gomesalexandre confirmed this with concrete evidence showing hex value 0x176d1c49189db correctly converts to 0.000412118294825435 ETH.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-11-24T21:21:12.774Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/typescript-best-practices.mdc:0-0
Timestamp: 2025-11-24T21:21:12.774Z
Learning: Applies to **/*.{ts,tsx} : ALWAYS use explicit types for function parameters and return values in TypeScript
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-08-13T15:52:25.116Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10272
File: src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts:61-0
Timestamp: 2025-08-13T15:52:25.116Z
Learning: In the ShapeShift web codebase, specifically in src/context/WalletProvider/MobileWallet/mobileMessageHandlers.ts, message variants in the Message union type do not include inline comments documenting their expected return types. The codebase follows a pattern of keeping these type definitions clean without such documentation comments.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-08-13T16:43:04.842Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10272
File: src/hooks/useActionCenterSubscribers/useSwapActionSubscriber.tsx:52-55
Timestamp: 2025-08-13T16:43:04.842Z
Learning: In the shapeshift/web project, gomesalexandre and the team conventionally don't require explicit return types for functions - they prefer implicit return types when the type is obvious from context. They are also almost never concerned about explicit void return type annotations, preferring to keep the code concise rather than overly explicit with TypeScript annotations.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-11-24T21:21:12.774Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/typescript-best-practices.mdc:0-0
Timestamp: 2025-11-24T21:21:12.774Z
Learning: Applies to **/*.{ts,tsx} : ALWAYS use explicit types for object shapes using interfaces or type aliases in TypeScript
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-11-24T21:20:44.637Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/react-best-practices.mdc:0-0
Timestamp: 2025-11-24T21:20:44.637Z
Learning: Applies to **/*.tsx : Ensure TypeScript types are explicit and proper; avoid use of `any` type
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-11-24T21:20:04.979Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : Avoid 'any' types - use specific type annotations instead
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-11-03T05:46:24.190Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10985
File: packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.tsx:130-141
Timestamp: 2025-11-03T05:46:24.190Z
Learning: In packages/swapper/src/swappers/PortalsSwapper/getPortalsTradeRate/getPortalsTradeRate.tsx, gomesalexandre prefers "let it crash" approach when Portals returns zero outputAmount or 100% buffer causing division by zero, rather than adding defensive guards to surface zero quotes. He wants to fail fast with exceptions that bubble up to the try/catch wrapper, surfacing upstream data issues rather than masking them with defensive programming.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.tspackages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/**/*.ts : Validate inputs and log errors for debugging in Swapper system implementations
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/**/*.ts : Avoid side effects in swap logic; ensure swap methods are deterministic and stateless
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/endpoints.ts : Reuse checkEvmSwapStatus utility for checking EVM swap status instead of implementing custom status checks
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-08-05T16:39:58.598Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10191
File: src/pages/Explore/Explore.tsx:56-56
Timestamp: 2025-08-05T16:39:58.598Z
Learning: In the ShapeShift web codebase, the established pattern for handling floating point numbers is to use BigNumber operations (bnOrZero, bn) for calculations and convert to strings using .toString() before passing to UI components like Amount.Fiat, Amount.Crypto, and Amount.Percent. This prevents JavaScript floating point precision issues and maintains consistency across the application.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-10-21T17:11:18.087Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10871
File: src/components/Modals/Send/hooks/useSendDetails/useSendDetails.tsx:426-428
Timestamp: 2025-10-21T17:11:18.087Z
Learning: In src/components/Modals/Send/hooks/useSendDetails/useSendDetails.tsx, within the handleInputChange function, use .toFixed() without arguments (not .toString()) when converting BigNumber amounts for input field synchronization. This avoids exponential notation in the input while preserving precision for presentational components like <Amount.Crypto /> and <Amount.Fiat /> to format appropriately.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-08-05T17:00:30.416Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10191
File: src/pages/Explore/components/AssetSearchRow.tsx:78-78
Timestamp: 2025-08-05T17:00:30.416Z
Learning: In BigNumber operations within the ShapeShift codebase, floating point literals like 0.01 should always be passed as strings like '0.01' to maintain precision. This applies to all BigNumber methods including .times(), .div(), .plus(), and .minus().
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-10-21T17:12:47.282Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10871
File: src/components/Modals/Send/hooks/useSendDetails/useSendDetails.tsx:426-428
Timestamp: 2025-10-21T17:12:47.282Z
Learning: In the ShapeShift web codebase, when converting bignumber.js BigNumber values, prefer using .toFixed() over .toString() unless there's a specific reason to use .toString(). The .toFixed() method avoids exponential notation while preserving precision, which is especially important for input field synchronization and display purposes.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-09-26T17:45:47.676Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10592
File: src/state/slices/tradeRampInputSlice/selectors.ts:53-63
Timestamp: 2025-09-26T17:45:47.676Z
Learning: gomesalexandre considers additional rate validation guards (like checking rate > 0) unnecessary when the mathematical operation already handles edge cases correctly (e.g., multiplying by 0 yields 0). He prefers to avoid defensive programming when domain knowledge indicates certain edge cases (like negative rates) are not valid concerns, consistent with his TypeScript-first approach over runtime validations.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
📚 Learning: 2025-09-12T12:04:59.556Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/content/SendTransactionContent.tsx:0-0
Timestamp: 2025-09-12T12:04:59.556Z
Learning: gomesalexandre confirmed that fromBaseUnit in the ShapeShift codebase correctly handles hex strings (like transaction.value from WalletConnect) without requiring manual hex-to-decimal conversion, as bnOrZero handles this automatically via BigNumber.js.
Applied to files:
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
🧬 Code graph analysis (3)
src/lib/utils/ton.ts (3)
packages/utils/src/assetData/baseAssets.ts (1)
ton(472-486)packages/chain-adapters/src/ton/TonChainAdapter.ts (1)
ChainAdapter(50-848)packages/caip/src/constants.ts (1)
tonChainId(95-95)
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts (6)
packages/swapper/src/swappers/StonfiSwapper/types.ts (2)
OmnistonAssetAddress(11-14)StonfiTradeSpecific(46-64)packages/swapper/src/types.ts (3)
CommonTradeQuoteInput(197-202)TradeQuoteResult(772-772)TradeQuote(601-606)packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts (4)
validateTonAssets(36-81)slippageDecimalToBps(100-114)waitForQuote(116-146)calculateRate(83-99)packages/swapper/src/swappers/StonfiSwapper/utils/omnistonManager.ts (1)
omnistonManager(7-21)packages/swapper/src/swappers/StonfiSwapper/utils/constants.ts (2)
STONFI_DEFAULT_SLIPPAGE_BPS(7-7)STONFI_QUOTE_TIMEOUT_MS(9-9)packages/swapper/src/utils.ts (1)
makeSwapErrorRight(42-58)
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts (4)
packages/swapper/src/swappers/StonfiSwapper/types.ts (3)
OmnistonAssetAddress(11-14)TonAssetValidationResult(16-25)QuoteResult(27-31)packages/caip/src/assetId/assetId.ts (1)
fromAssetId(140-172)packages/swapper/src/utils.ts (1)
makeSwapErrorRight(42-58)src/state/apis/swapper/types.ts (1)
TradeQuoteError(39-39)
🔇 Additional comments (13)
packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts (5)
13-15: Clear TON chain guard.Simple and correct chain ID check for TON assets.
17-34: Address mapping is clear and deterministic.Native TON vs jetton mapping is handled cleanly.
36-81: Validation and SwapError mapping look solid.The error cases are explicit and return consistent
SwapErrorRightpayloads.
83-99: Rate calculation is precision-safe.Good use of precision conversion + BigNumber division to avoid float errors.
100-114: Input sanitization is solid.The NaN/negative guards keep slippage behavior predictable.
packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts (1)
18-40: Stonfi-specific payload mapping looks correct.Fields are mapped consistently and fallback to request asset addresses.
src/lib/utils/ton.ts (1)
1-39: LGTM overall - clean utility module for TON adapter support.The utility functions follow established patterns in the codebase. The fallback to
TxStatus.Pendingon error ingetTonTransactionStatusis appropriate for transaction status queries where temporary failures shouldn't be treated as definitive states.packages/chain-adapters/src/ton/TonChainAdapter.ts (6)
468-502: LGTM - Transaction building handles both native and jetton transfers correctly.The implementation properly distinguishes between native TON transfers and jetton transfers via the
contractAddresscheck, includes appropriate expiration handling, and correctly encodes the message data.
585-647: LGTM - Fee estimation with sensible fallbacks.The implementation correctly handles both native and jetton transfers with appropriate fee buffers. The fallback values provide resilience when RPC config fetch fails, and the fast/slow multipliers (150%/80%) give users reasonable options.
378-437: LGTM - Address validation now includes proper CRC16 checksum verification.The CRC16-CCITT implementation follows the TON spec correctly with polynomial 0x1021, and the checksum validation for user-friendly addresses prevents accepting addresses with corrupted checksums.
694-847: LGTM - Transaction parsing handles TON message structure correctly.The implementation properly:
- Fetches message data and resolves to transaction hash
- Determines transaction status from abort/action flags
- Handles both incoming and outgoing messages for transfer direction
- Uses normalized address comparison for pubkey matching
50-70: LGTM - Conservative rate limiting for Toncenter free tier.The PQueue configuration (1 request per 2 seconds) appropriately respects the Toncenter rate limits while still allowing operations to proceed.
81-144: LGTM - Robust retry logic with proper error categorization.The rpcRequest method correctly:
- Handles HTTP 429/5xx with exponential backoff
- Distinguishes retryable from non-retryable errors
- Validates that successful responses contain result data
- Re-throws TON RPC errors immediately to avoid unnecessary retries
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| async broadcastTransaction(input: BroadcastTransactionInput): Promise<string> { | ||
| try { | ||
| const { hex: signedTx } = input | ||
|
|
||
| const result = await this.rpcRequest<{ hash: string }>('sendBocReturnHash', { | ||
| boc: signedTx, | ||
| }) | ||
|
|
||
| return result.hash ?? '' | ||
| } catch (err) { | ||
| return ErrorHandler(err, { | ||
| translation: 'chainAdapters.errors.broadcastTransaction', | ||
| }) | ||
| } | ||
| } |
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.
Returning empty string on missing hash may cause downstream issues.
If result.hash is undefined, returning an empty string could lead to silent failures when callers use this hash for transaction tracking or status checks.
🛠️ Suggested fix
async broadcastTransaction(input: BroadcastTransactionInput): Promise<string> {
try {
const { hex: signedTx } = input
const result = await this.rpcRequest<{ hash: string }>('sendBocReturnHash', {
boc: signedTx,
})
- return result.hash ?? ''
+ if (!result.hash) {
+ throw new Error('TON RPC: broadcast succeeded but no transaction hash returned')
+ }
+ return result.hash
} catch (err) {
return ErrorHandler(err, {
translation: 'chainAdapters.errors.broadcastTransaction',
})
}
}🤖 Prompt for AI Agents
In `@packages/chain-adapters/src/ton/TonChainAdapter.ts` around lines 569 - 583,
The method broadcastTransaction currently returns an empty string when
result.hash is missing; change it to treat a missing hash as an error by
checking if result.hash is falsy after the rpcRequest and then throw
ErrorHandler(new Error('Missing transaction hash'), { translation:
'chainAdapters.errors.broadcastTransaction', details: result }) instead of
returning ''; update the logic in broadcastTransaction (which calls rpcRequest
and uses ErrorHandler) so callers always get a thrown error on missing hash
rather than an empty string.
| 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 | ||
| } | ||
|
|
||
| const { bidAssetAddress, askAssetAddress } = validation | ||
| const omniston = omnistonManager.getInstance() | ||
|
|
||
| try { | ||
| const slippageBps = slippageDecimalToBps( | ||
| slippageTolerancePercentageDecimal, | ||
| STONFI_DEFAULT_SLIPPAGE_BPS, | ||
| ) | ||
|
|
||
| const quoteResult = await waitForQuote( | ||
| omniston, | ||
| { | ||
| settlementMethods: [SettlementMethod.SETTLEMENT_METHOD_SWAP], | ||
| bidAssetAddress, | ||
| askAssetAddress, | ||
| amount: { bidUnits: sellAmountIncludingProtocolFeesCryptoBaseUnit }, | ||
| settlementParams: { | ||
| maxPriceSlippageBps: slippageBps, | ||
| gaslessSettlement: 'GASLESS_SETTLEMENT_PROHIBITED', | ||
| }, | ||
| }, | ||
| STONFI_QUOTE_TIMEOUT_MS, | ||
| ) | ||
|
|
||
| if (quoteResult.type === 'error') { | ||
| console.error('[Stonfi] Quote request error:', quoteResult.error) | ||
| return Err( | ||
| makeSwapErrorRight({ | ||
| message: `[Stonfi] Connection error while fetching quote`, | ||
| code: TradeQuoteError.QueryFailed, | ||
| cause: quoteResult.error, | ||
| }), | ||
| ) | ||
| } | ||
|
|
||
| if (quoteResult.type === 'timeout') { | ||
| return Err( | ||
| makeSwapErrorRight({ | ||
| message: `[Stonfi] Quote request timed out`, | ||
| code: TradeQuoteError.QueryFailed, | ||
| }), | ||
| ) | ||
| } | ||
|
|
||
| if (quoteResult.type === 'noQuote') { | ||
| return Err( | ||
| makeSwapErrorRight({ | ||
| message: `[Stonfi] No quote available for this pair`, | ||
| code: TradeQuoteError.NoRouteFound, | ||
| }), | ||
| ) | ||
| } | ||
|
|
||
| const quote = quoteResult.quote | ||
| const buyAmountCryptoBaseUnit = quote.askUnits | ||
| const networkFeeCryptoBaseUnit = quote.gasBudget | ||
|
|
||
| const rate = calculateRate( | ||
| buyAmountCryptoBaseUnit, | ||
| sellAmountIncludingProtocolFeesCryptoBaseUnit, | ||
| buyAsset.precision, | ||
| sellAsset.precision, | ||
| ) | ||
|
|
||
| const tradeQuote: TradeQuote = { | ||
| id: quote.quoteId, | ||
| rate, | ||
| receiveAddress, | ||
| affiliateBps: '0', | ||
| slippageTolerancePercentageDecimal: | ||
| slippageTolerancePercentageDecimal ?? String(STONFI_DEFAULT_SLIPPAGE_BPS / 10000), | ||
| quoteOrRate: 'quote', | ||
| swapperName: SwapperName.Stonfi, | ||
| steps: [ | ||
| { | ||
| buyAmountBeforeFeesCryptoBaseUnit: buyAmountCryptoBaseUnit, | ||
| buyAmountAfterFeesCryptoBaseUnit: buyAmountCryptoBaseUnit, | ||
| sellAmountIncludingProtocolFeesCryptoBaseUnit, | ||
| feeData: { | ||
| networkFeeCryptoBaseUnit, | ||
| protocolFees: undefined, | ||
| }, | ||
| rate, | ||
| source: SwapperName.Stonfi, | ||
| buyAsset, | ||
| sellAsset, | ||
| accountNumber, | ||
| allowanceContract: '0x0', | ||
| estimatedExecutionTimeMs: 30000, | ||
| stonfiSpecific: buildStonfiSpecific(quote, bidAssetAddress, askAssetAddress), | ||
| }, | ||
| ], | ||
| } | ||
|
|
||
| return Ok([tradeQuote]) | ||
| } catch (err) { | ||
| return Err( | ||
| makeSwapErrorRight({ | ||
| message: `[Stonfi] Error getting quote: ${err}`, | ||
| code: TradeQuoteError.QueryFailed, | ||
| cause: err, | ||
| }), | ||
| ) | ||
| } | ||
| } |
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.
Keep stored slippage decimal consistent with the BPS actually used.
When slippageTolerancePercentageDecimal is invalid, slippageDecimalToBps falls back to the default, but the quote still stores the original (invalid) string. This can surface incorrect slippage in UI/analytics.
🛠️ Proposed fix
const slippageBps = slippageDecimalToBps(
slippageTolerancePercentageDecimal,
STONFI_DEFAULT_SLIPPAGE_BPS,
)
+ const slippageTolerancePercentageDecimalNormalized = String(slippageBps / 10000)
@@
const tradeQuote: TradeQuote = {
id: quote.quoteId,
rate,
receiveAddress,
affiliateBps: '0',
- slippageTolerancePercentageDecimal:
- slippageTolerancePercentageDecimal ?? String(STONFI_DEFAULT_SLIPPAGE_BPS / 10000),
+ slippageTolerancePercentageDecimal: slippageTolerancePercentageDecimalNormalized,
quoteOrRate: 'quote',
swapperName: SwapperName.Stonfi,🤖 Prompt for AI Agents
In `@packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts`
around lines 42 - 161, The stored slippageDecimal in the returned TradeQuote can
differ from the actual BPS used because getTradeQuote calls slippageDecimalToBps
(producing slippageBps) but continues to store the original
slippageTolerancePercentageDecimal; update getTradeQuote to derive a canonical
decimal string from slippageBps (e.g., slippageBps / 10000 as a string, using
STONFI_DEFAULT_SLIPPAGE_BPS when the input was invalid) and assign that
canonical value to tradeQuote.slippageTolerancePercentageDecimal so the returned
quote always reflects the BPS actually used.
| export const waitForQuote = ( | ||
| omniston: Omniston, | ||
| request: QuoteRequest, | ||
| timeoutMs: number, | ||
| ): Promise<QuoteResult> => { | ||
| return new Promise(resolve => { | ||
| const timer = setTimeout(() => { | ||
| subscription.unsubscribe() | ||
| resolve({ type: 'timeout' }) | ||
| }, timeoutMs) | ||
|
|
||
| const subscription = omniston.requestForQuote(request).subscribe({ | ||
| next: (event: QuoteResponseEvent) => { | ||
| if (event.type === 'quoteUpdated' && event.quote) { | ||
| clearTimeout(timer) | ||
| subscription.unsubscribe() | ||
| resolve({ type: 'success', quote: event.quote }) | ||
| } else if (event.type === 'noQuote') { | ||
| clearTimeout(timer) | ||
| subscription.unsubscribe() | ||
| resolve({ type: 'noQuote' }) | ||
| } | ||
| }, | ||
| error: err => { | ||
| clearTimeout(timer) | ||
| subscription.unsubscribe() | ||
| resolve({ type: 'error', error: err }) | ||
| }, | ||
| }) | ||
| }) |
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:
# First, let's check the actual file content and verify it matches the review comment
cat -n packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts | sed -n '100,150p'Repository: shapeshift/web
Length of output: 1731
🏁 Script executed:
# Let's also check what omniston is and how it's imported
head -50 packages/swapper/src/swappers/StonfiSwapper/utils/helpers.tsRepository: shapeshift/web
Length of output: 1708
🏁 Script executed:
# Search for omniston imports and type definitions
rg "omniston|Omniston" packages/swapper/src/swappers/StonfiSwapper/ -A 2 -B 2Repository: shapeshift/web
Length of output: 19187
🏁 Script executed:
# Check if there's a package.json reference or node_modules reference to omniston
rg "omniston" . --type json -lRepository: shapeshift/web
Length of output: 89
🏁 Script executed:
# Search for all requestForQuote usages
rg "requestForQuote" packages/swapper/src/swappers/StonfiSwapper/ -B 3 -A 3Repository: shapeshift/web
Length of output: 744
🏁 Script executed:
# Check package.json for omniston version
cat packages/swapper/package.json | grep -A 2 -B 2 "omniston"Repository: shapeshift/web
Length of output: 232
🏁 Script executed:
# Look at trackTrade pattern (similar to requestForQuote)
rg "trackTrade.*subscribe" packages/swapper/src/swappers/StonfiSwapper/ -B 5 -A 5Repository: shapeshift/web
Length of output: 1002
🌐 Web query:
omniston-sdk requestForQuote synchronous error throwing
💡 Result:
Short answer: requestForQuote returns an RxJS Observable (so runtime errors are normally delivered as Observable errors), but it can also throw synchronously (e.g., invalid params or internal validation before the Observable is created). Handle both by (1) surrounding the call with try/catch for synchronous throws and (2) handling observable errors with subscribe's error callback or operators like catchError. See examples below.
Why: Omniston's SDK exposes requestForQuote as an observable-based API. Observable executions deliver asynchronous errors via the error notification, but code that runs before the Observable is created/returned can throw immediately. [1][2]
Minimal patterns
-
Catch synchronous throws:
try {
const rfq$ = omniston.requestForQuote(params); // may throw synchronously
rfq$.subscribe({
next: (ev) => { /* handle next / },
error: (err) => { / handle async observable error */ },
});
} catch (syncErr) {
// handle synchronous validation/constructor errors
} -
Handle observable errors with catchError:
import { catchError, of } from 'rxjs';
omniston
.requestForQuote(params)
.pipe(
catchError(err => {
// transform or recover; must return an Observable
return of({ type: 'rfq_error', error: err });
})
)
.subscribe(result => { /* ... */ });
References
- Omniston SDK docs (requestForQuote is RxJS/Observable-based). [1]
- RxJS Observable anatomy and catchError operator (how errors are delivered and handled). [2][3]
Sources
[1] Omniston SDK docs — Node.js / requestForQuote usage. [turn0search3]
[2] RxJS documentation — Observable anatomy / execution. [turn1search1]
[3] Learn RxJS — catchError operator. [turn1search5]
Guard against synchronous omniston.requestForQuote() errors.
The SDK can throw synchronously during validation or parameter processing before returning an Observable. If this happens, subscription remains undefined and the timeout handler will throw when calling .unsubscribe(), leaking the timer and promise.
Proposed fix
export const waitForQuote = (
omniston: Omniston,
request: QuoteRequest,
timeoutMs: number,
): Promise<QuoteResult> => {
return new Promise(resolve => {
+ let subscription: { unsubscribe: () => void } | undefined
const timer = setTimeout(() => {
- subscription.unsubscribe()
+ subscription?.unsubscribe()
resolve({ type: 'timeout' })
}, timeoutMs)
- const subscription = omniston.requestForQuote(request).subscribe({
- next: (event: QuoteResponseEvent) => {
- if (event.type === 'quoteUpdated' && event.quote) {
- clearTimeout(timer)
- subscription.unsubscribe()
- resolve({ type: 'success', quote: event.quote })
- } else if (event.type === 'noQuote') {
- clearTimeout(timer)
- subscription.unsubscribe()
- resolve({ type: 'noQuote' })
- }
- },
- error: err => {
- clearTimeout(timer)
- subscription.unsubscribe()
- resolve({ type: 'error', error: err })
- },
- })
+ try {
+ subscription = omniston.requestForQuote(request).subscribe({
+ next: (event: QuoteResponseEvent) => {
+ if (event.type === 'quoteUpdated' && event.quote) {
+ clearTimeout(timer)
+ subscription?.unsubscribe()
+ resolve({ type: 'success', quote: event.quote })
+ } else if (event.type === 'noQuote') {
+ clearTimeout(timer)
+ subscription?.unsubscribe()
+ resolve({ type: 'noQuote' })
+ }
+ },
+ error: err => {
+ clearTimeout(timer)
+ subscription?.unsubscribe()
+ resolve({ type: 'error', error: err })
+ },
+ })
+ } catch (err) {
+ clearTimeout(timer)
+ subscription?.unsubscribe()
+ resolve({ type: 'error', error: err })
+ }
})
}Note: The same vulnerability exists in endpoints.ts with omniston.trackTrade().
🤖 Prompt for AI Agents
In `@packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts` around lines
116 - 145, The code in waitForQuote can throw synchronously from
omniston.requestForQuote(), leaving subscription undefined and causing the
timeout handler to throw when calling subscription.unsubscribe(); fix by
declaring subscription variable before calling omniston.requestForQuote(), wrap
the call in try/catch, and in the catch clear the timer and resolve with an
error (or appropriate result), and ensure subscription is always a safe object
(or checked) before calling unsubscribe; apply the same pattern to the
omniston.trackTrade() usage in endpoints.ts to guard against synchronous throws.
Summary
Web fren of shapeshift/hdwallet#782
Adds TON (The Open Network) blockchain as a second-class citizen in ShapeShift Web.
closes #11646
Missing:
Changes
Chain Adapter (
packages/chain-adapters/src/ton/)TonChainAdapter.ts- Full chain adapter implementationtypes.ts- TON-specific types (Account, BuildTxInput, FeeData, TonSignTx)CAIP Constants (
packages/caip/src/constants.ts)tonAssetId,tonChainIdCHAIN_NAMESPACE.Ton,CHAIN_REFERENCE.TonMainnetASSET_REFERENCE.TonCoinGecko Integration (
packages/caip/src/adapters/coingecko/)Ton = 'the-open-network'toCoingeckoAssetPlatformenumchainIdToCoingeckoAssetPlatformandcoingeckoAssetPlatformToChainIdPlugin (
src/plugins/ton/)Tonfeature flagVITE_TON_NODE_URLConfig & Feature Flags
VITE_TON_NODE_URLenvironment variableVITE_FEATURE_TONfeature flagTontoFeatureFlagstype and initial stateAsset Generation (
scripts/generateAssetData/ton/)Utility Functions
chainIdToFeeAssetId.ts- Added TON casegetChainShortName.ts- Added TON case (returns 'TON')getBaseAsset.ts- Added TON casebaseAssets.ts- Added TON base asset definitionTON Chain Specifications
m/44'/607'/<account>'(3-level hardened)the-open-networkDependencies
Requires hdwallet TON support: https://github.com/shapeshift/hdwallet/pull/XXX
Environment Variables
Testing
https://jam.dev/c/6255e8a6-da4d-446f-a897-507ecfd2cfb8
https://jam.dev/c/35df1985-0e6e-4a69-8fd4-12c41a562cb9
Near intents might be dead right now, to test swapping to TON chain
yarn build:packagesyarn type-checkyarn generate:asset-dataSummary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.