Skip to content

Conversation

@NeOMakinG
Copy link
Collaborator

@NeOMakinG NeOMakinG commented Jan 12, 2026

Summary

Web fren of shapeshift/hdwallet#782

Adds TON (The Open Network) blockchain as a second-class citizen in ShapeShift Web.

closes #11646

Missing:

  • Proper transaction parsing, to do in a follow up once we've merged hdwallet

Changes

Chain Adapter (packages/chain-adapters/src/ton/)

  • TonChainAdapter.ts - Full chain adapter implementation
    • Address derivation from public key (WalletV4R2 StateInit)
    • Account balance fetching via TON Center API
    • Transaction building with proper BoC encoding
    • Transaction signing via hdwallet
    • Transaction broadcasting
    • Fee estimation
    • Transaction status polling
  • types.ts - TON-specific types (Account, BuildTxInput, FeeData, TonSignTx)

CAIP Constants (packages/caip/src/constants.ts)

  • Added tonAssetId, tonChainId
  • Added CHAIN_NAMESPACE.Ton, CHAIN_REFERENCE.TonMainnet
  • Added ASSET_REFERENCE.Ton

CoinGecko Integration (packages/caip/src/adapters/coingecko/)

  • Added Ton = 'the-open-network' to CoingeckoAssetPlatform enum
  • Added TON mappings in chainIdToCoingeckoAssetPlatform and coingeckoAssetPlatformToChainId

Plugin (src/plugins/ton/)

  • TON plugin with Ton feature flag
  • Chain adapter registration with VITE_TON_NODE_URL

Config & Feature Flags

  • Added VITE_TON_NODE_URL environment variable
  • Added VITE_FEATURE_TON feature flag
  • Added Ton to FeatureFlags type and initial state

Asset Generation (scripts/generateAssetData/ton/)

  • TON asset generation script using CoinGecko

Utility Functions

  • chainIdToFeeAssetId.ts - Added TON case
  • getChainShortName.ts - Added TON case (returns 'TON')
  • getBaseAsset.ts - Added TON case
  • baseAssets.ts - Added TON base asset definition

TON Chain Specifications

Property Value
SLIP44 Coin Type 607
Derivation Path m/44'/607'/<account>' (3-level hardened)
Signing Algorithm Ed25519
Address Format User-friendly base64 (48 chars, WalletV4R2)
Decimals 9
CoinGecko Platform the-open-network

Dependencies

Requires hdwallet TON support: https://github.com/shapeshift/hdwallet/pull/XXX

Environment Variables

VITE_TON_NODE_URL=https://toncenter.com/api/v2/jsonRPC
VITE_FEATURE_TON=true

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

  • Build packages: yarn build:packages
  • Type check: yarn type-check
  • Generate assets: yarn generate:asset-data
  • Manual testing with native wallet (send both jettons and native token, swap from any chains to ton, swap from ton to another chain, swap ton jettons, address derivations, fee estimations, tokens, market data.... a whole new chain test!)

Summary by CodeRabbit

  • New Features

    • Full TON (The Open Network) support: assets, tokens, wallet integration, fee estimation, address handling, and transaction signing/broadcasting.
    • Integrated STON.fi swapper and DEX flow for TON swaps, including quote/rate, unsigned-TON transaction construction, and trade status tracking.
  • Improvements

    • Multi-chain UI/workflow updates to surface TON in swaps, fees, plugins, and feature flags.

✏️ Tip: You can customize this high-level summary in your review settings.

- 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
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Environment & Config
\.env, \.env.development, src/config.ts, src/state/slices/preferencesSlice/preferencesSlice.ts, src/test/mocks/store.ts
New env vars VITE_TON_NODE_URL, VITE_FEATURE_TON, VITE_FEATURE_STONFI_SWAP; dev enables TON/Stonfi; feature flags added and surfaced in config and mock store.
CSP / Headers
headers/csps/chains/ton.ts, headers/csps/defi/swappers/Stonfi.ts, headers/csps/index.ts
New CSP entries for TON RPC and STON.fi websockets; registered in CSP index.
Chain Adapter: TON
packages/chain-adapters/src/ton/*, packages/chain-adapters/src/index.ts, packages/chain-adapters/src/types.ts, packages/chain-adapters/package.json
New TON chain-adapter implementation, types, and public export; added @ton/* and ethers deps; hdwallet resolution bumps.
CAIP & Constants
packages/caip/src/constants.ts, packages/caip/src/adapters/coingecko/*, packages/types/src/base.ts, src/constants/chains.ts
Added TON chain/asset IDs (tonChainId/tonAssetId), jetton namespace, CoinGecko platform mapping and token parsing, and chain listing updates.
Swapper - STON.fi Integration
packages/swapper/src/swappers/StonfiSwapper/*, packages/swapper/src/constants.ts, packages/swapper/src/types.ts, packages/swapper/package.json
New Stonfi swapper: helpers, omniston manager, quote/rate endpoints, transaction building, status tracking, types, and registration; default slippage constant; new @ston-fi/omniston-sdk dep.
Swapper - Near Intents & Core Extensions
packages/swapper/src/swappers/NearIntentsSwapper/*, packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts, packages/swapper/src/utils.ts, packages/swapper/src/types.ts, packages/swapper/src/swappers/ButterSwap/*
Added TON support and retry logic to NearIntents swapper, extended swapper types for TON, added executeTonTransaction helper, and test mocks updated to include Ton adapter.
Assets & Asset Generation
packages/utils/src/assetData/*, scripts/generateAssetData/*, packages/caip/src/adapters/coingecko/*
Added Toncoin base asset, asset generation pipeline for TON (CoinGecko), TrustWallet mapping, and generation inclusion.
UI / Trade Execution / Components
src/components/*/useTradeExecution.tsx, src/components/Modals/Send/utils.ts, src/components/MultiHopTrade/*, src/pages/RFOX/*
Integrated TON into send modal, multi-hop trade execution and fee estimation, trade metadata (stonfiSpecific), and RFOX bridge deps.
Accounting & Utilities
src/lib/account/ton.ts, src/lib/account/account.ts, src/lib/utils/ton.ts, src/lib/tradeExecution.ts, src/hooks/*
Added account derivation for TON, wallet support detection, adapter assertion, tx status querying, trade-execution path for TON, and action-center polling.
State / Plugins / Context
src/state/*, src/plugins/activePlugins.ts, src/plugins/ton/index.tsx, src/context/PluginProvider/PluginProvider.tsx
Added TON plugin registration, injected assertGetTonChainAdapter into swapper deps, feature flags gating, state slice updates, small migration added.
Misc Manifest / Version Bumps
various package.json files (package.json, packages/contracts/package.json, packages/unchained-client/package.json)
Version bumps and hdwallet resolution 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)
Loading
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
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

high risk

Suggested reviewers

  • kaladinlight
  • gomesalexandre

Poem

🐰 I hopped through code and found a TON,
New swaps and adapters, and lots of fun.
With Stonfi and memos, websockets that sing,
I stitched in the chains — a delightful spring.
Hooray for hops, new swaps take flight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add TON blockchain support' clearly and concisely summarizes the main objective of the changeset, which is adding comprehensive TON blockchain support throughout the application.
Linked Issues check ✅ Passed The PR successfully implements all primary coding objectives from issue #11646: TON chain adapter with address derivation and fee estimation, CAIP constants for TON, CoinGecko integration, Stonfi swapper integration, plugin registration with feature flags, and asset generation scripts.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing TON blockchain support as specified in the linked issue. Package version updates and hdwallet dependency bumps are necessary for TON support. No unrelated changes detected.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ton-chain

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@twblack88 twblack88 mentioned this pull request Jan 14, 2026
@twblack88
Copy link
Contributor

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)
@NeOMakinG NeOMakinG marked this pull request as ready for review January 15, 2026 20:10
@NeOMakinG NeOMakinG requested a review from a team as a code owner January 15, 2026 20:10
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 chainAdapter is truthy and an object, but then calls getChainId() 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_NANOTON
src/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 from sellAccountId assumes 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 fromAccountId from @shapeshiftoss/caip.

packages/swapper/src/swappers/StonfiSwapper/types.ts (1)

27-31: Consider renaming to avoid shadowing the global QuoteResult type.

This QuoteResult type shadows the global QuoteResult exported from packages/swapper/src/types.ts (which is Result<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.

package.json Outdated
},
"packageManager": "yarn@3.5.0",
"resolutions": {
"@shapeshiftoss/hdwallet-native": "1.62.39",
Copy link
Contributor

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({
Copy link
Contributor

Choose a reason for hiding this comment

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

revert fluff here

Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

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

the fuark

Copy link
Contributor

Choose a reason for hiding this comment

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

ditto revert hunks

Copy link
Contributor

@gomesalexandre gomesalexandre left a 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
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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.

Comment on lines 18 to 40
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,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 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.ts

Repository: 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.ts

Repository: 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 -5

Repository: 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.ts

Repository: 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
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 if getChainId method doesn't exist.

The guard checks typeof chainAdapter !== 'object' but then directly calls (chainAdapter as ton.ChainAdapter).getChainId(). If chainAdapter is an object but not a chain adapter (i.e., lacks getChainId method), 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.

📥 Commits

Reviewing files that changed from the base of the PR and between d6548e2 and 5e4065d.

📒 Files selected for processing (6)
  • packages/chain-adapters/src/ton/TonChainAdapter.ts
  • packages/chain-adapters/src/ton/types.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
  • src/components/Modals/Send/utils.ts
  • src/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 run yarn lint --fix and yarn type-check after making changes
Avoid let variable assignments - prefer const with inline IIFE switch statements or extract to functions for conditional logic

Files:

  • src/lib/utils/ton.ts
  • packages/chain-adapters/src/ton/TonChainAdapter.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/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 in useMemo and callbacks in useCallback where 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 using useColorModeValue hook
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() from react-polyglot
Use useFeatureFlag('FlagName') hook to access feature flag values in components
Prefer type over interface for type definitions
Use strict typing - avoid any
Use Nominal types for domain identifiers (e.g., WalletId, AccountId)
Import types from @shapeshiftoss/caip for chain/account/asset IDs
Use useAppSelector for Redux state
Use useAppDispatch for Redux actions
Memoize expensive computations with useMemo
Memoize callbacks with useCallback

**/*.{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
ALWAYS use custom error classes from @shapeshiftoss/errors with meaningful error codes for internationalization and relevant details in error objects
ALWAYS wrap async op...

Files:

  • src/lib/utils/ton.ts
  • packages/chain-adapters/src/ton/TonChainAdapter.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/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
Use handle prefix for event handlers with descriptive names in camelCase
Use descriptive boolean variable names with is, has, can, should prefixes
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 like data, item, obj, and single-letter variable names except in loops
Avoid abbreviations in names unless they are widely understood
Avoid generic function names like fn, func, or callback

Files:

  • src/lib/utils/ton.ts
  • packages/chain-adapters/src/ton/TonChainAdapter.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/swapper/src/swappers/StonfiSwapper/utils/helpers.ts
**/swapper{s,}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

ALWAYS use makeSwapErrorRight for swapper errors with TradeQuoteError enum for error codes and provide detailed error information

Files:

  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/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.ts
  • packages/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.ts
  • packages/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.ts
  • packages/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.ts
  • packages/chain-adapters/src/ton/TonChainAdapter.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/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.ts
  • packages/chain-adapters/src/ton/TonChainAdapter.ts
  • packages/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.ts
  • packages/chain-adapters/src/ton/TonChainAdapter.ts
  • packages/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.ts
  • packages/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.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/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.ts
  • packages/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.ts
  • packages/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.ts
  • packages/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.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/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.ts
  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/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.ts
  • packages/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.ts
  • 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/*/*.ts : Reuse executeEvmTransaction utility for EVM-based swappers instead of implementing custom transaction execution

Applied to files:

  • packages/swapper/src/swappers/StonfiSwapper/swapperApi/getTradeQuote.ts
  • packages/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.ts
  • packages/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.ts
  • 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/**/*.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 SwapErrorRight payloads.


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.Pending on error in getTonTransactionStatus is 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 contractAddress check, 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.

Comment on lines +569 to +583
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',
})
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +42 to +161
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,
}),
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +116 to +145
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 })
},
})
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 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.ts

Repository: 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 2

Repository: 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 -l

Repository: shapeshift/web

Length of output: 89


🏁 Script executed:

# Search for all requestForQuote usages
rg "requestForQuote" packages/swapper/src/swappers/StonfiSwapper/ -B 3 -A 3

Repository: 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 5

Repository: 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TON

4 participants