@sodax/dapp-kit 2.0.0-rc.2 → 2.0.0-rc.3
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.
- package/ai-exported/integration/ai-rules.md +1 -0
- package/ai-exported/integration/architecture.md +2 -0
- package/ai-exported/integration/features/money-market.md +30 -2
- package/ai-exported/integration/recipes/money-market.md +5 -4
- package/ai-exported/integration/recipes/setup.md +24 -0
- package/ai-exported/integration/recipes/wallet-connectivity.md +27 -0
- package/ai-exported/integration/reference/glossary.md +2 -0
- package/ai-exported/migration/breaking-changes/sdk-leakage.md +2 -0
- package/ai-exported/migration/features/bridge.md +50 -13
- package/ai-exported/migration/features/money-market.md +45 -3
- package/ai-exported/migration/features/swap.md +17 -2
- package/ai-exported/migration/recipes.md +11 -14
- package/ai-exported/migration/reference/renamed-hooks.md +2 -0
- package/dist/index.cjs +3 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.mjs +3 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
- package/src/providers/SodaxProvider.tsx +15 -11
|
@@ -39,6 +39,7 @@ DO / DO NOT / workflow / stop conditions for AI agents writing v2 dapp-kit code.
|
|
|
39
39
|
- **DO NOT** recreate the v1 `invalidateMmQueries(...)` utility or anything similar. Each mutation hook invalidates the relevant keys in its own `onSuccess`. Add cross-feature invalidations via consumer `onSuccess`.
|
|
40
40
|
- **DO NOT** destructure cross-chain mutation results as arrays — `[a, b] = result.value` is wrong. The shape is `TxHashPair = { srcChainTxHash, dstChainTxHash }` (object). This applies to `useBridge`, `useStake`/`useUnstake`/etc., `useDexDeposit`/`useDexWithdraw`, all four MM mutations, and all four migration mutations.
|
|
41
41
|
- **DO NOT** use legacy chain-id constants (`BSC_MAINNET_CHAIN_ID`, etc.). They're gone in v2 — use `ChainKeys.X_MAINNET`.
|
|
42
|
+
- **DO NOT** reach for `as any` / `as IEvmWalletProvider` casts when wiring `useWalletProvider({ xChainId })` into mutation `mutate(vars)`. v2 supports the broad-union case structurally; the cast is not needed. See `recipes/wallet-connectivity.md` § "No type cast is needed".
|
|
42
43
|
|
|
43
44
|
## Stop conditions (defer to user)
|
|
44
45
|
|
|
@@ -33,6 +33,8 @@ Every v2 design concept the hooks rest on, in one TOC-navigable file. Read it on
|
|
|
33
33
|
|
|
34
34
|
`SodaxProvider` does **not** depend on `@sodax/wallet-sdk-react` — wallet state is wired side-by-side. Backend / non-React consumers (Node scripts, bots) bypass dapp-kit entirely and use `@sodax/sdk` directly with their own wallet implementation.
|
|
35
35
|
|
|
36
|
+
**Config reactivity.** `config` is tracked by reference - see [`recipes/setup.md § Config reactivity`](recipes/setup.md#config-reactivity) for the module-const vs `useMemo` patterns.
|
|
37
|
+
|
|
36
38
|
### `createSodaxQueryClient`
|
|
37
39
|
|
|
38
40
|
Returns a `QueryClient` pre-wired with a `MutationCache.onError` hook for global mutation observability. Default behavior: logs every mutation failure to console as `[sodax] Mutation error: <error>`.
|
|
@@ -29,10 +29,38 @@ useUserFormattedSummary({ params, queryOptions }); // Health factor, collate
|
|
|
29
29
|
useUserReservesData({ params, queryOptions }); // Per-reserve user position
|
|
30
30
|
|
|
31
31
|
// aTokens
|
|
32
|
-
useAToken({ params, queryOptions });
|
|
33
|
-
useATokensBalances({ params, queryOptions });
|
|
32
|
+
useAToken({ params: { aToken }, queryOptions }); // aToken metadata (single)
|
|
33
|
+
useATokensBalances({ params: { aTokens, spokeChainKey, userAddress }, queryOptions }); // aToken balances (batched multicall)
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### Read-hook param shapes
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// @ai-snippets-skip
|
|
40
|
+
// useUserFormattedSummary / useUserReservesData — user-position queries
|
|
41
|
+
type UseUserFormattedSummaryParams = ReadHookParams<FormatUserSummaryResponse, {
|
|
42
|
+
spokeChainKey: SpokeChainKey | undefined;
|
|
43
|
+
userAddress: string | undefined;
|
|
44
|
+
}>;
|
|
45
|
+
// Same shape on useUserReservesData. The hook derives the hub wallet from (spokeChainKey, userAddress) internally.
|
|
46
|
+
|
|
47
|
+
// useAToken — single aToken metadata; FLAT (no chain/user fields)
|
|
48
|
+
type UseATokenParams = ReadHookParams<ATokenData, {
|
|
49
|
+
aToken: Address | string | undefined;
|
|
50
|
+
}>;
|
|
51
|
+
// ATokenData = Erc20Token & { chainKey: ChainKey }
|
|
52
|
+
|
|
53
|
+
// useATokensBalances — batched aToken balances
|
|
54
|
+
type UseATokensBalancesParams = ReadHookParams<Map<Address, bigint>, {
|
|
55
|
+
aTokens: readonly Address[];
|
|
56
|
+
spokeChainKey: SpokeChainKey | undefined;
|
|
57
|
+
userAddress: string | undefined;
|
|
58
|
+
}>;
|
|
59
|
+
// Returns a Map keyed by aToken address. The hook resolves the hub wallet from (spokeChainKey, userAddress).
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
> **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
|
+
|
|
36
64
|
## Mutation params
|
|
37
65
|
|
|
38
66
|
All four user mutations share the same TVars shape (only the `action` literal differs):
|
|
@@ -191,16 +191,17 @@ type MoneyMarketSupplyParams<K extends SpokeChainKey = SpokeChainKey> = {
|
|
|
191
191
|
token: string;
|
|
192
192
|
amount: bigint;
|
|
193
193
|
action: 'supply';
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
dstChainKey?: SpokeChainKey;
|
|
195
|
+
dstAddress?: string;
|
|
196
196
|
};
|
|
197
197
|
|
|
198
|
-
// Borrow / Withdraw / Repay follow the same shape with their respective `action` literal
|
|
198
|
+
// Borrow / Withdraw / Repay follow the same shape with their respective `action` literal
|
|
199
|
+
// (`'borrow'` / `'withdraw'` / `'repay'`) and the same `src*` / `dst*` field names.
|
|
199
200
|
```
|
|
200
201
|
|
|
201
202
|
## Notes
|
|
202
203
|
|
|
203
204
|
- **Borrow/withdraw skip approval** — `useMMAllowance` returns `true` automatically for these actions.
|
|
204
205
|
- **Health factor < 1.0** means liquidation risk.
|
|
205
|
-
- All operations
|
|
206
|
+
- All four operations (supply / borrow / withdraw / repay) accept optional `dstChainKey` / `dstAddress` for cross-chain delivery — omit both for same-chain. There are no `from*` / `to*` field variants on any action.
|
|
206
207
|
- Mutations throw on SDK failure — use `mutateAsyncSafe` for `Result<T>` ergonomics without `try/catch`.
|
|
@@ -47,6 +47,30 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
### Config reactivity
|
|
51
|
+
|
|
52
|
+
`SodaxProvider`'s `config` prop is tracked by **reference**, not by value. The SDK is re-instantiated whenever the prop identity changes - resetting wagmi connection state, in-flight RPC, and any persisted state inside `useSodaxContext` consumers. Choose the pattern that matches your config source:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
// @ai-snippets-skip — illustrative; uses placeholder values + JSX without surrounding imports
|
|
56
|
+
// ✅ Static config — module constant (preferred when nothing depends on runtime state).
|
|
57
|
+
const sodaxConfig: DeepPartial<SodaxConfig> = {
|
|
58
|
+
chains: { [ChainKeys.SONIC_MAINNET]: { rpcUrl: '...' } },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// ✅ Runtime-switchable config — useMemo with explicit deps.
|
|
62
|
+
// Re-runs only when listed deps change, so the SDK survives unrelated re-renders.
|
|
63
|
+
const sodaxConfig = useMemo(
|
|
64
|
+
() => ({ solver: solverConfigMap[solverEnv], chains: { ... } }),
|
|
65
|
+
[solverEnv], // SDK re-inits when solverEnv switches.
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// ❌ Inline — new identity every parent render, SDK churns on every render.
|
|
69
|
+
<SodaxProvider config={{ chains: { ... } }}>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Drive runtime config switches (solver env, feature flags, etc.) through `useMemo` deps - never remount `SodaxProvider` for them.
|
|
73
|
+
|
|
50
74
|
### Optional: Add Wallet Provider
|
|
51
75
|
|
|
52
76
|
If you want to use `@sodax/wallet-sdk-react` for wallet connectivity, wrap `SodaxWalletProvider` inside `QueryClientProvider`:
|
|
@@ -99,3 +99,30 @@ function SwapButton() {
|
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
This pattern is consistent across all features: `useSwap`, `useBridge`, `useSupply`, `useStake`, `useDexDeposit`, etc.
|
|
102
|
+
|
|
103
|
+
## No type cast is needed — broad-union wiring just works
|
|
104
|
+
|
|
105
|
+
A common anti-pattern is reaching for `as any` / `as IEvmWalletProvider` when the runtime-typed `walletProvider` from `useWalletProvider({ xChainId })` is passed into a mutation hook. **That cast is not needed.** v2 accepts the broad-union wallet-provider type as long as the chain key on the payload and the wallet provider both come from the same runtime `xChainId` value.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
// @ai-snippets-skip — illustrative anti-pattern vs correct
|
|
109
|
+
// ❌ ANTI-PATTERN — unnecessary cast
|
|
110
|
+
const walletProvider = useWalletProvider({ xChainId });
|
|
111
|
+
await swap({ params, walletProvider: walletProvider as any }); // don't
|
|
112
|
+
await swap({ params, walletProvider: walletProvider as IEvmWalletProvider }); // don't
|
|
113
|
+
|
|
114
|
+
// ✅ CORRECT — pass directly, TypeScript infers the relationship
|
|
115
|
+
const walletProvider = useWalletProvider({ xChainId });
|
|
116
|
+
if (!walletProvider) return; // narrow undefined first
|
|
117
|
+
await swap({ params, walletProvider });
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Why this works
|
|
121
|
+
|
|
122
|
+
- `useWalletProvider({ xChainId })` returns `GetWalletProviderType<typeof xChainId> | undefined`. When `xChainId` is a runtime value (e.g. from props/state typed `SpokeChainKey`), the return type is the **broad union** `IWalletProvider | undefined`, not `any`.
|
|
123
|
+
- Mutation hooks like `useSwap<K>()` default `K` to the broad `SpokeChainKey` union. Their `mutate` vars are typed `{ params: SwapParams<SpokeChainKey>, walletProvider: GetWalletProviderType<SpokeChainKey> }` — i.e. `walletProvider` is the same broad union.
|
|
124
|
+
- The two unions are structurally assignable. No cast required.
|
|
125
|
+
|
|
126
|
+
### When the cast is actually needed
|
|
127
|
+
|
|
128
|
+
If you've narrowed `xChainId` to a literal (e.g. via `chainKey === ChainKeys.BSC_MAINNET` checks in a branch) and the mutation hook is also generic-narrowed, you'll get narrower types on both sides. Even there, the cast is usually unnecessary — TypeScript propagates the narrowed `K` through the hook's generic. Reach for a cast only when you can produce a real TS error message proving it's needed.
|
|
@@ -132,6 +132,8 @@ App-level React component. Provides:
|
|
|
132
132
|
|
|
133
133
|
Optional config: `<SodaxProvider config={DeepPartial<SodaxConfig>}>`. Without config, SDK uses packaged defaults.
|
|
134
134
|
|
|
135
|
+
Config is tracked by **reference** - see [`recipes/setup.md § Config reactivity`](../recipes/setup.md#config-reactivity) for module-const vs `useMemo` patterns.
|
|
136
|
+
|
|
135
137
|
### `createSodaxQueryClient`
|
|
136
138
|
|
|
137
139
|
Factory for a `QueryClient` with `MutationCache.onError` pre-wired for global mutation observability. Optional — if you construct your own `QueryClient`, dapp-kit hooks still work; you just don't get the global observability seam.
|
|
@@ -96,6 +96,8 @@ Full SDK-level detail: [`../../../../sdk/ai-exported/migration/breaking-changes/
|
|
|
96
96
|
+ }}>
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
**Config is tracked by reference in v2.** See [`../../integration/recipes/setup.md § Config reactivity`](../../integration/recipes/setup.md#config-reactivity) for the module-const vs `useMemo` patterns. Drive runtime config switches (e.g. solver env) through `useMemo` deps, not by remounting the provider.
|
|
100
|
+
|
|
99
101
|
Other v1 fields renamed or restructured:
|
|
100
102
|
|
|
101
103
|
| v1 | v2 |
|
|
@@ -9,9 +9,11 @@ Pair: [`../../integration/features/bridge.md`](../../integration/features/bridge
|
|
|
9
9
|
1. **`CreateBridgeIntentParams` field renames** (SDK-leakage):
|
|
10
10
|
- `srcChainId` → `srcChainKey`
|
|
11
11
|
- `dstChainId` → `dstChainKey`
|
|
12
|
-
- `
|
|
13
|
-
-
|
|
14
|
-
|
|
12
|
+
- `srcAsset` → `srcToken`
|
|
13
|
+
- `dstAsset` → `dstToken`
|
|
14
|
+
- `recipient` is **unchanged** (stays `recipient` in v2 — it is NOT renamed to `dstAddress`; the only `dstAddress`-shaped field in v2 lives on money-market params, not bridge)
|
|
15
|
+
- **NEW required**: `srcAddress` (the user's spoke-side sender address, distinct from `recipient` which is the destination)
|
|
16
|
+
2. **`bridge()` return shape changed.** v1 was `Promise<string>` (a single tx hash that threw on error). v2 returns `Promise<Result<TxHashPair, BridgeOrchestrationError>>` where `TxHashPair = { srcChainTxHash: string; dstChainTxHash: string }`. **This is the shape at the direct-SDK boundary** (`sodax.bridge.bridge(...)`); the `useBridge` hook does not adapt the inner object, it only unwraps the `Result` wrapper (so the hook surfaces `TxHashPair` directly via `data` / `mutateAsync` / `mutateAsyncSafe`'s `value`). Don't destructure as a tuple — there was no `[spokeTxHash, hubTxHash]` form in either v1 or v2.
|
|
15
17
|
3. **`useGetBridgeableAmount` reshape.** v1 took `(srcChainId, srcAsset, dstChainId, dstAsset)`. v2 takes `{ from: XToken, to: XToken }` — each `XToken` carries its own `chainKey`.
|
|
16
18
|
4. **`useGetBridgeableAmount` return value is `BridgeLimit`, not bare `bigint`.** Access `.value.amount` and `.value.decimals`.
|
|
17
19
|
5. **`useGetBridgeableTokens` is sync now** in the SDK. The hook still returns `UseQueryResult` but the underlying SDK call doesn't fire RPC — it's config-derived.
|
|
@@ -29,7 +31,8 @@ Pair: [`../../integration/features/bridge.md`](../../integration/features/bridge
|
|
|
29
31
|
|
|
30
32
|
const handleBridge = async () => {
|
|
31
33
|
+ if (!walletProvider) return;
|
|
32
|
-
-
|
|
34
|
+
- // v1: single tx hash, throws on failure
|
|
35
|
+
- const txHash: string = await bridge.mutateAsync({
|
|
33
36
|
- params: {
|
|
34
37
|
- srcChainId: BASE_MAINNET_CHAIN_ID,
|
|
35
38
|
- srcAsset: '0x...',
|
|
@@ -39,28 +42,62 @@ Pair: [`../../integration/features/bridge.md`](../../integration/features/bridge
|
|
|
39
42
|
- recipient: '0x...',
|
|
40
43
|
- },
|
|
41
44
|
- });
|
|
42
|
-
- if (result.ok) {
|
|
43
|
-
- const [spokeTxHash, hubTxHash] = result.value;
|
|
44
|
-
- /* ... */
|
|
45
|
-
- }
|
|
46
45
|
+ const result = await bridge({
|
|
47
46
|
+ params: {
|
|
48
47
|
+ srcChainKey: ChainKeys.BASE_MAINNET,
|
|
49
|
-
+ srcAddress, // NEW: required
|
|
50
|
-
+
|
|
48
|
+
+ srcAddress, // NEW: required (your spoke-side sender)
|
|
49
|
+
+ srcToken: '0x...', // RENAMED from `srcAsset`
|
|
51
50
|
+ amount: 1_000_000n,
|
|
52
51
|
+ dstChainKey: ChainKeys.POLYGON_MAINNET,
|
|
53
|
-
+
|
|
54
|
-
+
|
|
52
|
+
+ dstToken: '0x...', // RENAMED from `dstAsset`
|
|
53
|
+
+ recipient: '0x...', // UNCHANGED — destination receiver
|
|
55
54
|
+ },
|
|
56
55
|
+ walletProvider,
|
|
57
56
|
+ });
|
|
58
57
|
+ if (!result.ok) return;
|
|
59
|
-
+ const { srcChainTxHash, dstChainTxHash } = result.value; //
|
|
58
|
+
+ const { srcChainTxHash, dstChainTxHash } = result.value; // TxHashPair object, not [a, b]
|
|
60
59
|
};
|
|
61
60
|
}
|
|
62
61
|
```
|
|
63
62
|
|
|
63
|
+
### Calling `sodax.bridge.bridge()` directly (no hook)
|
|
64
|
+
|
|
65
|
+
If you call the SDK directly inside a custom `useMutation` (instead of `useBridge`), the return shape is **exactly the same** — the dapp-kit hook only unwraps the `Result` wrapper, it does not reshape `TxHashPair`:
|
|
66
|
+
|
|
67
|
+
```diff
|
|
68
|
+
- // v1 direct-SDK call:
|
|
69
|
+
- const txHash: string = await sodax.bridge.bridge({ params: { /* v1 shape */ }, spokeProvider });
|
|
70
|
+
+ // v2 direct-SDK call:
|
|
71
|
+
+ const result = await sodax.bridge.bridge({
|
|
72
|
+
+ params: {
|
|
73
|
+
+ srcChainKey: ChainKeys.BASE_MAINNET,
|
|
74
|
+
+ srcAddress,
|
|
75
|
+
+ srcToken: '0x...',
|
|
76
|
+
+ amount: 1_000_000n,
|
|
77
|
+
+ dstChainKey: ChainKeys.POLYGON_MAINNET,
|
|
78
|
+
+ dstToken: '0x...',
|
|
79
|
+
+ recipient: '0x...',
|
|
80
|
+
+ },
|
|
81
|
+
+ raw: false,
|
|
82
|
+
+ walletProvider,
|
|
83
|
+
+ });
|
|
84
|
+
+ // Type: Result<TxHashPair, BridgeOrchestrationError>
|
|
85
|
+
+ if (!result.ok) {
|
|
86
|
+
+ // result.error is a SodaxError<C> with feature: 'bridge'
|
|
87
|
+
+ return;
|
|
88
|
+
+ }
|
|
89
|
+
+ const { srcChainTxHash, dstChainTxHash } = result.value; // same shape as the hook
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The hook equivalence in code:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// @ai-snippets-skip
|
|
96
|
+
// What `useBridge` does internally (sketch):
|
|
97
|
+
// mutationFn: async vars => unwrapResult(await sodax.bridge.bridge({ ...vars, raw: false }))
|
|
98
|
+
// `unwrapResult` throws on `!ok` and returns `value` on `ok` — that's the only adapter.
|
|
99
|
+
```
|
|
100
|
+
|
|
64
101
|
### `useGetBridgeableAmount` — params + return reshape
|
|
65
102
|
|
|
66
103
|
```diff
|
|
@@ -10,7 +10,7 @@ Pair: [`../../integration/features/money-market.md`](../../integration/features/
|
|
|
10
10
|
2. **`MoneyMarketSupplyParams<K>` (and the four similar action-param types) gained required `srcChainKey` + `srcAddress`** — SDK-leakage.
|
|
11
11
|
3. **`useMMAllowance` auto-skips on-chain checks for borrow/withdraw** — v1 may have had this too, but v2 returns `true` synchronously for those actions instead of issuing a no-op RPC.
|
|
12
12
|
4. **`useMMApprove` returns standard `SafeUseMutationResult`** — `isLoading` → `isPending`.
|
|
13
|
-
5. **
|
|
13
|
+
5. **Position / aToken-balance hooks renamed `address` → `userAddress`.** Applies to `useUserFormattedSummary`, `useUserReservesData`, **and `useATokensBalances`** (which also drops `spokeProvider` and adds a required `spokeChainKey` alongside `aTokens` + `userAddress`). `useAToken` is unaffected — it takes only `{ aToken }`.
|
|
14
14
|
|
|
15
15
|
## Per-method delta
|
|
16
16
|
|
|
@@ -45,7 +45,25 @@ Pair: [`../../integration/features/money-market.md`](../../integration/features/
|
|
|
45
45
|
}
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
`useBorrow`, `useWithdraw`, `useRepay` follow the same pattern with their respective `action` literals (`'borrow'` / `'withdraw'` / `'repay'`).
|
|
48
|
+
`useBorrow`, `useWithdraw`, `useRepay` follow the same pattern with their respective `action` literals (`'borrow'` / `'withdraw'` / `'repay'`). **All four MM action params share an identical field shape** — only the `action` literal differs:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// @ai-snippets-skip
|
|
52
|
+
// MoneyMarketSupplyParams<K> | MoneyMarketBorrowParams<K> | MoneyMarketWithdrawParams<K> | MoneyMarketRepayParams<K>
|
|
53
|
+
{
|
|
54
|
+
srcChainKey: K; // required — where the user signs / funds come from
|
|
55
|
+
srcAddress: string; // required — user's spoke-side address on srcChainKey
|
|
56
|
+
token: string; // token on srcChainKey (supply/repay) or on dstChainKey (borrow/withdraw)
|
|
57
|
+
amount: bigint;
|
|
58
|
+
action: 'supply' | 'borrow' | 'withdraw' | 'repay';
|
|
59
|
+
dstChainKey?: SpokeChainKey; // optional — defaults to srcChainKey (same-chain)
|
|
60
|
+
dstAddress?: string; // optional — defaults to srcAddress (same-chain)
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Cross-chain delivery via `dstChainKey` / `dstAddress` is supported on **all four** actions, not just borrow/repay. Omit both for same-chain operations.
|
|
65
|
+
|
|
66
|
+
> **Porting note** — v2 does NOT use `fromChainKey` / `fromAddress` / `toChainKey` / `toAddress` (or `fromChainId` / `toChainId`) on any MM action. Borrow and repay use the **same** `src*` / `dst*` field names as supply and withdraw — the v2 type system unified the cross-chain shape across all four actions. If your v1 call sites or app types carry `from*` / `to*` naming for the spend-chain vs. debt-chain, rename to `src*` / `dst*` (e.g. `fromChainKey → srcChainKey`, `toChainKey → dstChainKey`). See the SDK migration doc cross-link below for explicit borrow/repay diff examples.
|
|
49
67
|
|
|
50
68
|
### `useMMAllowance` — auto-skip
|
|
51
69
|
|
|
@@ -82,11 +100,35 @@ User-position hooks renamed param fields:
|
|
|
82
100
|
+ const { data } = useUserFormattedSummary({ params: { spokeChainKey, userAddress } });
|
|
83
101
|
```
|
|
84
102
|
|
|
103
|
+
Same shape on `useUserReservesData`.
|
|
104
|
+
|
|
105
|
+
### `useATokensBalances`
|
|
106
|
+
|
|
107
|
+
```diff
|
|
108
|
+
- const { data: balances } = useATokensBalances({ aTokens, spokeProvider, userAddress });
|
|
109
|
+
+ const { data: balances } = useATokensBalances({
|
|
110
|
+
+ params: {
|
|
111
|
+
+ aTokens, // readonly Address[]
|
|
112
|
+
+ spokeChainKey, // SpokeChainKey — NOT `srcChainKey`
|
|
113
|
+
+ userAddress, // string — spoke-side user address (renamed from `address` if you're porting from any earlier shape)
|
|
114
|
+
+ },
|
|
115
|
+
+ });
|
|
116
|
+
+ // data: Map<Address, bigint> | undefined (already unwrapped — hook throws on SDK !ok)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Three things to verify when porting:
|
|
120
|
+
|
|
121
|
+
- `spokeProvider` is gone — the hook derives the hub wallet internally from `(spokeChainKey, userAddress)` via `EvmHubProvider.getUserHubWalletAddress`.
|
|
122
|
+
- The chain-key field is **`spokeChainKey`**, not `srcChainKey`. `src*` names belong to mutation params (`useSupply`/`useBorrow`/etc.) — read hooks for a single-chain position use `spokeChainKey`.
|
|
123
|
+
- The user-address field is **`userAddress`**, not `address` — same rename as `useUserFormattedSummary` and `useUserReservesData`.
|
|
124
|
+
|
|
125
|
+
`useAToken` (metadata-only) is unaffected by the user/chain renames — it takes only `{ aToken }`.
|
|
126
|
+
|
|
85
127
|
## Pitfalls
|
|
86
128
|
|
|
87
129
|
1. **`srcAddress` is the user's spoke-side address**, not the hub address. Hub wallet is derived internally.
|
|
88
130
|
2. **`useMMAllowance` returns `true` instantly for borrow/withdraw** — don't wait on the query state. Branch on `isApproved` directly.
|
|
89
|
-
3. **Cross-chain
|
|
131
|
+
3. **Cross-chain delivery (all four actions)**: omit `dstChainKey` / `dstAddress` for same-chain. Don't pass `dstChainKey === srcChainKey` (let the default kick in). The field names are `src*` / `dst*` on **every** MM action — there is no `from*` / `to*` variant.
|
|
90
132
|
4. **`MoneyMarketSupplyParams<K>` is now generic.** Use `as const` on `srcChainKey` for narrowing: `srcChainKey: ChainKeys.BASE_MAINNET as const`.
|
|
91
133
|
|
|
92
134
|
## Cross-references
|
|
@@ -27,7 +27,9 @@ Pair: [`../../integration/features/swap.md`](../../integration/features/swap.md)
|
|
|
27
27
|
+ if (!walletProvider) return;
|
|
28
28
|
- const result = await swap.mutateAsync({ params: intentParams });
|
|
29
29
|
- if (result.ok) {
|
|
30
|
-
-
|
|
30
|
+
- // v1: result.value was a TUPLE — destructure positionally
|
|
31
|
+
- const [executionResponse] = result.value;
|
|
32
|
+
- const txHash = executionResponse.intent_hash;
|
|
31
33
|
- /* ... */
|
|
32
34
|
- } else {
|
|
33
35
|
- toast.error(result.error.message);
|
|
@@ -37,12 +39,25 @@ Pair: [`../../integration/features/swap.md`](../../integration/features/swap.md)
|
|
|
37
39
|
+ toast.error(result.error instanceof Error ? result.error.message : 'Swap failed');
|
|
38
40
|
+ return;
|
|
39
41
|
+ }
|
|
40
|
-
+
|
|
42
|
+
+ // v2: result.value is a NAMED OBJECT (SwapResponse) — destructure by name
|
|
43
|
+
+ const { solverExecutionResponse, intent, intentDeliveryInfo } = result.value;
|
|
44
|
+
+ const intentHash = solverExecutionResponse.intent_hash; // protocol-level intent id (v1 ≈ executionResponse.intent_hash)
|
|
45
|
+
+ const spokeTxHash = intentDeliveryInfo.srcTxHash; // on-chain tx hash on srcChainKey — use for tx-history display
|
|
41
46
|
+ /* ... */
|
|
42
47
|
};
|
|
43
48
|
}
|
|
44
49
|
```
|
|
45
50
|
|
|
51
|
+
#### `SwapResponse` field map (`result.value`)
|
|
52
|
+
|
|
53
|
+
| Field | Type | Use it for |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `solverExecutionResponse` | `{ answer: 'OK'; intent_hash: Hex }` | Protocol-level intent identifier. **`solverExecutionResponse.intent_hash` is the v2 equivalent of v1's `executionResponse.intent_hash`.** Pass it to `useStatus({ params: { intentTxHash } })` to poll execution status. |
|
|
56
|
+
| `intent` | `Intent` | Intent metadata (`intentId: bigint`, `creator`, tokens, amounts, `srcChain`/`dstChain` as `IntentRelayChainId` bigints). **No transaction hashes on this object** — don't read `intent.intent_hash` (does not exist) or `intent.tx_hash`. |
|
|
57
|
+
| `intentDeliveryInfo` | `{ srcChainKey, srcTxHash, srcAddress, dstChainKey, dstTxHash, dstAddress }` | **On-chain transaction hashes.** `srcTxHash` is the spoke-chain tx on `srcChainKey` (the one that typically feeds a user-facing transaction history); `dstTxHash` is the delivery tx on `dstChainKey`. |
|
|
58
|
+
|
|
59
|
+
**Porting `[executionResponse] = result.value` (v1 tuple) → v2:** rename to `{ solverExecutionResponse } = result.value`. Any `executionResponse.intent_hash` read becomes `solverExecutionResponse.intent_hash`. If the value was being used as the **transaction hash** in a transaction-history row (not as the intent identifier), switch to `intentDeliveryInfo.srcTxHash` instead — `intent_hash` and `srcTxHash` are not interchangeable.
|
|
60
|
+
|
|
46
61
|
### `useSwapApprove` — return shape
|
|
47
62
|
|
|
48
63
|
```diff
|
|
@@ -42,28 +42,25 @@ for (const file of project.getSourceFiles('src/**/*.{ts,tsx}')) {
|
|
|
42
42
|
await project.save();
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
## Codemod 2: useSpokeProvider deletion
|
|
45
|
+
## Codemod 2: useSpokeProvider deletion (2-arg → 1-arg)
|
|
46
46
|
|
|
47
|
-
`useSpokeProvider` is gone in v2.
|
|
47
|
+
`useSpokeProvider` is gone in v2. Replace with `useWalletProvider` from `@sodax/wallet-sdk-react`.
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
# 1. Find all usages first.
|
|
51
|
-
grep -rE '\buseSpokeProvider\b' src/
|
|
52
|
-
|
|
53
|
-
# 2. Manual delete + rewrite each (no safe sed for this — context varies).
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Per call site, the rewrite:
|
|
49
|
+
v1 had **two positional args**: `useSpokeProvider(spokeChainId, walletProvider)`. v2 collapses to a single options object: `useWalletProvider({ xChainId })`. The second v1 argument is **dropped entirely** — v2 resolves the wallet provider from the wallet-sdk-react store internally based on `xChainId`. If your v1 code constructed a wallet provider separately to pass in, delete that construction code too.
|
|
57
50
|
|
|
58
51
|
```diff
|
|
52
|
+
- // v1 — 2 positional args (chainKey + walletProvider)
|
|
59
53
|
- import { useSpokeProvider } from '@sodax/dapp-kit';
|
|
60
|
-
- const spokeProvider = useSpokeProvider(
|
|
54
|
+
- const spokeProvider = useSpokeProvider(srcChainId, walletProvider);
|
|
55
|
+
|
|
56
|
+
+ // v2 — 1 options arg, no external walletProvider
|
|
61
57
|
+ import { useWalletProvider } from '@sodax/wallet-sdk-react';
|
|
62
|
-
+
|
|
63
|
-
+ const walletProvider = useWalletProvider({ xChainId: ChainKeys.BSC_MAINNET });
|
|
58
|
+
+ const walletProvider = useWalletProvider({ xChainId: srcChainId });
|
|
64
59
|
```
|
|
65
60
|
|
|
66
|
-
|
|
61
|
+
A naive single-pass regex over `useSpokeProvider\(([^)]+)\)` eats both args and produces invalid `useWalletProvider({ xChainId: chainKey, walletProvider })`. Do the rewrite in two passes: first the call shape (drop the second positional arg), then the import (from `@sodax/dapp-kit` → `@sodax/wallet-sdk-react`). A small number of v1 callers used the single-arg form `useSpokeProvider(chainId)` — handle those separately since they don't have a second arg to drop.
|
|
62
|
+
|
|
63
|
+
Update downstream consumers of `spokeProvider` to use `walletProvider` instead — usually inside `mutate(vars)` payloads or query hook params. The field name on payloads also renamed `spokeProvider:` → `walletProvider:` (see `@sodax/sdk/ai-exported/migration/checklist.md` step 7b).
|
|
67
64
|
|
|
68
65
|
## Codemod 3: invalidate*Queries utilities deletion
|
|
69
66
|
|
|
@@ -29,6 +29,8 @@ These keep their name but their TypeScript signature is different. Usually requi
|
|
|
29
29
|
| `useMMAllowance` | Query params: `{ params: { payload: MoneyMarketParams<K> } }`. For `'borrow'` / `'withdraw'` actions: `enabled: false` — `data` stays `undefined`. |
|
|
30
30
|
| `useUserReservesData` | Param key rename: `address → userAddress`; chain key is `spokeChainKey`. |
|
|
31
31
|
| `useUserFormattedSummary` | Same. |
|
|
32
|
+
| `useATokensBalances` | Params: `{ aTokens, spokeProvider, userAddress } → { params: { aTokens, spokeChainKey, userAddress } }`. `spokeProvider` dropped; chain key is **`spokeChainKey`** (NOT `srcChainKey`); user field is **`userAddress`** (NOT `address`). Data: `Map<Address, bigint> \| undefined` (already unwrapped). |
|
|
33
|
+
| `useAToken` | Unaffected by the position-hook renames — takes only `{ aToken }`. Data: `Erc20Token & { chainKey }` (`hubAsset` / `vault` NOT included). |
|
|
32
34
|
| All staking mutations | Same as MM. Plus dedicated approve hooks per token (`useStakeApprove` ↔ `useUnstakeApprove` ↔ `useInstantUnstakeApprove`). |
|
|
33
35
|
| `useStakeRatio` | Return: `Result<bigint> → [xSodaAmount, previewDepositAmount]` (unwrapped tuple — NOT Result-wrapped in v2). |
|
|
34
36
|
| `useStakingInfo` / `useUnstakingInfo` / `useUnstakingInfoWithPenalty` / `useStakingConfig` / `useInstantUnstakeRatio` / `useConvertedAssets` | All unwrapped in v2 (hooks throw on SDK `!ok`). Branch on `isError`/`error`, not `data?.ok`. |
|
package/dist/index.cjs
CHANGED
|
@@ -2484,10 +2484,9 @@ function useClaimRewards({
|
|
|
2484
2484
|
});
|
|
2485
2485
|
}
|
|
2486
2486
|
var SodaxProvider = ({ children, config }) => {
|
|
2487
|
-
const
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
return /* @__PURE__ */ jsxRuntime.jsx(SodaxContext.Provider, { value: { sodax }, children });
|
|
2487
|
+
const sodax = react.useMemo(() => new sdk.Sodax(config), [config]);
|
|
2488
|
+
const contextValue = react.useMemo(() => ({ sodax }), [sodax]);
|
|
2489
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SodaxContext.Provider, { value: contextValue, children });
|
|
2491
2490
|
};
|
|
2492
2491
|
var defaultOnMutationError = (error) => {
|
|
2493
2492
|
console.error("[sodax] Mutation error:", error);
|