@sodax/sdk with React Query into feature-organized hooks. Used alongside @sodax/wallet-sdk-react (no direct dependency — shared types come from @sodax/sdk).
Features
- Swap/Intent —
useQuote,useSwap,useSwapAllowance,useSwapApprove,useCancelSwap,useCreateLimitOrder,useCancelLimitOrder,useStatus - Bridge —
useBridge,useBridgeDetailedStatus,useBridgeAllowance,useBridgeApprove,useGetBridgeableAmount,useGetBridgeableTokens - Money Market —
useSupply,useWithdraw,useBorrow,useRepay,useMMAllowance,useMMApprove, plus reserves data hooks - Staking —
useStake,useUnstake,useInstantUnstake,useClaim,useCancelUnstake, approval hooks, info/config/ratio queries - DEX —
useDexDeposit,useDexWithdraw,useSupplyLiquidity,useDecreaseLiquidity,useClaimRewards, pool/position queries, param builders - Leverage Yield —
useLeverageYieldDeposit,useLeverageYieldWithdraw,useLeverageYieldVaultSwap,useLeverageYieldDetailedStatus,useLeverageYieldNotifySolver, plus vault position/APR/TVL/share queries - Migration —
useMigrateIcxToSoda,useRevertMigrateSodaToIcx,useMigratebnUSD,useMigrateBaln,useMigrationApprove,useMigrationAllowance - Bitcoin (Bound Exchange) —
useRadfiAuth,useEnsureRadfiAccessToken,useRadfiSession,useTradingWallet,useTradingWalletBalance,useBitcoinBalance,useBitcoinTradingSetup,useFundTradingWallet,useRadfiWithdraw,useExpiredUtxos,useRenewUtxos - Partner —
useFetchAssetsBalances,useGetAutoSwapPreferences,useIsTokenApproved,useApproveToken,useSetSwapPreference,useFeeClaimSwap,useFeeClaimWithdraw,usePartnerCancelIntent,useGetUserIntent,useGetIntentDetails - Recovery —
useHubAssetBalances,useWithdrawHubAsset - Backend Queries — Intent tracking, swap-tx submission + status, orderbook, money market position queries
- Shared —
useXBalances,useDeriveUserWalletAddress,useGetUserHubWalletAddress,useStellarTrustlineCheck,useEstablishTrustline,useStellarGate,useNearStorageCheck,useRegisterNearStorage,useNearStorageGate,useEstimateGas - Sponsoring —
useStellarAccountActive,useStellarAccountStatus,useSponsorConfig,useActivateStellarAccount
Installation
Quick Start
1. Set up providers
RPC URLs are injected throughconfig.chains. SodaxProvider is the outermost wrapper; QueryClientProvider wraps everything inside it.
SodaxProvider builds its Sodax instance in the browser, so sodaxConfig ships in your bundle. Leave
apiKey out of it: to send a SODAX API key, point api.baseURL and solver.solverApiEndpoint at your own
backend proxy and attach the key there — see API key good practices.
2. Get a wallet provider
3. Use feature hooks
All mutation hooks accept no arguments at initialization. Domain inputs (params, walletProvider) flow throughmutate(vars):
Requirements
- Node.js >= 22.12.0
- React >= 18
- TypeScript
API Reference
Provider
SodaxProvider— Wraps your app, creates theSodaxSDK instance. Acceptsconfig?: SodaxOptions.createSodaxQueryClient()— Factory for aQueryClientwith global mutation observability (onMutationErrorhook,meta.silentopt-out).
Swap Hooks
useQuote()— Real-time quote (auto-refreshes every 3s)useSwap()— Submit a cross-chain swap intentuseSwapAllowance()— Check token approvaluseSwapApprove()— Approve token spendinguseStatus()— Track intent execution statususeCancelSwap()— Cancel a pending swapuseCreateLimitOrder()— Create a limit order (no deadline)useCancelLimitOrder()— Cancel a limit order
Money Market Hooks
useSupply()— Supply collateraluseWithdraw()— Withdraw supplied tokensuseBorrow()— Borrow against collateraluseRepay()— Repay borrowed tokensuseMMAllowance()— Check approval (auto-skips for borrow/withdraw)useMMApprove()— Approve token spendinguseReservesData()— All reserve datauseReservesHumanized()— Reserves in decimal-normalized formatuseReservesList()— List of reserve asset addressesuseReservesUsdFormat()— Reserves with USD valuesuseUserFormattedSummary()— User portfolio summary (health factor, collateral, debt)useUserReservesData()— User reserve positionsuseAToken()— aToken metadatauseATokensBalances()— aToken balances
Bridge Hooks
useBridge()— Execute a cross-chain bridge transferuseBridgeDetailedStatus()— Track a bridge from its source tx, whichever completion path ranuseBridgeAllowance()— Check token approvaluseBridgeApprove()— Approve token spendinguseGetBridgeableAmount()— Max bridgeable amount between two tokensuseGetBridgeableTokens()— Tokens bridgeable to a destination chain
Staking Hooks
useStake()— Stake SODA, receive xSODAuseUnstake()— Request unstake (waiting period)useInstantUnstake()— Instant unstake with slippageuseClaim()— Claim SODA after waiting perioduseCancelUnstake()— Cancel pending unstakeuseStakeApprove()— Approve SODA for stakinguseUnstakeApprove()— Approve xSODA for unstakinguseInstantUnstakeApprove()— Approve xSODA for instant unstakinguseStakeAllowance()— Check SODA approvaluseUnstakeAllowance()— Check xSODA approval for unstakinguseInstantUnstakeAllowance()— Check xSODA approval for instant unstakinguseStakingInfo()— User staking positionuseUnstakingInfo()— Pending unstake requestsuseUnstakingInfoWithPenalty()— Unstake requests with penalty calcsuseStakingConfig()— Unstaking period, max penaltyuseStakeRatio()— SODA-to-xSODA exchange rateuseInstantUnstakeRatio()— Instant unstake rateuseConvertedAssets()— xSODA to SODA conversion
Leverage Position Hooks
A position is one Aave account owned by the user’s hub wallet, never their signing address — pass the signer asparams.srcAddress and the SDK resolves the owner.
Which write hook to use is decided by the operation, not by preference. increaseLeverage and
decreaseLeverage only post a solver intent, and an intent the solver was never told about
expires unfilled, so those go through useSubmitLeveragePositionIntent(), which reports it.
withdraw, settle and cancel are synchronous on the hub and need no notification.
useOpenLeveragePosition()— Open a position (side: 'collateral' | 'debt') and report its intentuseSubmitLeveragePositionIntent()— RunincreaseLeverage/decreaseLeverageand report the intentuseRunLeveragePositionOperation()— Runwithdraw/settle/cancel; no intent, nothing to reportuseLeveragePositionPayoutAddress()— Where a withdrawal can be paid, which off the hub is not the signeruseLeveragePositionFundingAllowance()— Whether the funding token is approved; the spender is chain-dependentuseApproveLeveragePositionFunding()— Approve it, against the spender the SDK resolvesuseLeveragePositionsForUser()— A user’s positions, discovered through their hub walletuseLeveragePositionAccount()— Collateral, debt, LTV and health factoruseLeveragePositionInfo()— The position’s collateral and borrow reservesuseLeveragePositionCollateral()— Collateral balance helduseLeveragePositionPending()— The pending-operation slot; a position permits one at a time
Resolving means the intent is live, not that leverage moved. Off-hub (spoke) origins are experimental and unverified on-chain; see each hook’s JSDoc.
DEX Hooks
usePools()— List available poolsusePoolData()— Pool details (price, tick, liquidity)usePoolBalances()— User pool token balancesusePositionInfo()— Position details by token IDuseDexDeposit()— Deposit assets into pool tokensuseDexWithdraw()— Withdraw assets from pool tokensuseDexAllowance()— Check approval for deposituseDexApprove()— Approve token spendinguseLiquidityAmounts()— Token amounts for a tick rangeuseSupplyLiquidity()— Supply liquidity to a positionuseDecreaseLiquidity()— Remove liquidityuseClaimRewards()— Claim trading feesuseCreateDepositParams()— Build deposit params with ERC-4626 conversionuseCreateWithdrawParams()— Build withdraw paramsuseCreateSupplyLiquidityParams()— Build tick range + liquidity paramsuseCreateDecreaseLiquidityParams()— Build decrease params from position state
Leverage Yield Hooks
useLeverageYieldEffectiveApr()— Vault effective APR (AAVE rates + LSD yield, leverage-adjusted; refreshes 60s)useLeverageYieldPosition()— Live position snapshot: collateral, debt, LTV, health factor, idle assets (refreshes 30s)useLeverageYieldTotalAssets()— Vault total underlying assets (TVL)useLeverageYieldPreviewRedeem()— Assets received for redeeming shares (ERC-4626previewRedeem)useLeverageYieldShareBalances()— Userlsoda*share balances across every chain they may hold a position underuseLeverageYieldDeposit()— Build the deposit payload (any token →lsoda*shares) foruseLeverageYieldVaultSwapuseLeverageYieldWithdraw()— Build the withdraw payload (lsoda*shares → any token) foruseLeverageYieldVaultSwapuseLeverageYieldVaultSwap()— Execute an end-to-end vault swap (deposit or withdraw); backend 2-step by default, with the client-side fallbackuseLeverageYieldDetailedStatus()— Track a vault swap from its source tx, whichever completion path ranuseLeverageYieldNotifySolver()— Notify the solver that a vault intent landed on the hub (standalone step for manual relay flows)
Migration Hooks
useMigrateIcxToSoda()— ICX/wICX (ICON) → SODA (Sonic)useRevertMigrateSodaToIcx()— SODA (Sonic) → wICX (ICON)useMigratebnUSD()— Legacy bnUSD ↔ new bnUSD (bidirectional)useMigrateBaln()— BALN (ICON) → SODA with optional lock perioduseMigrationApprove()— Approve token spending before migrationuseMigrationAllowance()— Check if approval is needed
Bitcoin (Bound Exchange) Hooks
useRadfiSession()— Manage Bound Exchange session (login, auto-refresh)useRadfiAuth()— Authenticate with Bound Exchange via BIP322 signinguseEnsureRadfiAccessToken()— Ensure a valid Bound Exchange access token (refresh, or full re-auth), returning it for a Bitcoin-source createIntentuseTradingWallet()— Get trading wallet address from persisted sessionuseBitcoinBalance()— BTC balance for any addressuseTradingWalletBalance()— Trading wallet balance from Bound Exchange APIuseBitcoinTradingSetup()— Bitcoin trading-wallet setup for one side of a cross-chain flow (inert unless the chain is Bitcoin)useFundTradingWallet()— Fund trading wallet from personal walletuseRadfiWithdraw()— Withdraw from trading walletuseExpiredUtxos()— Fetch expired UTXOs (polls every 60s)useRenewUtxos()— Renew expired UTXOs
Partner Hooks
useFetchAssetsBalances()— Fetch partner asset balancesuseGetAutoSwapPreferences()— Get auto-swap preferencesuseIsTokenApproved()— Check token approvaluseApproveToken()— Approve tokenuseSetSwapPreference()— Set swap preferenceuseFeeClaimSwap()— Claim partner fees via swapuseFeeClaimWithdraw()— Withdraw a partner fee balance via a Sonic-sourced bridge to the destination chainusePartnerCancelIntent()— Cancel a stuck partner fee-claim auto-swap intent and recover the locked tokensuseGetUserIntent()— Look up the stored intent hash for a partner(user, fromToken, toToken)pairuseGetIntentDetails()— Fetch the full intent details for a ProtocolIntents intent hash
Recovery Hooks
useHubAssetBalances()— Get hub asset balancesuseWithdrawHubAsset()— Withdraw hub asset
Shared Hooks
useSodaxContext()— Access theSodaxSDK instanceuseHubProvider()— Access the hub chain (Sonic) provideruseXBalances()— Cross-chain token balances for an addressuseDeriveUserWalletAddress()— Derive hub wallet address (CREATE3)useGetUserHubWalletAddress()— Derive hub wallet address (wallet router)useEstimateGas()— Estimate gas for transactionsuseStellarTrustlineCheck()— Check Stellar trustlineuseEstablishTrustline()— Request Stellar trustlineuseRequestTrustline()— Deprecated, useuseEstablishTrustline()useStellarGate()— Sequences the Stellar destination prerequisites (activation → funding → trustline)useNearStorageCheck()— Check NEP-141 storage registration for a token on NEAR (NEAR’s analogue of a Stellar trustline)useRegisterNearStorage()— Submit a NEP-141storage_depositso a NEAR account can receive a tokenuseNearStorageGate()— Derive the NEP-141 storage-registration UI gate state for a NEAR delivery
Sponsoring Hooks
Stellar accounts must exist on-chain before they can hold or receive anything, and a new user holds 0 XLM. These drive sponsored activation, where the SODAX sponsor pays the account’s base reserve.useStellarAccountActive()— Whether a Stellar account exists on-chainuseStellarAccountStatus()— Existence plus whether the account can afford a trustlineuseSponsorConfig()— Sponsor account, network, fee band, max time boundsuseActivateStellarAccount()— Activate a Stellar account via the sponsor
Backend Query Hooks
useBackendIntentByTxHash()— Get intent by hub tx hash (polls 1s)useBackendIntentByHash()— Get intent by intent hashuseBackendUserIntents()— All intents for a user with date filteringuseBackendOrderbook()— Solver orderbook (cached 30s, no auto-refetch)useBackendMoneyMarketPosition()— User money market positionuseBackendAllMoneyMarketAssets()— All MM assetsuseBackendMoneyMarketAsset()— Single MM asset detailsuseBackendMoneyMarketAssetBorrowers()— Asset borrowersuseBackendMoneyMarketAssetSuppliers()— Asset suppliersuseBackendAllMoneyMarketBorrowers()— All borrowersuseBackendOracleMarkets()— Oracle candle discovery: quote, intervals, symbols (cached 60s)useBackendOracleCandles()— USD OHLC candles for a symbol over[from, to)(cached 10s)
Swaps API Hooks (sodax.api.swaps)
Typed wrappers over the backend Swaps API — one useSwapsApi* hook per endpoint (21 total). Highlights:
useSwapsApiQuote()— Solver quote for a cross-chain swapuseSwapsApiCreateIntent()— Build an unsigned create-intent txuseSwapsApiSubmitTx()— Submit swap tx to backenduseSwapsApiSubmitTxStatus()— Check submitted swap status
src/hooks/swapsApi/ for the full set (tokens, deadline, allowance, approve, submit/cancel intent, status, hash, packet, extra-data, intent lookups, limit orders, gas, fees).
Bridge API Hooks (sodax.api.bridge)
Typed wrappers over the backend Bridge API v2 — one useBridgeApi* hook per endpoint. Highlights:
useBridgeApiSubmitTx()— Hand a broadcast spoke-deposit tx to the backend relay pipelineuseBridgeApiSubmitTxStatus()— Poll a submitted bridge tx by(txHash, srcChainKey)
src/hooks/bridgeApi/ for the full set (tokens, allowance, approve, create intent, fee, bridgeable amount, bridgeable pair).
API-key failures are terminal
Every retryinguseSwapsApi*, useLeverageYieldApi* and useBridgeApi* hook defaults retry to retryUnlessAuthFailure(): transport blips retry up to 3 times, a 401/403 never replays. useSwapsApiStatus, useSwapsApiSubmitTxStatus, useLeverageYieldApiSubmitTxStatus and useBridgeApiSubmitTxStatus stop their 1s poll on a rejected key too, since retry bounds attempts within a tick rather than the interval — so a bad key surfaces once, on error, instead of re-requesting forever. Override either through queryOptions / mutationOptions.
useDetailedStatus and useBridgeDetailedStatus stop on a rejected key as well, but by a different route: the SDK hands them the 401/403 inside a Result rather than throwing, so React Query never sees an error and retry has nothing to withhold. Their poll interval checks isAuthFailure(data.error) instead. It has to — a rejected key leaves the backend unanswered, so a relay miss behind it is never tagged DETAILED_STATUS_NOT_DELIVERED, and the not-delivered budget that stops every other unresolved read would never advance.
Utils
DEX param builders:createDepositParamsProps()— Build deposit params from pool data and spoke asset infocreateWithdrawParamsProps()— Build withdraw params with optional destination infocreateSupplyLiquidityParamsProps()— Build concentrated liquidity supply paramscreateDecreaseLiquidityParamsProps()— Build decrease liquidity params
resolveNearStorageGate()— Derive the NEP-141 storage-registration UI gate state (hook-free counterpart ofuseNearStorageGate)
Development
AI agent docs
AI-readable docs for@sodax/dapp-kit (and the other @sodax/* packages) are shipped via @sodax/skills — a separate npm package bundling Claude-Code SKILL.md files and a long-form knowledge tree.
Recommended: skills CLI — from your project root:
AGENTS.md pointer (fallback for web chats, or when you prefer a devDependency over the CLI):
node_modules/@sodax/skills/AGENTS.md. See docs/ai-integration-guide.md for all install modes and per-tool wiring.