@sodax/skills 2.0.0-rc.11 → 2.0.0-rc.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/package.json +1 -1
  2. package/skills/sodax-dapp-kit/SKILL.md +2 -2
  3. package/skills/sodax-dapp-kit/integration/knowledge/README.md +1 -1
  4. package/skills/sodax-dapp-kit/integration/knowledge/features/README.md +1 -1
  5. package/skills/sodax-dapp-kit/integration/knowledge/features/auxiliary-services.md +44 -0
  6. package/skills/sodax-dapp-kit/integration/knowledge/features/bitcoin.md +6 -6
  7. package/skills/sodax-dapp-kit/integration/knowledge/features/money-market.md +5 -0
  8. package/skills/sodax-dapp-kit/integration/knowledge/recipes/bitcoin.md +8 -8
  9. package/skills/sodax-dapp-kit/integration/knowledge/recipes/wallet-connectivity.md +3 -0
  10. package/skills/sodax-dapp-kit/integration/knowledge/reference/glossary.md +1 -1
  11. package/skills/sodax-dapp-kit/integration/knowledge/reference/hooks-index.md +6 -2
  12. package/skills/sodax-dapp-kit/integration/knowledge/reference/querykey-conventions.md +2 -0
  13. package/skills/sodax-dapp-kit/migration-v1-to-v2/knowledge/breaking-changes/hook-signatures.md +1 -1
  14. package/skills/sodax-dapp-kit/migration-v1-to-v2/knowledge/breaking-changes/sdk-leakage.md +1 -1
  15. package/skills/sodax-dapp-kit/migration-v1-to-v2/knowledge/checklist.md +1 -1
  16. package/skills/sodax-dapp-kit/migration-v1-to-v2/knowledge/features/README.md +1 -1
  17. package/skills/sodax-dapp-kit/migration-v1-to-v2/knowledge/features/bitcoin.md +2 -2
  18. package/skills/sodax-dapp-kit/migration-v1-to-v2/knowledge/reference/renamed-hooks.md +1 -1
  19. package/skills/sodax-sdk/SKILL.md +1 -1
  20. package/skills/sodax-sdk/integration/knowledge/README.md +1 -1
  21. package/skills/sodax-sdk/integration/knowledge/architecture.md +5 -3
  22. package/skills/sodax-sdk/integration/knowledge/chain-specifics.md +33 -8
  23. package/skills/sodax-sdk/integration/knowledge/features/bridge.md +1 -0
  24. package/skills/sodax-sdk/integration/knowledge/features/money-market.md +1 -0
  25. package/skills/sodax-sdk/integration/knowledge/features/swap.md +1 -0
  26. package/skills/sodax-sdk/integration/knowledge/recipes/README.md +1 -0
  27. package/skills/sodax-sdk/integration/knowledge/recipes/initialize-sodax.md +6 -2
  28. package/skills/sodax-sdk/integration/knowledge/recipes/logging.md +102 -0
  29. package/skills/sodax-sdk/integration/knowledge/reference/public-api.md +8 -0
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "2.0.0-rc.11",
7
+ "version": "2.0.0-rc.12",
8
8
  "license": "MIT",
9
9
  "description": "AI-agent skills and knowledge for consumers of @sodax/* SDKs (Claude Code, Codex, Cursor, …)",
10
10
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sodax-dapp-kit
3
- description: 'INTEGRATION (write NEW code) — @sodax/dapp-kit is React hooks wrapping @sodax/sdk with React Query across 11 feature domains (swap, money market, staking, bridge, dex, migration, partner, recovery, bitcoin/Radfi, backend queries, shared). React-only — Node and backend code uses `@sodax/sdk` directly. Use whenever a React dapp needs SODAX feature hooks. Triggers on "use @sodax/dapp-kit", "useSwap", "useMoneyMarket", "useStake", "useBridge", "useDex", "useMigrate", any `use<Feature>` hook name from dapp-kit, "Sodax React hooks", "dapp-kit query / mutation". v2 hook shape: mutation hooks return `SafeUseMutationResult` with `mutateAsyncSafe(vars): Promise<Result<TData>>`. mutationFn unwraps SDK Result<T> and throws on `!ok` so React Query''s native error model engages. Hook-owned invalidations — consumer onSuccess runs after. MIGRATION (port v1 → v2) — a deep canonicalization pass: single-object hook params, mandatory `mutateAsyncSafe`, hook-owned invalidations, throw-on-`Result.!ok` inside `mutationFn`, canonical queryKey/mutationKey conventions. Plus the SDK underneath was reshaped (chain-key-driven routing, `Result<T>` everywhere, `WalletProviderSlot<K, Raw>`). v1 dapp-kit code will not compile against v2. Triggers on "migrate @sodax/dapp-kit", "useSpokeProvider gone", "dapp-kit v1 → v2", "invalidateMmQueries broken", "dapp-kit hook signatures changed", v1 fingerprints (positional args, hook-init `spokeProvider`, `useSpokeProvider`, `invalidateMmQueries`, legacy `useMigrate`, `*_MAINNET_CHAIN_ID`). Load this skill if EITHER applies; the body gates by mode.'
3
+ description: 'INTEGRATION (write NEW code) — @sodax/dapp-kit is React hooks wrapping @sodax/sdk with React Query across 11 feature domains (swap, money market, staking, bridge, dex, migration, partner, recovery, bitcoin/Bound Exchange, backend queries, shared). React-only — Node and backend code uses `@sodax/sdk` directly. Use whenever a React dapp needs SODAX feature hooks. Triggers on "use @sodax/dapp-kit", "useSwap", "useMoneyMarket", "useStake", "useBridge", "useDex", "useMigrate", any `use<Feature>` hook name from dapp-kit, "Sodax React hooks", "dapp-kit query / mutation". v2 hook shape: mutation hooks return `SafeUseMutationResult` with `mutateAsyncSafe(vars): Promise<Result<TData>>`. mutationFn unwraps SDK Result<T> and throws on `!ok` so React Query''s native error model engages. Hook-owned invalidations — consumer onSuccess runs after. MIGRATION (port v1 → v2) — a deep canonicalization pass: single-object hook params, mandatory `mutateAsyncSafe`, hook-owned invalidations, throw-on-`Result.!ok` inside `mutationFn`, canonical queryKey/mutationKey conventions. Plus the SDK underneath was reshaped (chain-key-driven routing, `Result<T>` everywhere, `WalletProviderSlot<K, Raw>`). v1 dapp-kit code will not compile against v2. Triggers on "migrate @sodax/dapp-kit", "useSpokeProvider gone", "dapp-kit v1 → v2", "invalidateMmQueries broken", "dapp-kit hook signatures changed", v1 fingerprints (positional args, hook-init `spokeProvider`, `useSpokeProvider`, `invalidateMmQueries`, legacy `useMigrate`, `*_MAINNET_CHAIN_ID`). Load this skill if EITHER applies; the body gates by mode.'
4
4
  ---
5
5
 
6
6
  # When to use this skill
@@ -32,7 +32,7 @@ Pick this mode when the consumer is a React dapp using `@sodax/dapp-kit` hooks.
32
32
  1. Read [`integration/knowledge/ai-rules.md`](./integration/knowledge/ai-rules.md) — DO / DO NOT / workflow / stop conditions.
33
33
  2. Read [`integration/knowledge/quickstart.md`](./integration/knowledge/quickstart.md) — install + wire providers + first feature.
34
34
  3. Read [`integration/knowledge/architecture.md`](./integration/knowledge/architecture.md) — hook shapes, queryKey conventions, `useSafeMutation`, `unwrapResult`, `Result<T>`.
35
- 4. For each feature you use, read [`integration/knowledge/features/`](./integration/knowledge/features/) — `swap.md`, `money-market.md`, `staking.md`, `bridge.md`, `dex.md`, `migration.md`, `bitcoin.md` (Radfi, dapp-kit-unique), `auxiliary-services.md` (partner + recovery + backend + shared).
35
+ 4. For each feature you use, read [`integration/knowledge/features/`](./integration/knowledge/features/) — `swap.md`, `money-market.md`, `staking.md`, `bridge.md`, `dex.md`, `migration.md`, `bitcoin.md` (Bound Exchange, dapp-kit-unique), `auxiliary-services.md` (partner + recovery + backend + shared).
36
36
  5. Recipes → [`integration/knowledge/recipes/`](./integration/knowledge/recipes/) — `setup.md`, `wallet-connectivity.md`, per-feature, `mutation-error-handling.md`, `observability.md`, `invalidations.md`.
37
37
  6. Lookups → [`integration/knowledge/reference/`](./integration/knowledge/reference/) — `hooks-index.md`, `querykey-conventions.md`, `public-api.md`, `glossary.md`.
38
38
 
@@ -15,7 +15,7 @@ This tree documents v2 of the dapp-kit React hooks for **new consumers** buildin
15
15
  | [`features/bridge.md`](features/bridge.md) | Bridge hooks: `useBridge`, allowance/approve, bridgeable amount/tokens. |
16
16
  | [`features/dex.md`](features/dex.md) | DEX hooks: deposit/withdraw, supply/decrease liquidity, claim rewards, position info, pools. |
17
17
  | [`features/migration.md`](features/migration.md) | Migration hooks: ICX/bnUSD/BALN forward + reverse, allowance/approve. |
18
- | [`features/bitcoin.md`](features/bitcoin.md) | Radfi hooks (dapp-kit-unique): session, trading wallet, fund/withdraw, UTXOs. |
18
+ | [`features/bitcoin.md`](features/bitcoin.md) | Bound Exchange hooks (dapp-kit-unique): session, trading wallet, fund/withdraw, UTXOs. |
19
19
  | [`features/auxiliary-services.md`](features/auxiliary-services.md) | Partner, recovery, backend queries, shared (xBalances, gas estimation, trustlines). |
20
20
  | [`recipes/`](recipes/) | Copy-paste patterns: setup, wallet connectivity, per-feature flows, mutation error handling, observability, invalidations. |
21
21
  | [`reference/`](reference/) | Lookup tables: full hook index, queryKey conventions, public API surface, glossary. |
@@ -10,7 +10,7 @@ Per-feature reference docs. Each file documents the hooks, params types, return
10
10
  | [`bridge.md`](bridge.md) | Cross-chain token bridging: `useBridge`, allowance/approve, bridgeable amount/tokens. |
11
11
  | [`dex.md`](dex.md) | Concentrated liquidity DEX: assets in/out, liquidity supply/decrease, claim rewards, position info, pool reads, param builders. |
12
12
  | [`migration.md`](migration.md) | Token migration: `useMigrateIcxToSoda`, `useRevertMigrateSodaToIcx`, `useMigratebnUSD`, `useMigrateBaln`, allowance/approve. |
13
- | [`bitcoin.md`](bitcoin.md) | Radfi (dapp-kit-unique): session, trading wallet, fund/withdraw, UTXOs. |
13
+ | [`bitcoin.md`](bitcoin.md) | Bound Exchange (dapp-kit-unique): session, trading wallet, fund/withdraw, UTXOs. |
14
14
  | [`auxiliary-services.md`](auxiliary-services.md) | Partner fee claiming, recovery, backend queries (intent tracking, orderbook, MM data), shared utilities (xBalances, gas, trustlines). |
15
15
 
16
16
  ## Reference vs recipes
@@ -94,6 +94,10 @@ useGetUserHubWalletAddress({ params, queryOptions }); // Hub wallet via wallet r
94
94
  useEstimateGas({ mutationOptions }); // Gas estimation for raw tx
95
95
  useStellarTrustlineCheck({ params, queryOptions });
96
96
  useRequestTrustline({ mutationOptions });
97
+ useNearStorageCheck({ params, queryOptions }); // NEP-141 storage registration check (NEAR)
98
+ useRegisterNearStorage({ mutationOptions }); // NEP-141 storage_deposit (NEAR)
99
+ useNearStorageGate({ dstChainKey, token, accountId, walletProvider }); // composite NEAR receive-side gate
100
+ resolveNearStorageGate(chainKey, check); // unwrapped util: gate-state from a useNearStorageCheck result
97
101
  ```
98
102
 
99
103
  ### `useXBalances` shape
@@ -144,6 +148,46 @@ if (hasTrustline === false) {
144
148
  }
145
149
  ```
146
150
 
151
+ ### NEAR storage registration
152
+
153
+ NEP-141 accounts must pay a one-time storage bond before they can receive a token — delivering to an unregistered account fails. The receive-side analogue of Stellar trustlines: gate any flow that delivers a token to the user on NEAR (swap output on NEAR, bridge into NEAR, money-market borrow/withdraw to NEAR). Use `useNearStorageGate` for app UI; use the lower-level `useNearStorageCheck` + `useRegisterNearStorage` pair when you need custom wiring.
154
+
155
+ ```ts
156
+ // @ai-snippets-skip — illustrative only; real types pulled into agents below.
157
+ // useNearStorageCheck is a canonical read hook: { params: { token, accountId, chainId }, queryOptions }.
158
+ // `chainId` is a SpokeChainKey typed loosely — the hook returns `true` for non-NEAR chains (safe to
159
+ // gate conditionally) and `true` for native NEAR (not a NEP-141 token). data is a boolean;
160
+ // queryKey: ['shared', 'nearStorageCheck', chainId, token, accountId].
161
+ const { data: isRegistered, isLoading } = useNearStorageCheck({
162
+ params: { token, accountId, chainId: ChainKeys.NEAR_MAINNET },
163
+ });
164
+
165
+ // useRegisterNearStorage IS a canonical mutation hook: { mutationOptions }, returns
166
+ // SafeUseMutationResult. Domain inputs flow through mutate / mutateAsync / mutateAsyncSafe.
167
+ // Vars: { token, accountId, walletProvider, deposit? } — deposit defaults to the NEP-141
168
+ // storage bond (0.00125 NEAR). mutationKey: ['shared', 'registerNearStorage'].
169
+ const { mutateAsyncSafe: registerStorage } = useRegisterNearStorage();
170
+ if (isRegistered === false) {
171
+ await registerStorage({ token, accountId, walletProvider });
172
+ }
173
+ ```
174
+
175
+ For the common UI path, `useNearStorageGate` returns `{ isNear, needsRegistration, blocksAction, isChecking, isRegistering, registerStorage }`.
176
+
177
+ Derive UI gate flags with the unwrapped `resolveNearStorageGate` util (no hook) when you need custom check/register composition:
178
+
179
+ ```ts
180
+ // @ai-snippets-skip — illustrative only.
181
+ // resolveNearStorageGate(chainKey, check) reads `isLoading` + `data` off the useNearStorageCheck
182
+ // result and returns { isNear, needsRegistration, blocksAction }:
183
+ // needsRegistration — show the register action (check resolved AND not registered)
184
+ // blocksAction — keep the downstream action disabled (still checking OR needs registration)
185
+ const check = useNearStorageCheck({ params: { token, accountId, chainId: dstChainKey } });
186
+ const { needsRegistration, blocksAction } = resolveNearStorageGate(dstChainKey, check);
187
+ ```
188
+
189
+ The underlying SDK methods (`isStorageRegistered` / `registerStorage` on the NEAR spoke service, and the `NEAR_STORAGE_DEPOSIT` constant) are documented in the `sodax-sdk` skill (integration mode).
190
+
147
191
  ## Default polling intervals
148
192
 
149
193
  | Hook | Polling | Notes |
@@ -1,6 +1,6 @@
1
- # Bitcoin (Radfi) — `@sodax/dapp-kit`
1
+ # Bitcoin (Bound Exchange) — `@sodax/dapp-kit`
2
2
 
3
- Bitcoin trading via the Radfi protocol — authenticate, fund a trading wallet, withdraw, manage UTXOs. **Dapp-kit-unique surface** (no SDK equivalent — these flows are React-shaped).
3
+ Bitcoin trading via the Bound Exchange protocol — authenticate, fund a trading wallet, withdraw, manage UTXOs. **Dapp-kit-unique surface** (no SDK equivalent — these flows are React-shaped).
4
4
 
5
5
  Pair: [`features/bitcoin.md`](../../../migration-v1-to-v2/knowledge/features/bitcoin.md).
6
6
 
@@ -15,7 +15,7 @@ useTradingWallet(walletAddress); // Synchronous: read persisted
15
15
 
16
16
  // Balances
17
17
  useBitcoinBalance({ params: { address, rpcUrl? }, queryOptions }); // Personal wallet (default mempool.space)
18
- useTradingWalletBalance({ params: { walletProvider, tradingAddress }, queryOptions }); // Radfi API
18
+ useTradingWalletBalance({ params: { walletProvider, tradingAddress }, queryOptions }); // Bound Exchange API
19
19
 
20
20
  // Operations
21
21
  useFundTradingWallet({ mutationOptions });
@@ -26,7 +26,7 @@ useRenewUtxos({ mutationOptions });
26
26
 
27
27
  ## Session flow
28
28
 
29
- Radfi requires authentication before trading operations. `useRadfiSession` handles the full lifecycle:
29
+ Bound Exchange requires authentication before trading operations. `useRadfiSession` handles the full lifecycle:
30
30
 
31
31
  ```ts
32
32
  // @ai-snippets-skip
@@ -76,8 +76,8 @@ type RenewUtxosVars = { txIdVouts: string[]; walletProvider: IBitcoinWalletProvi
76
76
 
77
77
  1. **Authentication required before any trading operation.** `useRadfiSession` manages this automatically — gate buttons on `isAuthed`.
78
78
  2. **Trading wallet is created during first authentication** — not a separate step.
79
- 3. **`useTradingWallet(walletAddress)` is synchronous** (no network call). It reads persisted Radfi session from localStorage. Use it when you don't have a `walletProvider` available yet but need the trading address.
80
- 4. **PSBT signing flow for withdrawals.** Radfi server builds an unsigned PSBT, the user signs locally via the wallet provider, then submits back for co-signing and broadcast. The dapp-kit hook orchestrates this — consumers don't see PSBTs directly.
79
+ 3. **`useTradingWallet(walletAddress)` is synchronous** (no network call). It reads persisted Bound Exchange session from localStorage. Use it when you don't have a `walletProvider` available yet but need the trading address.
80
+ 4. **PSBT signing flow for withdrawals.** Bound Exchange server builds an unsigned PSBT, the user signs locally via the wallet provider, then submits back for co-signing and broadcast. The dapp-kit hook orchestrates this — consumers don't see PSBTs directly.
81
81
  5. **Session tokens are for API rate-limiting, not asset access.** Stored in localStorage keyed by wallet address. Compromise of localStorage data doesn't affect funds.
82
82
  6. **`useExpiredUtxos` polls every 60s** — set `queryOptions.refetchInterval: false` to disable while UI is hidden.
83
83
 
@@ -43,6 +43,8 @@ type UseUserFormattedSummaryParams = ReadHookParams<FormatUserSummaryResponse, {
43
43
  userAddress: string | undefined;
44
44
  }>;
45
45
  // Same shape on useUserReservesData. The hook derives the hub wallet from (spokeChainKey, userAddress) internally.
46
+ // Bitcoin: always pass the personal address as userAddress — the hook transparently resolves to the
47
+ // Bound Exchange trading-wallet-derived hub wallet when a session is active (local lookup, no network).
46
48
 
47
49
  // useAToken — single aToken metadata; FLAT (no chain/user fields)
48
50
  type UseATokenParams = ReadHookParams<ATokenData, {
@@ -57,10 +59,13 @@ type UseATokensBalancesParams = ReadHookParams<Map<Address, bigint>, {
57
59
  userAddress: string | undefined;
58
60
  }>;
59
61
  // Returns a Map keyed by aToken address. The hook resolves the hub wallet from (spokeChainKey, userAddress).
62
+ // Bitcoin: always pass the personal address — trading-wallet resolution is automatic when a session exists.
60
63
  ```
61
64
 
62
65
  > **Read-side chain key is `spokeChainKey`, not `srcChainKey`.** Mutation hooks (`useSupply`/`useBorrow`/etc.) use `srcChainKey` because the request crosses chains and needs a source. Read hooks describe a user's position on a single spoke chain — the field is `spokeChainKey`. Applies to `useATokensBalances`, `useUserFormattedSummary`, and `useUserReservesData` (`useAToken` is metadata-only and takes neither). Don't grep-replace one for the other.
63
66
 
67
+ > **Bitcoin `userAddress`**: always pass the **personal** address (the user's wallet address), not the Bound Exchange trading address. The read hooks (`useATokensBalances`, `useUserReservesData`, `useUserFormattedSummary`, `useGetUserHubWalletAddress`) automatically resolve to the trading-wallet-derived hub wallet when a Bound Exchange session is active — this is a local lookup with no network call and no throw on an unauthenticated session. See [`features/bitcoin.md`](bitcoin.md) for Bound Exchange session setup.
68
+
64
69
  ## Mutation params
65
70
 
66
71
  All four user mutations share the same TVars shape (only the `action` literal differs):
@@ -1,6 +1,6 @@
1
- # Recipe: Bitcoin (Radfi)
1
+ # Recipe: Bitcoin (Bound Exchange)
2
2
 
3
- Bitcoin trading via the Radfi protocol. Authenticate, fund a trading wallet, trade, withdraw, and manage UTXOs.
3
+ Bitcoin trading via the Bound Exchange protocol. Authenticate, fund a trading wallet, trade, withdraw, and manage UTXOs.
4
4
 
5
5
  **Depends on:** [setup.md](setup.md), [wallet-connectivity.md](wallet-connectivity.md)
6
6
 
@@ -10,7 +10,7 @@ Bitcoin trading via the Radfi protocol. Authenticate, fund a trading wallet, tra
10
10
 
11
11
  | Hook | Type | Purpose |
12
12
  |------|------|---------|
13
- | `useRadfiAuth` | Mutation | Authenticate with Radfi via BIP322 signing |
13
+ | `useRadfiAuth` | Mutation | Authenticate with Bound Exchange via BIP322 signing |
14
14
  | `useRadfiSession` | Utility | Manage full session lifecycle (login, refresh, auto-refresh) |
15
15
  | `useTradingWallet` | Utility | Get trading wallet address from persisted session (synchronous) |
16
16
 
@@ -19,7 +19,7 @@ Bitcoin trading via the Radfi protocol. Authenticate, fund a trading wallet, tra
19
19
  | Hook | Type | Purpose |
20
20
  |------|------|---------|
21
21
  | `useBitcoinBalance` | Query | BTC balance for any address (sums UTXOs from mempool.space) |
22
- | `useTradingWalletBalance` | Query | Trading wallet balance from Radfi API (confirmed + pending) |
22
+ | `useTradingWalletBalance` | Query | Trading wallet balance from Bound Exchange API (confirmed + pending) |
23
23
 
24
24
  ### Operations
25
25
 
@@ -32,7 +32,7 @@ Bitcoin trading via the Radfi protocol. Authenticate, fund a trading wallet, tra
32
32
 
33
33
  ## Session Flow
34
34
 
35
- Radfi requires authentication before any trading operation. The typical flow:
35
+ Bound Exchange requires authentication before any trading operation. The typical flow:
36
36
 
37
37
  ```tsx
38
38
  import { useRadfiSession } from '@sodax/dapp-kit';
@@ -49,7 +49,7 @@ function BitcoinAuth() {
49
49
 
50
50
  return (
51
51
  <button onClick={login} disabled={isLoginPending}>
52
- {isLoginPending ? 'Signing...' : 'Login to Radfi'}
52
+ {isLoginPending ? 'Signing...' : 'Login to Bound Exchange'}
53
53
  </button>
54
54
  );
55
55
  }
@@ -77,7 +77,7 @@ function BitcoinBalances({ walletAddress }: { walletAddress: string }) {
77
77
  params: { address: walletAddress },
78
78
  });
79
79
 
80
- // Trading wallet balance (from Radfi API). `RadfiWalletBalance` fields:
80
+ // Trading wallet balance (from Bound Exchange API). `RadfiWalletBalance` fields:
81
81
  // `btcSatoshi`, `pendingSatoshi`, `externalPendingSatoshi`, `totalUtxos`.
82
82
  const { data: tradingBalance } = useTradingWalletBalance({
83
83
  params: { walletProvider, tradingAddress },
@@ -188,6 +188,6 @@ function UtxoManager({ tradingAddress }: { tradingAddress: string }) {
188
188
 
189
189
  - **Authentication required** before any trading operation. `useRadfiSession` manages this automatically.
190
190
  - **Trading wallet** is created during first authentication — not a separate step.
191
- - **`useTradingWallet(walletAddress)`** is a synchronous utility (no network call) — it reads the persisted Radfi session from localStorage.
191
+ - **`useTradingWallet(walletAddress)`** is a synchronous utility (no network call) — it reads the persisted Bound Exchange session from localStorage.
192
192
  - **PSBT signing flow**: withdraw and renew operations build an unsigned PSBT server-side, user signs it locally, then submits back for co-signing and broadcast.
193
193
  - **Session tokens** are stored in localStorage keyed by wallet address. They're for API rate-limiting, not for accessing user assets.
@@ -16,6 +16,9 @@ Connect wallets and pass wallet providers to feature hooks.
16
16
  | `useEstimateGas` | `@sodax/dapp-kit` | Mutation | Estimate gas for raw transactions |
17
17
  | `useStellarTrustlineCheck` | `@sodax/dapp-kit` | Query | Check if Stellar account has sufficient trustline |
18
18
  | `useRequestTrustline` | `@sodax/dapp-kit` | Mutation | Request a Stellar trustline for a token |
19
+ | `useNearStorageCheck` | `@sodax/dapp-kit` | Query | Check if a NEAR account is NEP-141 storage-registered for a token |
20
+ | `useRegisterNearStorage` | `@sodax/dapp-kit` | Mutation | Submit a NEP-141 `storage_deposit` so a NEAR account can receive a token |
21
+ | `useNearStorageGate` | `@sodax/dapp-kit` | Hook | Combine the NEAR storage check, registration mutation, and UI gate flags |
19
22
 
20
23
  ## Connect a Wallet
21
24
 
@@ -130,7 +130,7 @@ App-level React component. Provides:
130
130
  - RPC config for all chains
131
131
  - Hub provider access (via `useHubProvider()`)
132
132
 
133
- Optional config: `<SodaxProvider config={DeepPartial<SodaxConfig>}>`. Without config, SDK uses packaged defaults.
133
+ Optional config: `<SodaxProvider config={SodaxOptions}>` (`SodaxOptions = DeepPartial<SodaxConfig> & { logger? }`). Without config, SDK uses packaged defaults.
134
134
 
135
135
  Config is tracked by **reference** - see [`recipes/setup.md § Config reactivity`](../recipes/setup.md#config-reactivity) for module-const vs `useMemo` patterns.
136
136
 
@@ -108,7 +108,7 @@ Comprehensive hook table across 11 feature domains. Use this when you know the f
108
108
  | `useMigrationApprove` | Mutation | Approve before migration (action-discriminated) |
109
109
  | `useMigrationAllowance` | Query | Approval check (action-discriminated) |
110
110
 
111
- ## Bitcoin / Radfi
111
+ ## Bitcoin / Bound Exchange
112
112
 
113
113
  | Hook | Type | Purpose |
114
114
  |---|---|---|
@@ -116,7 +116,7 @@ Comprehensive hook table across 11 feature domains. Use this when you know the f
116
116
  | `useRadfiSession` | Utility | Manage full session lifecycle |
117
117
  | `useTradingWallet` | Utility | Synchronously read trading wallet from localStorage |
118
118
  | `useBitcoinBalance` | Query | BTC balance for any address |
119
- | `useTradingWalletBalance` | Query | Trading wallet balance from Radfi API |
119
+ | `useTradingWalletBalance` | Query | Trading wallet balance from Bound Exchange API |
120
120
  | `useFundTradingWallet` | Mutation | Fund trading wallet from personal wallet |
121
121
  | `useRadfiWithdraw` | Mutation | Withdraw from trading wallet |
122
122
  | `useExpiredUtxos` | Query | Expired UTXOs (polls 60s) |
@@ -179,6 +179,10 @@ Comprehensive hook table across 11 feature domains. Use this when you know the f
179
179
  | `useEstimateGas` | Mutation | Estimate gas for raw tx |
180
180
  | `useStellarTrustlineCheck` | Query | Check Stellar trustline |
181
181
  | `useRequestTrustline` | Mutation | Request a Stellar trustline |
182
+ | `useNearStorageCheck` | Query | Check NEP-141 storage registration (NEAR) |
183
+ | `useRegisterNearStorage` | Mutation | Submit NEP-141 `storage_deposit` (NEAR) |
184
+ | `useNearStorageGate` | Hook | Composite NEAR receive-side storage gate |
185
+ | `resolveNearStorageGate` | Utility | Derive gate flags from a `useNearStorageCheck` result (unwrapped) |
182
186
  | `useSafeMutation` | Internal | The wrapper every mutation hook calls |
183
187
  | `unwrapResult` | Internal | `Result<T>` → throw / return |
184
188
  | `toResult` | Internal | `Promise<T>` → `Result<T>` |
@@ -77,6 +77,7 @@ queryKey: ['staking', 'allowance', srcChainKey, action, srcAddress, amount.toStr
77
77
  queryKey: ['mm', 'userReservesData', spokeChainKey, userAddress]
78
78
  queryKey: ['staking', 'info', srcChainKey, srcAddress] // useStakingInfo — second segment is 'info', not 'stakingInfo'
79
79
  queryKey: ['shared', 'xBalances', xChainId, tokens, address]
80
+ queryKey: ['shared', 'nearStorageCheck', chainId, token, accountId] // useNearStorageCheck
80
81
 
81
82
  // Mutation default keys
82
83
  mutationKey: ['swap'] // useSwap
@@ -90,6 +91,7 @@ mutationKey: ['staking', 'approve', 'stake'] // useStake
90
91
  mutationKey: ['bridge'] // useBridge — single segment (no 'execute' suffix)
91
92
  mutationKey: ['migrate', 'icxToSoda']
92
93
  mutationKey: ['dex', 'supplyLiquidity']
94
+ mutationKey: ['shared', 'registerNearStorage'] // useRegisterNearStorage
93
95
  ```
94
96
 
95
97
  ## Per-feature key tables
@@ -27,7 +27,7 @@ The structural shape of every dapp-kit hook changed in v2. Five categories of br
27
27
  + }}>
28
28
  ```
29
29
 
30
- The `config` prop is `DeepPartial<SodaxConfig>` from `@sodax/sdk`. Other fields available: `api`, `solver`, `swaps`, `bridge`, `dex`, `moneyMarket`, `hub`, `relay`, `fee`. See `sodax-sdk`: `migration-v1-to-v2/knowledge/breaking-changes/architecture.md` for the SDK-side reshape.
30
+ The `config` prop is `SodaxOptions` from `@sodax/sdk` (`= DeepPartial<SodaxConfig> & { logger? }`). Data override fields: `api`, `solver`, `swaps`, `bridge`, `dex`, `moneyMarket`, `hub`, `relay`, `fee`, plus the client-side `logger` sink. See `sodax-sdk`: `migration-v1-to-v2/knowledge/breaking-changes/architecture.md` for the SDK-side reshape.
31
31
 
32
32
  **Recommended pairing**: replace `new QueryClient()` with `createSodaxQueryClient()` for global mutation observability.
33
33
 
@@ -81,7 +81,7 @@ Full SDK-level detail: `sodax-sdk`: `migration-v1-to-v2/knowledge/breaking-chang
81
81
 
82
82
  ## `SodaxConfig` reshape — `rpcConfig` → `chains`
83
83
 
84
- `SodaxProvider`'s config prop is `DeepPartial<SodaxConfig>`. The SDK renamed/restructured the config:
84
+ `SodaxProvider`'s config prop is `SodaxOptions` (`= DeepPartial<SodaxConfig> & { logger? }`). The SDK renamed/restructured the config:
85
85
 
86
86
  ```diff
87
87
  - <SodaxProvider rpcConfig={{
@@ -33,7 +33,7 @@ For each feature your app uses, walk the matching `migration/features/<feature>.
33
33
  - [ ] [`features/bridge.md`](features/bridge.md) — bridge.
34
34
  - [ ] [`features/dex.md`](features/dex.md) — deposit / supply liquidity / etc.
35
35
  - [ ] [`features/migration.md`](features/migration.md) — ICX / bnUSD / BALN. **Note:** v1's `useMigrate(spokeProvider)` is gone; v2 has 6 per-action hooks.
36
- - [ ] [`features/bitcoin.md`](features/bitcoin.md) — Radfi.
36
+ - [ ] [`features/bitcoin.md`](features/bitcoin.md) — Bound Exchange.
37
37
  - [ ] [`features/auxiliary-services.md`](features/auxiliary-services.md) — partner, recovery, backend queries, shared utilities.
38
38
 
39
39
  The cross-cutting pattern at every call site:
@@ -10,7 +10,7 @@ Per-feature porting playbooks. Each file shows the v1 → v2 delta for one featu
10
10
  | [`bridge.md`](bridge.md) | Field renames in `useBridge` params (`srcChainId` → `srcChainKey`, `recipient` → `dstAddress`). `useGetBridgeableAmount` shape change. |
11
11
  | [`dex.md`](dex.md) | Two-step flow stayed the same; field renames + `srcChainKey` requirement. `useSupplyLiquidity` mint/increase routing. |
12
12
  | [`migration.md`](migration.md) | **Biggest change**: v1's `useMigrate(spokeProvider)` → 6 per-action hooks. |
13
- | [`bitcoin.md`](bitcoin.md) | Radfi flow shapes are mostly unchanged; provider/session lifecycle hooks tightened. |
13
+ | [`bitcoin.md`](bitcoin.md) | Bound Exchange flow shapes are mostly unchanged; provider/session lifecycle hooks tightened. |
14
14
  | [`auxiliary-services.md`](auxiliary-services.md) | Partner / recovery / backend queries / shared utilities — small per-hook changes. |
15
15
 
16
16
  ## Pair-completeness
@@ -1,8 +1,8 @@
1
- # Bitcoin (Radfi) migration — v1 → v2 (dapp-kit)
1
+ # Bitcoin (Bound Exchange) migration — v1 → v2 (dapp-kit)
2
2
 
3
3
  Pair: [`features/bitcoin.md`](../../../integration/knowledge/features/bitcoin.md).
4
4
 
5
- Bitcoin / Radfi hook surface was new in v1 with limited adoption; v2 standardizes its shapes against the canonical hook conventions. Treat the v2 forms below as the canonical reference — if your v1 code used a different shape, swap to v2's directly.
5
+ Bitcoin / Bound Exchange hook surface was new in v1 with limited adoption; v2 standardizes its shapes against the canonical hook conventions. Treat the v2 forms below as the canonical reference — if your v1 code used a different shape, swap to v2's directly.
6
6
 
7
7
  ## TL;DR
8
8
 
@@ -59,7 +59,7 @@ These keep their name but their TypeScript signature is different. Usually requi
59
59
  | `useTradingWalletBalance` | Return is `RadfiWalletBalance = { btcSatoshi, pendingSatoshi, externalPendingSatoshi, totalUtxos }` — NOT `{ confirmed, pending }`. |
60
60
  | `useExpiredUtxos` | Return is `RadfiUtxo[]` (with `_id`, `txid`, `vout`, `txidVout`, `satoshi`, `amount`, `address`, `isSpent`, `status`, `source`, optional `runes`). Both this and `useTradingWalletBalance` take `{ params: { walletProvider, tradingAddress } }`. |
61
61
  | `useRadfiAuth` | TData is `RadfiAuthResult = { accessToken, refreshToken, tradingAddress }` (3 fields — `publicKey` is persisted to localStorage but NOT returned). |
62
- | All Bitcoin/Radfi mutations | Standard pattern — drop hook-init args; pass through `mutate(vars)`. |
62
+ | All Bitcoin/Bound Exchange mutations | Standard pattern — drop hook-init args; pass through `mutate(vars)`. |
63
63
 
64
64
  ## Cross-references
65
65
 
@@ -72,7 +72,7 @@ Follow in order. Skipping `ai-rules.md` is the most common cause of agents rever
72
72
  1. Read [`integration/knowledge/ai-rules.md`](./integration/knowledge/ai-rules.md) — DO / DO NOT / workflow / stop conditions.
73
73
  2. Read [`integration/knowledge/quickstart.md`](./integration/knowledge/quickstart.md) — install, initialize, first-run troubleshooting.
74
74
  3. For your feature, read [`integration/knowledge/features/`](./integration/knowledge/features/) — `swap.md`, `money-market.md`, `staking.md`, `bridge.md`, `dex.md`, `migration.md`, `partner.md`, `recovery.md`, `backend-api.md`.
75
- 4. For specific patterns (init, raw vs signed, chain narrowing, gas, testing, errors), read [`integration/knowledge/recipes/`](./integration/knowledge/recipes/).
75
+ 4. For specific patterns (init, raw vs signed, chain narrowing, gas, testing, errors, logging), read [`integration/knowledge/recipes/`](./integration/knowledge/recipes/).
76
76
  5. Lookups (chain keys, error codes, public API surface, wallet provider types, glossary) → [`integration/knowledge/reference/`](./integration/knowledge/reference/).
77
77
  6. Non-EVM quirks (Stellar trustline, BTC PSBT, Solana PDA, ICON, NEAR) → [`integration/knowledge/chain-specifics.md`](./integration/knowledge/chain-specifics.md).
78
78
 
@@ -19,7 +19,7 @@ This tree documents v2 of the SDK for **new consumers** building against it. If
19
19
  | [`features/backend-api.md`](features/backend-api.md) | `BackendApiService` — HTTP client for backend reads + swap-tx submission. |
20
20
  | [`recipes/`](recipes/) | Copy-pasteable patterns: SDK initialization, `Result` + error discrimination, raw-tx flow, signed-tx flow, chain-key narrowing + cast-at-boundary, testing (mocks/stubs), gas estimation, backend-server init. |
21
21
  | [`reference/`](reference/) | Lookup tables: 20-chain `ChainKeys` table with family + relay id, `I*WalletProvider` interfaces, 13 `SodaxErrorCode` meanings + per-feature narrow unions, public API surface (incl. `@sodax/types` re-export rule), glossary. |
22
- | [`chain-specifics.md`](chain-specifics.md) | Non-EVM quirks — Stellar trustline check/request, Bitcoin PSBT + Radfi auth/session, Solana PDA derivation, ICON Hana wallet + chain-key string, NEAR connector discovery. |
22
+ | [`chain-specifics.md`](chain-specifics.md) | Non-EVM quirks — Stellar trustline check/request, Bitcoin PSBT + Bound Exchange auth/session, Solana PDA derivation, ICON Hana wallet + chain-key string, NEAR connector discovery. |
23
23
 
24
24
  ## Reading order for a new integrator
25
25
 
@@ -95,7 +95,7 @@ The chain key is the bridge between the type system and runtime routing.
95
95
  The `Sodax` class is the public entry point. It constructs and wires every service once at construction time, then reuses them across calls:
96
96
 
97
97
  ```ts
98
- const sodax = new Sodax(/* optional DeepPartial<SodaxConfig> */);
98
+ const sodax = new Sodax(/* optional SodaxOptions */);
99
99
  await sodax.config.initialize(); // fetch dynamic config; fall back to packaged defaults
100
100
 
101
101
  // All feature services accessed off the instance:
@@ -127,11 +127,13 @@ All feature services receive `{ hubProvider, config, spoke }` via constructor in
127
127
  ### Constructor
128
128
 
129
129
  ```ts
130
- import { Sodax, type SodaxConfig, type DeepPartial } from '@sodax/sdk';
130
+ import { Sodax, type SodaxOptions } from '@sodax/sdk';
131
131
 
132
- new Sodax(config?: DeepPartial<SodaxConfig>): Sodax;
132
+ new Sodax(config?: SodaxOptions): Sodax;
133
133
  ```
134
134
 
135
+ `SodaxOptions` is `DeepPartial<SodaxConfig> & { logger?: SodaxLoggerOption }` — a deep-partial override of the `SodaxConfig` data contract, plus the client-side `logger` sink (kept off `SodaxConfig` itself; see [`recipes/logging.md`](recipes/logging.md)).
136
+
135
137
  `SodaxConfig` has exactly **10 fields** (all required at the type level, but `DeepPartial` makes every leaf optional):
136
138
 
137
139
  - `fee: PartnerFee | undefined` — global partner fee, applied unless a feature-level config overrides.
@@ -5,7 +5,7 @@ EVM chains (12 of them) work uniformly through `IEvmWalletProvider`. Non-EVM cha
5
5
  ## Section index
6
6
 
7
7
  1. [Stellar trustline](#1-stellar-trustline) — `STELLAR_MAINNET`. Required before receiving any non-XLM asset.
8
- 2. [Bitcoin PSBT and Radfi](#2-bitcoin-psbt-and-radfi) — `BITCOIN_MAINNET`. PSBT signing; trading wallet; Radfi auth/session.
8
+ 2. [Bitcoin PSBT and Bound Exchange](#2-bitcoin-psbt-and-radfi) — `BITCOIN_MAINNET`. PSBT signing; trading wallet; Bound Exchange auth/session.
9
9
  3. [Solana PDA derivation](#3-solana-pda-derivation) — `SOLANA_MAINNET`. Deterministic addresses; one-time setup utilities.
10
10
  4. [ICON Hana wallet](#4-icon-hana-wallet) — `ICON_MAINNET`. Low-level Hana-extension helpers; chain key string vs numeric ID.
11
11
  5. [NEAR connector discovery](#5-near-connector-discovery) — `NEAR_MAINNET`. Account-id semantics; multiple wallet variants.
@@ -39,7 +39,7 @@ Stellar accounts that have never held the asset have **no** trustline — receiv
39
39
 
40
40
  ---
41
41
 
42
- ## 2. Bitcoin PSBT and Radfi
42
+ ## 2. Bitcoin PSBT and Bound Exchange
43
43
 
44
44
  Bitcoin support uses Partially Signed Bitcoin Transactions (PSBT) — a different model than EVM tx signing. The wallet provider (`BTCWalletProvider`) handles PSBT construction and signing internally.
45
45
 
@@ -51,11 +51,11 @@ type BtcAddressType = 'P2PKH' | 'P2SH' | 'P2WPKH' | 'P2TR';
51
51
 
52
52
  `BTCWalletProvider` config takes an `addressType`. `'P2WPKH'` (native SegWit) is the modern default; `'P2TR'` (Taproot) is supported but may have lower compatibility with some on-chain logic.
53
53
 
54
- ### Radfi (auth + trading wallet)
54
+ ### Bound Exchange (auth + trading wallet)
55
55
 
56
- SODAX uses the Radfi infrastructure for Bitcoin. Each user gets a derived "trading wallet" funded from their main BTC address. Operations consume UTXOs from the trading wallet rather than directly from the user's main address.
56
+ SODAX uses the Bound Exchange infrastructure for Bitcoin. Each user gets a derived "trading wallet" funded from their main BTC address. Operations consume UTXOs from the trading wallet rather than directly from the user's main address.
57
57
 
58
- The Radfi provider is owned by `BitcoinSpokeService`. Reach it via the spoke router:
58
+ The Bound Exchange provider is owned by `BitcoinSpokeService`. Reach it via the spoke router:
59
59
 
60
60
  ```ts
61
61
  import { ChainKeys, type BitcoinSpokeService } from '@sodax/sdk';
@@ -64,10 +64,10 @@ const btcSpoke = sodax.spoke.getSpokeService(ChainKeys.BITCOIN_MAINNET) as Bitco
64
64
  const radfi = btcSpoke.radfi; // RadfiProvider instance
65
65
  ```
66
66
 
67
- Most consumer flows don't need to touch `radfi` directly — `sodax.bridge.bridge(...)`, `sodax.swaps.createIntent(...)`, etc. handle the Radfi auth + trading-wallet routing internally on the Bitcoin path. For explicit lifecycle management:
67
+ Most consumer flows don't need to touch `radfi` directly — `sodax.bridge.bridge(...)`, `sodax.swaps.createIntent(...)`, etc. handle the Bound Exchange auth + trading-wallet routing internally on the Bitcoin path. For explicit lifecycle management:
68
68
 
69
69
  ```ts
70
- // Authenticate against Radfi (the wallet signs an auth message):
70
+ // Authenticate against Bound Exchange (the wallet signs an auth message):
71
71
  await radfi.authenticateWithWallet(/* args per RadfiProvider source */);
72
72
 
73
73
  // Fetch the trading wallet for an address (creating it if needed):
@@ -84,7 +84,7 @@ Other public methods on `RadfiProvider` you may need: `setRadfiAccessToken`, `re
84
84
 
85
85
  ### Pitfall
86
86
 
87
- `BitcoinSpokeService.radfi` is what feature services use under the hood. Bypassing the feature services and driving Radfi yourself works but is rarely needed — and you have to wire token balances + UTXO state manually. Prefer the standard feature flows unless you specifically need lifecycle control.
87
+ `BitcoinSpokeService.radfi` is what feature services use under the hood. Bypassing the feature services and driving Bound Exchange yourself works but is rarely needed — and you have to wire token balances + UTXO state manually. Prefer the standard feature flows unless you specifically need lifecycle control.
88
88
 
89
89
  ---
90
90
 
@@ -168,6 +168,31 @@ Both are valid. `GetAddressType<typeof ChainKeys.NEAR_MAINNET>` accepts both via
168
168
 
169
169
  `NearWalletProvider` requires the `accountId` field at construction (alongside `privateKey`). Unlike EVM, NEAR can't derive an account from a key alone — keys are scoped to accounts.
170
170
 
171
+ ### Receiving tokens: NEP-141 storage registration
172
+
173
+ Before a NEAR account can **receive** (hold a balance of) a NEP-141 token, it must pay a one-time storage bond on that token contract — delivering to an unregistered account fails. This gates any flow that delivers a token to a user on NEAR: swap output on NEAR, bridge into NEAR, money-market borrow/withdraw to NEAR. (Native NEAR is not a NEP-141 token and needs no registration.)
174
+
175
+ The NEAR spoke service exposes two methods — reach it via `sodax.spoke.near` or `sodax.spoke.getSpokeService(ChainKeys.NEAR_MAINNET)`:
176
+
177
+ - `isStorageRegistered(token, accountId): Promise<boolean>` — whether `accountId` can already receive `token`. Returns `true` for the native token (no NEP-141). Read-only; uses the configured NEAR RPC.
178
+ - `registerStorage({ token, accountId, walletProvider, deposit?, raw? })` — submits a `storage_deposit` for `accountId`; returns the tx hash (or the unsigned tx when `raw: true`). `deposit` defaults to `NEAR_STORAGE_DEPOSIT` (0.00125 NEAR, exported from `@sodax/sdk`) — override per token if its `storage_balance_bounds.min` differs. Throws for the native token. The recipient's NEAR wallet signs it.
179
+
180
+ Gate pattern, run before delivering to NEAR:
181
+
182
+ ```ts
183
+ // @ai-snippets-skip — illustrative.
184
+ const near = sodax.spoke.near;
185
+ if (!(await near.isStorageRegistered(token, accountId))) {
186
+ await near.registerStorage({ token, accountId, walletProvider });
187
+ }
188
+ ```
189
+
190
+ The `sodax-dapp-kit` skill wraps these as the `useNearStorageCheck` / `useRegisterNearStorage` hooks plus a `resolveNearStorageGate` helper (integration mode).
191
+
192
+ ### `ft_transfer_call` attaches 1 yoctoNEAR
193
+
194
+ Deposits **from** NEAR (`deposit()` / `fillIntent()` on the NEAR spoke service) call the token's `ft_transfer_call`, which per NEP-141 must carry **exactly 1 yoctoNEAR** — the spoke service attaches it automatically. The signer only needs a small NEAR balance to cover gas (the 1 yoctoNEAR is dust). Native-token deposits use a plain `transfer` and aren't subject to this.
195
+
171
196
  ---
172
197
 
173
198
  ## Other non-EVM chains
@@ -169,4 +169,5 @@ if (result.ok) {
169
169
 
170
170
  - v1 → v2 bridge migration: [`features/bridge.md`](../../../migration-v1-to-v2/knowledge/features/bridge.md).
171
171
  - Stellar destinations need a trustline first: [`../chain-specifics.md`](../chain-specifics.md).
172
+ - NEAR destinations need NEP-141 storage registration first: [`../chain-specifics.md`](../chain-specifics.md).
172
173
  - Hub-and-spoke vault architecture: [`../architecture.md`](../architecture.md) § 1.
@@ -196,3 +196,4 @@ Aave's RAY precision (27 decimals) is used for interest calculations under the h
196
196
  - v1 → v2 money market migration: [`features/money-market.md`](../../../migration-v1-to-v2/knowledge/features/money-market.md).
197
197
  - Architecture (hub-side wallet abstraction, ConfigService): [`../architecture.md`](../architecture.md) §§ 3, 4.
198
198
  - Stellar destinations need a trustline: [`../chain-specifics.md`](../chain-specifics.md).
199
+ - NEAR destinations (borrow/withdraw to NEAR) need NEP-141 storage registration: [`../chain-specifics.md`](../chain-specifics.md).
@@ -271,3 +271,4 @@ Solver-specific context on `EXTERNAL_API_ERROR`:
271
271
  - v1 → v2 swap migration: [`features/swap.md`](../../../migration-v1-to-v2/knowledge/features/swap.md).
272
272
  - Error model: [`../architecture.md`](../architecture.md) § 8 and [`../reference/`](../reference/) § 3.
273
273
  - Stellar destinations require a trustline first: [`../chain-specifics.md`](../chain-specifics.md) § "Stellar trustline".
274
+ - NEAR destinations require NEP-141 storage registration first: [`../chain-specifics.md`](../chain-specifics.md) § "Receiving tokens: NEP-141 storage registration".
@@ -6,6 +6,7 @@ Copy-pasteable patterns for the most common Core SDK consumer tasks. Each file i
6
6
  |---|---|
7
7
  | [`initialize-sodax.md`](initialize-sodax.md) | App startup. `new Sodax()` + `await sodax.config.initialize()`, with and without config override. |
8
8
  | [`result-and-errors.md`](result-and-errors.md) | Branching on `result.ok`, switching on `(error.feature, error.code)`, `isSodaxError` over `instanceof`, sub-Result propagation, logger integration. |
9
+ | [`logging.md`](logging.md) | Select a `SodaxLogger` (`'console'` / `'silent'` / custom sink) at construction; forward SDK diagnostics to Sentry / Pino / Datadog. |
9
10
  | [`signed-tx-flow.md`](signed-tx-flow.md) | `raw: false` + chain-narrowed `walletProvider`. Returns tx hash (or `TxHashPair` for cross-chain mutations). |
10
11
  | [`raw-tx-flow.md`](raw-tx-flow.md) | `raw: true`. Builds an unsigned chain-specific payload; you sign elsewhere. |
11
12
  | [`chain-key-narrowing.md`](chain-key-narrowing.md) | Cast-at-boundary pattern; runtime `chainType` discriminant. |
@@ -15,9 +15,10 @@ const result = sodax.config.isValidSpokeChainKey(ChainKeys.ARBITRUM_MAINNET);
15
15
  ### With config override
16
16
 
17
17
  ```ts
18
- import { Sodax, ChainKeys, type SodaxConfig, type DeepPartial } from '@sodax/sdk';
18
+ import { Sodax, ChainKeys, type SodaxOptions } from '@sodax/sdk';
19
19
 
20
- const config: DeepPartial<SodaxConfig> = {
20
+ // `SodaxOptions` = `DeepPartial<SodaxConfig>` (the data override) plus the client-side `logger` option.
21
+ const config: SodaxOptions = {
21
22
  // Per-chain overrides — merged with packaged defaults at the field level.
22
23
  chains: {
23
24
  [ChainKeys.SONIC_MAINNET]: { rpcUrl: process.env.SONIC_RPC_URL },
@@ -31,6 +32,8 @@ const config: DeepPartial<SodaxConfig> = {
31
32
  solver: {
32
33
  solverApiEndpoint: 'https://my-solver.example.com',
33
34
  },
35
+ // SDK log sink: 'console' (default) | 'silent' | a custom SodaxLogger. See logging.md.
36
+ logger: 'silent',
34
37
  };
35
38
 
36
39
  const sodax = new Sodax(config);
@@ -74,5 +77,6 @@ export const SUPPORTED_TOKENS_PER_CHAIN = sodaxConfig.swaps.supportedTokens;
74
77
  ## Cross-references
75
78
 
76
79
  - [`README.md`](README.md) — recipe index.
80
+ - [`logging.md`](logging.md) — the `logger` constructor option in depth (presets + custom sinks).
77
81
  - [`../architecture.md`](../architecture.md) — concepts behind these patterns.
78
82
  - [`../reference/`](../reference/) — chain keys, error codes, public API surface.
@@ -0,0 +1,102 @@
1
+ # Logging
2
+
3
+ The SDK routes all of its internal diagnostics through a single `SodaxLogger` instead of calling
4
+ `console.*` directly. Pick a preset or forward to a structured sink (Sentry, Pino, Datadog, …) without
5
+ patching globals.
6
+
7
+ ## Select a logger at construction
8
+
9
+ `logger` is a `Sodax` constructor option — a field on `SodaxOptions` (the constructor's parameter
10
+ type), kept off the `SodaxConfig` data contract because it is a client-side sink, not backend-fetched
11
+ config. It accepts a preset name or a custom implementation:
12
+
13
+ ```ts
14
+ import { Sodax } from '@sodax/sdk';
15
+
16
+ new Sodax(); // default — same as { logger: 'console' }
17
+ new Sodax({ logger: 'console' }); // mirror console.* output
18
+ new Sodax({ logger: 'silent' }); // drop all SDK logs
19
+ ```
20
+
21
+ The logger is resolved **once** at construction and held on `ConfigService` outside the swappable
22
+ dynamic config (read it back as `sodax.config.logger`). `await sodax.config.initialize()` fetches fresh
23
+ chain config from the backend but **never** replaces the logger — the backend cannot set or overwrite
24
+ it.
25
+
26
+ > Combine with the other constructor options freely — pass `logger` alongside `chains` / `api` /
27
+ > `solver` overrides in the same object. The constructor splits `logger` off the data override before
28
+ > merging, so it never lands in `sodax.instanceConfig`. See [`initialize-sodax.md`](initialize-sodax.md).
29
+
30
+ ## Custom sink
31
+
32
+ Implement the `SodaxLogger` interface and pass the instance:
33
+
34
+ ```ts
35
+ import { Sodax, type SodaxLogger } from '@sodax/sdk';
36
+
37
+ const sentryLogger: SodaxLogger = {
38
+ debug: (message, data) => Sentry.addBreadcrumb({ level: 'debug', message, data }),
39
+ info: (message, data) => Sentry.addBreadcrumb({ level: 'info', message, data }),
40
+ warn: (message, data) => Sentry.captureMessage(message, { level: 'warning', extra: data }),
41
+ error: (message, error, data) =>
42
+ error !== undefined
43
+ ? Sentry.captureException(error, { extra: { message, ...data } })
44
+ : Sentry.captureMessage(message, { level: 'error', extra: data }),
45
+ };
46
+
47
+ const sodax = new Sodax({ logger: sentryLogger });
48
+ ```
49
+
50
+ ## Interface
51
+
52
+ ```ts
53
+ import type { SodaxLogger, SodaxLoggerOption } from '@sodax/sdk';
54
+
55
+ // SodaxLogger:
56
+ // debug(message: string, data?: Record<string, unknown>): void;
57
+ // info(message: string, data?: Record<string, unknown>): void;
58
+ // warn(message: string, data?: Record<string, unknown>): void;
59
+ // error(message: string, error?: unknown, data?: Record<string, unknown>): void;
60
+ //
61
+ // SodaxLoggerOption = SodaxLogger | 'console' | 'silent';
62
+ ```
63
+
64
+ `error()` takes the thrown value as a separate second argument (before structured `data`) so adapters
65
+ can attach it as the exception — `warn` / `info` / `debug` take only `(message, data?)`. SDK errors are
66
+ `SodaxError` instances; their `toJSON()` is the canonical serialization surface (`JSON.stringify(error)`
67
+ invokes it automatically). For routing a failed `Result<T>`'s `error` into a sink, see the **Logging**
68
+ section of [`result-and-errors.md`](result-and-errors.md).
69
+
70
+ ## Built-in loggers and resolver
71
+
72
+ `consoleLogger`, `silentLogger`, and `resolveLogger(option)` are exported from `@sodax/sdk` for
73
+ composition — e.g. wrapping the console logger to add a prefix, or resolving a preset name yourself:
74
+
75
+ ```ts
76
+ import { consoleLogger, resolveLogger, type SodaxLogger } from '@sodax/sdk';
77
+
78
+ // Wrap the default to add a prefix
79
+ const prefixed: SodaxLogger = {
80
+ debug: (m, d) => consoleLogger.debug(`[sodax] ${m}`, d),
81
+ info: (m, d) => consoleLogger.info(`[sodax] ${m}`, d),
82
+ warn: (m, d) => consoleLogger.warn(`[sodax] ${m}`, d),
83
+ error: (m, e, d) => consoleLogger.error(`[sodax] ${m}`, e, d),
84
+ };
85
+
86
+ // Resolve a preset name to a concrete logger
87
+ const resolved = resolveLogger('silent'); // → silentLogger
88
+ ```
89
+
90
+ ## Coverage
91
+
92
+ All instance feature services (swap, bridge, money market, DEX, staking, partner, recovery, the spoke
93
+ services), `BackendApiService`, and the solver API client route through the configured logger. A small
94
+ number of pure utility / static-helper functions still call `console.*` directly because they have no
95
+ access to the instance logger.
96
+
97
+ ## Cross-references
98
+
99
+ - [`README.md`](README.md) — recipe index.
100
+ - [`initialize-sodax.md`](initialize-sodax.md) — the constructor options object `logger` lives in.
101
+ - [`result-and-errors.md`](result-and-errors.md) — routing a failed `Result<T>` into a logging sink.
102
+ - [`../reference/public-api.md`](../reference/public-api.md) — exported logger symbols.
@@ -8,9 +8,17 @@ Import everything from `@sodax/sdk`. The barrel re-exports the entire `@sodax/ty
8
8
  import {
9
9
  // Main entry
10
10
  Sodax,
11
+ type SodaxOptions, // constructor param: DeepPartial<SodaxConfig> & { logger? }
11
12
  type SodaxConfig,
12
13
  type DeepPartial,
13
14
 
15
+ // Logging (see recipes/logging.md)
16
+ type SodaxLogger,
17
+ type SodaxLoggerOption,
18
+ consoleLogger,
19
+ silentLogger,
20
+ resolveLogger,
21
+
14
22
  // Chain keys + narrowing
15
23
  ChainKeys,
16
24
  type ChainKey,