@sodax/sdk 1.5.7-beta → 2.0.0-rc.2
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/README.md +91 -7
- package/ai-exported/AGENTS.md +99 -0
- package/ai-exported/integration/README.md +41 -0
- package/ai-exported/integration/ai-rules.md +75 -0
- package/ai-exported/integration/architecture.md +519 -0
- package/ai-exported/integration/chain-specifics.md +189 -0
- package/ai-exported/integration/features/README.md +19 -0
- package/ai-exported/integration/features/auxiliary-services.md +189 -0
- package/ai-exported/integration/features/bridge.md +136 -0
- package/ai-exported/integration/features/dex.md +182 -0
- package/ai-exported/integration/features/icx-bnusd-baln.md +181 -0
- package/ai-exported/integration/features/money-market.md +198 -0
- package/ai-exported/integration/features/staking.md +166 -0
- package/ai-exported/integration/features/swap.md +207 -0
- package/ai-exported/integration/quickstart.md +213 -0
- package/ai-exported/integration/recipes/README.md +21 -0
- package/ai-exported/integration/recipes/backend-server-init.md +69 -0
- package/ai-exported/integration/recipes/chain-key-narrowing.md +65 -0
- package/ai-exported/integration/recipes/gas-estimation.md +33 -0
- package/ai-exported/integration/recipes/initialize-sodax.md +53 -0
- package/ai-exported/integration/recipes/raw-tx-flow.md +71 -0
- package/ai-exported/integration/recipes/result-and-errors.md +104 -0
- package/ai-exported/integration/recipes/signed-tx-flow.md +46 -0
- package/ai-exported/integration/recipes/testing.md +101 -0
- package/ai-exported/integration/reference/README.md +18 -0
- package/ai-exported/integration/reference/chain-keys.md +67 -0
- package/ai-exported/integration/reference/error-codes.md +165 -0
- package/ai-exported/integration/reference/glossary.md +32 -0
- package/ai-exported/integration/reference/public-api.md +138 -0
- package/ai-exported/integration/reference/wallet-providers.md +62 -0
- package/ai-exported/migration/README.md +58 -0
- package/ai-exported/migration/ai-rules.md +80 -0
- package/ai-exported/migration/breaking-changes/architecture.md +342 -0
- package/ai-exported/migration/breaking-changes/result-and-errors.md +363 -0
- package/ai-exported/migration/breaking-changes/type-system.md +321 -0
- package/ai-exported/migration/checklist.md +61 -0
- package/ai-exported/migration/features/README.md +35 -0
- package/ai-exported/migration/features/auxiliary-services.md +156 -0
- package/ai-exported/migration/features/bridge.md +125 -0
- package/ai-exported/migration/features/dex.md +143 -0
- package/ai-exported/migration/features/icx-bnusd-baln.md +151 -0
- package/ai-exported/migration/features/money-market.md +214 -0
- package/ai-exported/migration/features/staking.md +138 -0
- package/ai-exported/migration/features/swap.md +198 -0
- package/ai-exported/migration/recipes.md +288 -0
- package/ai-exported/migration/reference/README.md +18 -0
- package/ai-exported/migration/reference/deleted-exports.md +126 -0
- package/ai-exported/migration/reference/error-code-crosswalk.md +104 -0
- package/ai-exported/migration/reference/return-shapes.md +49 -0
- package/ai-exported/migration/reference/sodax-config.md +52 -0
- package/dist/index.cjs +32076 -31544
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8604 -7136
- package/dist/index.d.ts +8604 -7136
- package/dist/index.mjs +31893 -31402
- package/dist/index.mjs.map +1 -1
- package/package.json +20 -12
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Token migration (ICX/bnUSD/BALN) — v1 → v2
|
|
2
|
+
|
|
3
|
+
Pure-SDK migration playbook for `MigrationService` (the SDK module — not v1→v2 SDK porting).
|
|
4
|
+
|
|
5
|
+
Pair: [`../../integration/features/icx-bnusd-baln.md`](../../integration/features/icx-bnusd-baln.md).
|
|
6
|
+
|
|
7
|
+
## TL;DR
|
|
8
|
+
|
|
9
|
+
1. **Drop `spokeProvider`. Pass `walletProvider` directly.**
|
|
10
|
+
2. **Add `srcChainKey` + `srcAddress` to every action params.** All migration param types (`MigrationParams<K>`, `UnifiedBnUSDMigrateParams<K>`, `IcxToSodaMigrateParams<K>`, `RevertSodaToIcxParams<K>`, `BalnSwapParams<K>`) gained both fields and a `<K>` generic.
|
|
11
|
+
3. **All 4 orchestrator methods return `Result<TxHashPair>`.** v1 returned a string tx hash and threw on error.
|
|
12
|
+
4. **`migratebnUSD` carries `direction` on error context** — `'forward'` (legacy → new) or `'reverse'` (new → legacy). The SDK detects direction from `(srcToken, dstToken)` addresses.
|
|
13
|
+
5. **`BalnSwapService` lock-management methods STILL THROW.** `claim`, `claimUnstaked`, `stake`, `unstake`, `cancelUnstake`, `getDetailedUserLocks` — these 6 methods preserve the v1 throw-on-error contract. Wrap in `try/catch` until a future cleanup release converts them to `Result`.
|
|
14
|
+
6. **Errors → `SodaxError` + `Result<T>`.** v1's `MigrationError<MigrationErrorCode>` is gone.
|
|
15
|
+
|
|
16
|
+
## Type / symbol cheat sheet
|
|
17
|
+
|
|
18
|
+
### Field-level renames
|
|
19
|
+
|
|
20
|
+
| Type | v1 shape | v2 shape | Notes |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| `MigrationParams` | non-generic | `MigrationParams<K extends SpokeChainKey>` with `srcChainKey`, `srcAddress`, `amount`, `dstAddress?` | Generic added. |
|
|
23
|
+
| `UnifiedBnUSDMigrateParams` | non-generic | `UnifiedBnUSDMigrateParams<K>` extends `MigrationParams<K>` with `srcToken`, `dstToken` | Generic added. |
|
|
24
|
+
| `BalnSwapParams` | `{ amount, lockPeriodMonths }` | `MigrationParams<K> & { lockPeriodMonths }` | Adds chain context. |
|
|
25
|
+
|
|
26
|
+
### Deleted symbols
|
|
27
|
+
|
|
28
|
+
- `MigrationError<MigrationErrorCode>` and `isMigrationError` — replaced by `SodaxError<C>` + `feature: 'migration'`.
|
|
29
|
+
- The 6 v1 separate-method-per-direction names (`migrateBnUSDForward`, `migrateBnUSDReverse`, …) — collapsed into `migratebnUSD` with auto-direction detection on `error.context.direction`.
|
|
30
|
+
|
|
31
|
+
### v1 → v2 error code crosswalk
|
|
32
|
+
|
|
33
|
+
| v1 `MigrationErrorCode` | v2 code + context |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `MIGRATE_BNUSD_FORWARD_FAILED` | `EXECUTION_FAILED` (`action: 'migratebnUSD'`, `direction: 'forward'`) |
|
|
36
|
+
| `MIGRATE_BNUSD_REVERSE_FAILED` | `EXECUTION_FAILED` (`action: 'migratebnUSD'`, `direction: 'reverse'`) |
|
|
37
|
+
| `MIGRATE_ICX_TO_SODA_FAILED` | `EXECUTION_FAILED` (`action: 'migrateIcxToSoda'`) |
|
|
38
|
+
| `REVERT_MIGRATE_SODA_TO_ICX_FAILED` | `EXECUTION_FAILED` (`action: 'revertMigrateSodaToIcx'`) |
|
|
39
|
+
| `MIGRATE_BALN_FAILED` | `EXECUTION_FAILED` (`action: 'migrateBaln'`) |
|
|
40
|
+
| `GET_AVAILABLE_AMOUNT_FAILED` | `LOOKUP_FAILED` (`method: 'getAvailableAmount'`) |
|
|
41
|
+
|
|
42
|
+
`migratebnUSD` additionally has `phase: 'destinationExecution'` on errors from its secondary `waitUntilIntentExecuted` watcher (after the primary relay completes, bnUSD waits for the destination contract to finalize).
|
|
43
|
+
|
|
44
|
+
## Per-method delta
|
|
45
|
+
|
|
46
|
+
### `migratebnUSD` (replaces v1 forward + reverse methods)
|
|
47
|
+
|
|
48
|
+
```diff
|
|
49
|
+
- // v1 had two methods:
|
|
50
|
+
- await sodax.migration.migrateBnUSDForward({ amount, /* … */ }, spokeProvider);
|
|
51
|
+
- await sodax.migration.migrateBnUSDReverse({ amount, /* … */ }, spokeProvider);
|
|
52
|
+
|
|
53
|
+
+ // v2 has one method; SDK detects direction from (srcToken, dstToken):
|
|
54
|
+
+ const result = await sodax.migration.migratebnUSD({
|
|
55
|
+
+ params: {
|
|
56
|
+
+ srcChainKey, srcAddress,
|
|
57
|
+
+ srcToken, // legacy or new bnUSD
|
|
58
|
+
+ dstToken, // the other one
|
|
59
|
+
+ amount,
|
|
60
|
+
+ dstAddress,
|
|
61
|
+
+ },
|
|
62
|
+
+ raw: false,
|
|
63
|
+
+ walletProvider,
|
|
64
|
+
+ });
|
|
65
|
+
+ if (!result.ok) {
|
|
66
|
+
+ const dir = result.error.context?.direction; // 'forward' | 'reverse'
|
|
67
|
+
+ /* … */
|
|
68
|
+
+ }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `migrateIcxToSoda` / `revertMigrateSodaToIcx` / `migrateBaln`
|
|
72
|
+
|
|
73
|
+
Standard pattern:
|
|
74
|
+
|
|
75
|
+
```diff
|
|
76
|
+
- await sodax.migration.migrateIcxToSoda({ amount }, spokeProvider);
|
|
77
|
+
+ const result = await sodax.migration.migrateIcxToSoda({
|
|
78
|
+
+ params: { srcChainKey: ChainKeys.ICON_MAINNET, srcAddress: 'hx…', amount },
|
|
79
|
+
+ raw: false,
|
|
80
|
+
+ walletProvider: iconWp,
|
|
81
|
+
+ });
|
|
82
|
+
+ if (!result.ok) return;
|
|
83
|
+
+ const { srcChainTxHash, dstChainTxHash } = result.value;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`migrateBaln` adds `lockPeriodMonths: 0 | 1 | 2 | 3 | 6 | 12 | 18 | 24` to the params. Reward multiplier ranges 0.5x (0 months) – 1.5x (24 months).
|
|
87
|
+
|
|
88
|
+
### Approve / allowance — action-discriminated
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
await sodax.migration.approve({
|
|
92
|
+
params: { srcChainKey, srcAddress, amount, action: 'migrateBaln' /* or migratebnUSD | migrateIcxToSoda | revertMigrateSodaToIcx */ },
|
|
93
|
+
raw: false,
|
|
94
|
+
walletProvider,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const allowed = await sodax.migration.isAllowanceValid({
|
|
98
|
+
params: { srcChainKey, srcAddress, amount, action: 'migrateBaln' },
|
|
99
|
+
raw: true, // read-only
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### `getAvailableAmount` (claimable from partial migration)
|
|
104
|
+
|
|
105
|
+
```diff
|
|
106
|
+
- const amount: bigint = await sodax.migration.icx.getAvailableAmount(spokeProvider);
|
|
107
|
+
+ const result = await sodax.migration.icxMigration.getAvailableAmount(); // sub-service renamed; takes no args in v2
|
|
108
|
+
+ if (!result.ok) return 0n;
|
|
109
|
+
+ const amount = result.value;
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### BALN lock management — STILL THROWS
|
|
113
|
+
|
|
114
|
+
```diff
|
|
115
|
+
try {
|
|
116
|
+
- const tx = await sodax.migration.balnSwapService.stake({ amount, lockPeriod }, spokeProvider);
|
|
117
|
+
+ const tx = await sodax.migration.balnSwapService.stake({
|
|
118
|
+
+ params: { srcChainKey, srcAddress, amount, lockPeriodMonths },
|
|
119
|
+
+ raw: false,
|
|
120
|
+
+ walletProvider,
|
|
121
|
+
+ });
|
|
122
|
+
/* … */
|
|
123
|
+
} catch (e) {
|
|
124
|
+
/* still v1-style: catch the throw */
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`claim`, `claimUnstaked`, `unstake`, `cancelUnstake`, `getDetailedUserLocks` follow the same pattern. They preserve the throw-on-error contract for now — future cleanup will Result-wrap them.
|
|
129
|
+
|
|
130
|
+
## Pitfalls
|
|
131
|
+
|
|
132
|
+
Cross-cutting traps (Result destructuring, error-model migration, srcChain/dstChain renames, etc.) live in [`../ai-rules.md`](../ai-rules.md). The list below is feature-specific — typecheck fingerprints, return-shape diffs, and gotchas unique to this feature.
|
|
133
|
+
|
|
134
|
+
1. **`migratebnUSD` direction detection.** v1 had explicit forward/reverse methods; v2 detects from `(srcToken, dstToken)`. If both are on the same side (both legacy or both new), the SDK rejects with `VALIDATION_FAILED`. Use the `direction` field on `error.context` to disambiguate in error messaging.
|
|
135
|
+
2. **BALN lock methods don't return `Result`.** Be careful migrating wrappers — if your wrapper assumes Result-shape, lock methods will produce `undefined.ok` runtime errors. Keep the `try/catch` shape.
|
|
136
|
+
3. **`lockPeriodMonths` is a literal union, not arbitrary `number`.** TypeScript rejects `lockPeriodMonths: 7`. Allowed values: `0 | 1 | 2 | 3 | 6 | 12 | 18 | 24`.
|
|
137
|
+
4. **`getAvailableAmount` lives on the sub-service `sodax.migration.icxMigration` and takes no arguments.** v1 expected `(spokeProvider)`; v2 reads on-chain SODA liquidity directly from the hub provider, so the method needs no chain context. Sub-service field names: `sodax.migration.icxMigration`, `sodax.migration.bnUSDMigrationService`, `sodax.migration.balnSwapService`.
|
|
138
|
+
5. **`destinationExecution` phase on bnUSD errors** — these errors land **after** the relay succeeds. The spoke and hub txs may already exist; the destination-side finalization is what failed. Distinguish from primary relay errors (`phase: 'relay'`) when surfacing UX.
|
|
139
|
+
|
|
140
|
+
## Verification
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
pnpm -C <your-app-dir> checkTs
|
|
144
|
+
|
|
145
|
+
grep -rE "spokeProvider:\s*\w+|migrateBnUSDForward\b|migrateBnUSDReverse\b|isMigrationError\b|MigrationError\b" src/
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Cross-references
|
|
149
|
+
|
|
150
|
+
- v2 token migration usage: [`../../integration/features/icx-bnusd-baln.md`](../../integration/features/icx-bnusd-baln.md).
|
|
151
|
+
- Cross-cutting prerequisites listed in [`../README.md`](../README.md).
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Money Market migration — v1 → v2
|
|
2
|
+
|
|
3
|
+
Pure-SDK migration playbook for `MoneyMarketService`.
|
|
4
|
+
|
|
5
|
+
Pair: [`../../integration/features/money-market.md`](../../integration/features/money-market.md).
|
|
6
|
+
|
|
7
|
+
## TL;DR
|
|
8
|
+
|
|
9
|
+
1. **Drop `spokeProvider` from every params object.** Pass `walletProvider` directly into the SDK call payload alongside `params` and `raw: false`.
|
|
10
|
+
2. **Add `srcChainKey` + `srcAddress` to every action params object.** v2's `MoneyMarketParams<K>` requires both; v1 didn't have them at all.
|
|
11
|
+
3. **Mutations resolve `Result<TxHashPair>`, not throw.** v1 mutation methods threw; v2 returns `{ ok: false, error }` on SDK-level failure. Branch on `result.ok`.
|
|
12
|
+
4. **`MoneyMarketSupplyParams` etc. are now generic** (`MoneyMarketSupplyParams<K extends SpokeChainKey>`). Add a chain-key generic to your params variables, or let TS infer from a literal `srcChainKey`.
|
|
13
|
+
5. **Replace `moneyMarketSupportedTokens[chainId]` with `sodax.moneyMarket.getSupportedTokensByChainId(chainKey)`.**
|
|
14
|
+
6. **Replace `hubAssets[chainId][address]?.vault` with `token.vault`** (now baked into `XToken`). Same for `token.hubAsset`.
|
|
15
|
+
7. **`sodax.moneyMarket.getAToken(...)` returns `Erc20Token & { chainKey }`, not a full `XToken`.** No `hubAsset` / `vault` on the result — look up the full `XToken` via `sodax.config.getMoneyMarketToken(chainKey, address)` if needed.
|
|
16
|
+
8. **Errors → `SodaxError` + `Result<T>`.** v1's `MoneyMarketError<MoneyMarketErrorCode>` is gone. The CODE moved from `error.code` to `error.message`-style? **No** — it's still on `error.code`, but the union changed (see crosswalk below).
|
|
17
|
+
|
|
18
|
+
## Type / symbol cheat sheet
|
|
19
|
+
|
|
20
|
+
### Field-level renames
|
|
21
|
+
|
|
22
|
+
| Type | v1 shape | v2 shape | Notes |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| `MoneyMarketSupplyParams` | `{ token, amount, action }` | `{ srcChainKey, srcAddress, token, amount, action, dstChainKey?, dstAddress? }` | Same template for borrow / withdraw / repay. Now generic in `K extends SpokeChainKey`. |
|
|
25
|
+
| `MoneyMarketBorrowParams` | non-generic | `MoneyMarketBorrowParams<K>` | Optional `dstChainKey`/`dstAddress` for cross-chain delivery. |
|
|
26
|
+
| `MoneyMarketRepayParams` | non-generic | `MoneyMarketRepayParams<K>` | Optional `dstChainKey`/`dstAddress` for paying off debt on a different chain. |
|
|
27
|
+
| `XToken` | `xChainId` | `chainKey` | Type renamed `Token` → `XToken`. New fields `vault`, `hubAsset` baked in. |
|
|
28
|
+
|
|
29
|
+
### Deleted symbols
|
|
30
|
+
|
|
31
|
+
- `moneyMarketSupportedTokens` — `Record<SpokeChainKey, XToken[]>` global. Use `sodax.moneyMarket.getSupportedTokensByChainId(chainKey)` / `getSupportedTokens()`.
|
|
32
|
+
- `hubAssets` — vault address lookup global. Use `XToken.vault` / `XToken.hubAsset` directly.
|
|
33
|
+
- `SodaTokens` — vault-validation registry. Use `sodax.config.getMoneyMarketReserveAssets()`.
|
|
34
|
+
- `MoneyMarketError<MoneyMarketErrorCode>` and `isMoneyMarketError` — replaced by `SodaxError<C>` + `isSodaxError(e) && e.feature === 'moneyMarket'`.
|
|
35
|
+
|
|
36
|
+
### v1 → v2 error code crosswalk (money-market-specific)
|
|
37
|
+
|
|
38
|
+
| v1 `MoneyMarketErrorCode` | v2 code + context |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `CREATE_SUPPLY_INTENT_FAILED` | `INTENT_CREATION_FAILED` (`action: 'supply'`) |
|
|
41
|
+
| `CREATE_BORROW_INTENT_FAILED` | `INTENT_CREATION_FAILED` (`action: 'borrow'`) |
|
|
42
|
+
| `CREATE_WITHDRAW_INTENT_FAILED` | `INTENT_CREATION_FAILED` (`action: 'withdraw'`) |
|
|
43
|
+
| `CREATE_REPAY_INTENT_FAILED` | `INTENT_CREATION_FAILED` (`action: 'repay'`) |
|
|
44
|
+
| `SUPPLY_FAILED` | `EXECUTION_FAILED` (`action: 'supply'`) |
|
|
45
|
+
| `BORROW_FAILED` | `EXECUTION_FAILED` (`action: 'borrow'`) |
|
|
46
|
+
| `WITHDRAW_FAILED` | `EXECUTION_FAILED` (`action: 'withdraw'`) |
|
|
47
|
+
| `REPAY_FAILED` | `EXECUTION_FAILED` (`action: 'repay'`) |
|
|
48
|
+
| `ALLOWANCE_CHECK_FAILED` | `ALLOWANCE_CHECK_FAILED` (unchanged) |
|
|
49
|
+
| `APPROVE_FAILED` | `APPROVE_FAILED` (unchanged) |
|
|
50
|
+
| `GAS_ESTIMATION_FAILED` | `GAS_ESTIMATION_FAILED` (unchanged) |
|
|
51
|
+
|
|
52
|
+
## Per-method delta
|
|
53
|
+
|
|
54
|
+
### `supply`
|
|
55
|
+
|
|
56
|
+
```diff
|
|
57
|
+
- const params: MoneyMarketSupplyParams = {
|
|
58
|
+
- token: token.address,
|
|
59
|
+
- amount: parseUnits('100', 6),
|
|
60
|
+
- action: 'supply',
|
|
61
|
+
- };
|
|
62
|
+
- const result = await sodax.moneyMarket.supply({ params, spokeProvider });
|
|
63
|
+
- // result throws on failure, or returns the tx hash
|
|
64
|
+
+ const params: MoneyMarketSupplyParams<typeof srcChainKey> = {
|
|
65
|
+
+ srcChainKey: ChainKeys.ARBITRUM_MAINNET,
|
|
66
|
+
+ srcAddress: '0x…',
|
|
67
|
+
+ token: token.address,
|
|
68
|
+
+ amount: parseUnits('100', 6),
|
|
69
|
+
+ action: 'supply',
|
|
70
|
+
+ };
|
|
71
|
+
+ const result = await sodax.moneyMarket.supply({ params, raw: false, walletProvider });
|
|
72
|
+
+ if (!result.ok) {
|
|
73
|
+
+ // result.error: SodaxError with feature: 'moneyMarket'
|
|
74
|
+
+ return;
|
|
75
|
+
+ }
|
|
76
|
+
+ const { srcChainTxHash, dstChainTxHash } = result.value;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### `borrow` — gain cross-chain delivery
|
|
80
|
+
|
|
81
|
+
If you ported a same-chain borrow, no new fields needed — just `srcChainKey` + `srcAddress`. For cross-chain delivery (which v1 didn't expose this cleanly), add `dstChainKey` and `dstAddress`:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
await sodax.moneyMarket.borrow({
|
|
85
|
+
params: {
|
|
86
|
+
srcChainKey: ChainKeys.ARBITRUM_MAINNET,
|
|
87
|
+
srcAddress,
|
|
88
|
+
token: USDC.address,
|
|
89
|
+
amount,
|
|
90
|
+
action: 'borrow',
|
|
91
|
+
dstChainKey: ChainKeys.STELLAR_MAINNET, // NEW in v2
|
|
92
|
+
dstAddress: 'G…', // NEW in v2
|
|
93
|
+
},
|
|
94
|
+
raw: false,
|
|
95
|
+
walletProvider,
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `repay` — pay from a different chain than the debt
|
|
100
|
+
|
|
101
|
+
Similar: v2 lets the spender chain (`srcChainKey`) differ from the debt chain (`dstChainKey`):
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
await sodax.moneyMarket.repay({
|
|
105
|
+
params: {
|
|
106
|
+
srcChainKey: fromChain,
|
|
107
|
+
srcAddress: fromAddress,
|
|
108
|
+
token: tokenOnFromChain.address,
|
|
109
|
+
amount,
|
|
110
|
+
action: 'repay',
|
|
111
|
+
dstChainKey: debtChain,
|
|
112
|
+
dstAddress: debtAddress,
|
|
113
|
+
},
|
|
114
|
+
raw: false,
|
|
115
|
+
walletProvider: walletOnFromChain,
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `approve` / `isAllowanceValid`
|
|
120
|
+
|
|
121
|
+
```diff
|
|
122
|
+
- const allowed = await sodax.moneyMarket.isAllowanceValid({ params, spokeProvider });
|
|
123
|
+
+ const allowed = await sodax.moneyMarket.isAllowanceValid({
|
|
124
|
+
+ params, // includes srcChainKey, srcAddress, action
|
|
125
|
+
+ raw: true, // read-only — walletProvider not needed
|
|
126
|
+
+ });
|
|
127
|
+
+ if (!allowed.ok) return false;
|
|
128
|
+
+ if (!allowed.value) await sodax.moneyMarket.approve({ params, raw: false, walletProvider });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The `params.action` field discriminates which token gets approved (relevant for repay where the spent token may differ).
|
|
132
|
+
|
|
133
|
+
## Replacing the static lookups
|
|
134
|
+
|
|
135
|
+
```diff
|
|
136
|
+
- import { moneyMarketSupportedTokens, hubAssets } from '@sodax/types';
|
|
137
|
+
- const supplyTokens = moneyMarketSupportedTokens[chainId];
|
|
138
|
+
+ const supplyTokens = sodax.moneyMarket.getSupportedTokensByChainId(chainKey);
|
|
139
|
+
|
|
140
|
+
- const allTokens = Object.entries(moneyMarketSupportedTokens)
|
|
141
|
+
- .flatMap(([chainId, tokens]) => tokens.map(t => ({ ...t, xChainId: chainId })));
|
|
142
|
+
+ const allTokens = Object.entries(sodax.moneyMarket.getSupportedTokens())
|
|
143
|
+
+ .flatMap(([_chainKey, tokens]) => tokens); // tokens already carry chainKey in v2
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```diff
|
|
147
|
+
- const vault = hubAssets[chainId]?.[token.address]?.vault;
|
|
148
|
+
+ const vault = token.vault; // baked into XToken
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Worked example — supply flow
|
|
152
|
+
|
|
153
|
+
A single supply call site, before and after. Shows every change in one place: spoke-provider drop, params shape, return shape, field rename.
|
|
154
|
+
|
|
155
|
+
```diff
|
|
156
|
+
- const params: MoneyMarketSupplyParams = {
|
|
157
|
+
- token: token.address,
|
|
158
|
+
- amount: parseUnits(amount, token.decimals),
|
|
159
|
+
- action: 'supply',
|
|
160
|
+
- };
|
|
161
|
+
- const txHash = await sodax.moneyMarket.supply({ params, spokeProvider });
|
|
162
|
+
- // throws on failure
|
|
163
|
+
+ const params: MoneyMarketSupplyParams<typeof srcChainKey> = {
|
|
164
|
+
+ srcChainKey, // NEW: required
|
|
165
|
+
+ srcAddress, // NEW: required (GetAddressType<K>)
|
|
166
|
+
+ token: token.address,
|
|
167
|
+
+ amount: parseUnits(amount, token.decimals),
|
|
168
|
+
+ action: 'supply',
|
|
169
|
+
+ };
|
|
170
|
+
+ const result = await sodax.moneyMarket.supply({
|
|
171
|
+
+ params,
|
|
172
|
+
+ raw: false, // NEW: discriminator
|
|
173
|
+
+ walletProvider, // NEW: was inside spokeProvider in v1
|
|
174
|
+
+ });
|
|
175
|
+
+ if (!result.ok) {
|
|
176
|
+
+ // result.error: SodaxError with feature: 'moneyMarket', context.action: 'supply'
|
|
177
|
+
+ return;
|
|
178
|
+
+ }
|
|
179
|
+
+ const { srcChainTxHash, dstChainTxHash } = result.value; // TxHashPair, not single hash
|
|
180
|
+
|
|
181
|
+
const successData: ActionSuccessData = {
|
|
182
|
+
/* … */
|
|
183
|
+
- destinationChainId: token.xChainId, // OLD field name
|
|
184
|
+
+ destinationChainId: token.chainKey, // RENAMED
|
|
185
|
+
txHash: srcChainTxHash,
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Pitfalls
|
|
190
|
+
|
|
191
|
+
Cross-cutting traps (Result destructuring, error-model migration, srcChain/dstChain renames, etc.) live in [`../ai-rules.md`](../ai-rules.md). The list below is feature-specific — typecheck fingerprints, return-shape diffs, and gotchas unique to this feature.
|
|
192
|
+
|
|
193
|
+
1. **Forgetting `srcChainKey` + `srcAddress` in params.** TypeScript surfaces this as `error TS1360: Type '{ token, amount, action }' does not satisfy the expected type 'MoneyMarketSupplyParams'`. Add both required fields.
|
|
194
|
+
2. **Borrow/repay default delivery to source.** Omit `dstChainKey`/`dstAddress` if you want same-chain. Don't pass them as the same value as `srcChainKey` / `srcAddress` — let the default kick in.
|
|
195
|
+
3. **`sodax.moneyMarket.getAToken(...)` returns a partial token.** `Erc20Token & { chainKey }`, not a full `XToken` — no `vault` / `hubAsset` on the result. Look up the full `XToken` via `sodax.config.getMoneyMarketToken(chainKey, address)` separately if you need those fields.
|
|
196
|
+
4. **`hubAssets` is gone.** Anything that walked it for vault lookup must use `token.vault` directly.
|
|
197
|
+
5. **`baseChainInfo[chain].id` is gone — entries have `.key`.** Common in `ChainSelector`-style components.
|
|
198
|
+
6. **`spokeProvider.chainConfig.chain.type === 'EVM'`** is gone. Use `getChainType(chainKey) === 'EVM'` from `@sodax/sdk`.
|
|
199
|
+
7. **`Number(chainKey)` returns `NaN` for non-numeric keys.** `ChainKeys.ICON_MAINNET` is `'0x1.icon'`; numeric coercions break.
|
|
200
|
+
|
|
201
|
+
## Verification
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
pnpm -C <your-app-dir> checkTs
|
|
205
|
+
|
|
206
|
+
# Targeted scans:
|
|
207
|
+
grep -rE "spokeProvider:\s*\w+|moneyMarketSupportedTokens|\bhubAssets\b" src/
|
|
208
|
+
grep -rE "isMoneyMarketError\b|MoneyMarketError\b" src/
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Cross-references
|
|
212
|
+
|
|
213
|
+
- v2 money market usage: [`../../integration/features/money-market.md`](../../integration/features/money-market.md).
|
|
214
|
+
- Cross-cutting prerequisites listed in [`../README.md`](../README.md).
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Staking migration — v1 → v2
|
|
2
|
+
|
|
3
|
+
Pure-SDK migration playbook for `StakingService`.
|
|
4
|
+
|
|
5
|
+
Pair: [`../../integration/features/staking.md`](../../integration/features/staking.md).
|
|
6
|
+
|
|
7
|
+
## TL;DR
|
|
8
|
+
|
|
9
|
+
1. **Drop `spokeProvider` from every params object.** Pass `walletProvider` directly into the SDK call.
|
|
10
|
+
2. **Add `srcChainKey` + `srcAddress` to every `*Params<K>`.** `account` field is renamed to `srcAddress`.
|
|
11
|
+
3. **All 5 staking actions are cross-chain.** Even though staking writes happen on the hub, every SDK method (`stake`, `unstake`, `instantUnstake`, `claim`, `cancelUnstake`) accepts `srcChainKey: K extends SpokeChainKey` and relays spoke→hub via `relayTxAndWaitPacket`. **Return shape is always `Result<TxHashPair>`.**
|
|
12
|
+
4. **`approve` is an exception — it returns `Result<TxReturnType<K, false>>` (single hash).** Approve is spoke-only (no relay) — it just spends ERC20 allowance on the source chain.
|
|
13
|
+
5. **Approve and allowance are action-discriminated.** `staking.approve` and `staking.isAllowanceValid` take a `StakingParamsUnion` discriminated by `params.action`. `'stake'` approves SODA; `'unstake'` and `'instantUnstake'` approve xSoda.
|
|
14
|
+
6. **Info getter signatures changed.** v1 took `spokeProvider`; v2 takes `(srcAddress, srcChainKey)`. The SDK derives the hub wallet internally.
|
|
15
|
+
7. **Hub-only / amount-only reads have no chain context.** `getStakingConfig()`, `getStakeRatio(amount)`, `getInstantUnstakeRatio(amount)`, `getConvertedAssets(amount)` — none accept `srcChainKey`. (Take `bigint` amount directly, not an object.) `getStakeRatio` returns `Result<[xSodaAmount, previewDepositAmount]>` (a tuple — both are bigints).
|
|
16
|
+
8. **Errors → `SodaxError` + `Result<T>`.** v1's `StakingError<StakingErrorCode>` is gone.
|
|
17
|
+
|
|
18
|
+
## Type / symbol cheat sheet
|
|
19
|
+
|
|
20
|
+
### Field-level renames
|
|
21
|
+
|
|
22
|
+
| Type | v1 shape | v2 shape | Notes |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| `StakeParams` | `{ amount, account, minReceive, action: 'stake' }` | `{ srcChainKey, srcAddress, amount, minReceive, action: 'stake' }` | Now generic `<K>`. `account` → `srcAddress`. |
|
|
25
|
+
| `UnstakeParams` | `{ amount, account, action: 'unstake' }` | `{ srcChainKey, srcAddress, amount, action: 'unstake' }` | |
|
|
26
|
+
| `InstantUnstakeParams` | `{ amount, minAmount, account, action: 'instantUnstake' }` | `{ srcChainKey, srcAddress, amount, minAmount, action: 'instantUnstake' }` | |
|
|
27
|
+
| `ClaimParams` | `{ requestId, amount, action: 'claim' }` | `{ srcChainKey, srcAddress, requestId, amount, action: 'claim' }` | Adds chain context. |
|
|
28
|
+
| `CancelUnstakeParams` | `{ requestId, action: 'cancelUnstake' }` | `{ srcChainKey, srcAddress, requestId, action: 'cancelUnstake' }` | Adds chain context. |
|
|
29
|
+
| `getStakingInfo` (read) | `(spokeProvider) => Promise<StakingInfo>` | `(srcAddress, srcChainKey) => Promise<Result<StakingInfo>>` | Renamed to `getStakingInfoFromSpoke` (the v1 `getStakingInfo` was hub-only and is not surfaced now). |
|
|
30
|
+
| `getUnstakingInfo` (read) | `(userAddress, spokeProvider)` | `(srcAddress, srcChainKey)` | v1 ignored `userAddress`; v2 reads it for real. |
|
|
31
|
+
| `getUnstakingInfoWithPenalty` (read) | new (v2) | `(srcAddress, srcChainKey) => Promise<Result<UnstakingInfo & { requestsWithPenalty: UnstakeRequestWithPenalty[] }>>` | Wraps `getUnstakingInfo`'s base shape with a penalty-augmented request list. |
|
|
32
|
+
|
|
33
|
+
### Deleted symbols
|
|
34
|
+
|
|
35
|
+
- `StakingError<StakingErrorCode>` and `isStakingError` — replaced by `SodaxError<C>` + `feature: 'staking'`.
|
|
36
|
+
- v1 `getStakingInfo(hubAddress, …)` — not exposed as a public method in v2. Use `getStakingInfoFromSpoke(srcAddress, srcChainKey)`; the SDK derives the hub wallet via `HubService.getUserHubWalletAddress` internally.
|
|
37
|
+
- `spokeProvider instanceof SonicSpokeProvider` runtime checks — replace with `isHubChainKeyType(chainKey)` from `@sodax/sdk`.
|
|
38
|
+
|
|
39
|
+
### v1 → v2 error code crosswalk (staking-specific)
|
|
40
|
+
|
|
41
|
+
| v1 `StakingErrorCode` | v2 code + context |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `STAKE_FAILED` | `EXECUTION_FAILED` (`action: 'stake'`) |
|
|
44
|
+
| `UNSTAKE_FAILED` | `EXECUTION_FAILED` (`action: 'unstake'`) |
|
|
45
|
+
| `INSTANT_UNSTAKE_FAILED` | `EXECUTION_FAILED` (`action: 'instantUnstake'`) |
|
|
46
|
+
| `CLAIM_FAILED` | `EXECUTION_FAILED` (`action: 'claim'`) |
|
|
47
|
+
| `CANCEL_UNSTAKE_FAILED` | `EXECUTION_FAILED` (`action: 'cancelUnstake'`) |
|
|
48
|
+
| `GET_STAKING_INFO_FAILED` | `LOOKUP_FAILED` (`method: 'getStakingInfo'` or `'getStakingInfoFromSpoke'`) |
|
|
49
|
+
| `GET_UNSTAKING_INFO_FAILED` | `LOOKUP_FAILED` (`method: 'getUnstakingInfo'`) |
|
|
50
|
+
| `GET_STAKING_CONFIG_FAILED` | `LOOKUP_FAILED` (`method: 'getStakingConfig'`) |
|
|
51
|
+
| `GET_STAKE_RATIO_FAILED` | `LOOKUP_FAILED` (`method: 'getStakeRatio'`) |
|
|
52
|
+
|
|
53
|
+
## Per-method delta
|
|
54
|
+
|
|
55
|
+
### `stake`
|
|
56
|
+
|
|
57
|
+
```diff
|
|
58
|
+
- await sodax.staking.stake({ amount, account, minReceive, action: 'stake' }, spokeProvider);
|
|
59
|
+
+ const result = await sodax.staking.stake({
|
|
60
|
+
+ params: {
|
|
61
|
+
+ srcChainKey: ChainKeys.ARBITRUM_MAINNET,
|
|
62
|
+
+ srcAddress: '0x…',
|
|
63
|
+
+ amount, minReceive,
|
|
64
|
+
+ action: 'stake',
|
|
65
|
+
+ },
|
|
66
|
+
+ raw: false,
|
|
67
|
+
+ walletProvider,
|
|
68
|
+
+ });
|
|
69
|
+
+ if (!result.ok) return;
|
|
70
|
+
+ const { srcChainTxHash, dstChainTxHash } = result.value;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### `unstake` / `instantUnstake` / `claim` / `cancelUnstake`
|
|
74
|
+
|
|
75
|
+
Same shape as stake. `account` → `srcAddress`. `requestId` (claim, cancelUnstake) is unchanged.
|
|
76
|
+
|
|
77
|
+
### `approve` / `isAllowanceValid` — action-discriminated
|
|
78
|
+
|
|
79
|
+
```diff
|
|
80
|
+
- await sodax.staking.approveStake({ amount, account, ... }, spokeProvider);
|
|
81
|
+
+ await sodax.staking.approve({
|
|
82
|
+
+ params: { srcChainKey, srcAddress, amount, action: 'stake' },
|
|
83
|
+
+ raw: false,
|
|
84
|
+
+ walletProvider,
|
|
85
|
+
+ });
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For `isAllowanceValid`:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const result = await sodax.staking.isAllowanceValid({
|
|
92
|
+
params: { srcChainKey, srcAddress, amount, action: 'stake' },
|
|
93
|
+
raw: true, // read-only
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Info reads
|
|
98
|
+
|
|
99
|
+
```diff
|
|
100
|
+
- const info = await sodax.staking.getStakingInfo(spokeProvider);
|
|
101
|
+
+ const result = await sodax.staking.getStakingInfoFromSpoke(srcAddress, srcChainKey);
|
|
102
|
+
+ if (!result.ok) return;
|
|
103
|
+
+ const info = result.value;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For amount-only reads (no chain context):
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const result = await sodax.staking.getStakeRatio(parseUnits('100', 18));
|
|
110
|
+
if (result.ok) {
|
|
111
|
+
const [xSodaAmount, previewDepositAmount] = result.value;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Pitfalls
|
|
116
|
+
|
|
117
|
+
Cross-cutting traps (Result destructuring, error-model migration, srcChain/dstChain renames, etc.) live in [`../ai-rules.md`](../ai-rules.md). The list below is feature-specific — typecheck fingerprints, return-shape diffs, and gotchas unique to this feature.
|
|
118
|
+
|
|
119
|
+
1. **Wrong return shape for actions.** Treating `stake/unstake/etc.` as returning `Result<TxReturnType<K, false>>` (single hash) is **wrong** — they return `Result<TxHashPair>` because they always relay spoke→hub. Only `approve` returns a single hash.
|
|
120
|
+
2. **Forgetting `raw: true` on the allowance query.** TypeScript error: `Property 'walletProvider' is missing`. `isAllowanceValid` requires `WalletProviderSlot<K, Raw>`; `raw: false` would force a wallet provider. Use `raw: true` for read-only.
|
|
121
|
+
3. **Forgetting to remove the v1 `account` field from params.** v2 uses `srcAddress`. If both are set, TypeScript rejects the literal.
|
|
122
|
+
4. **`getStakingInfo(hubAddress)` is not a public method in v2.** v1 had it for direct hub queries. v2 has `getStakingInfoFromSpoke(srcAddress, srcChainKey)` which derives the hub wallet internally. Use the spoke variant.
|
|
123
|
+
5. **`UnstakingInfo` no longer accepts `userAddress` separately.** v1 took both `spokeProvider` and `userAddress` props but ignored `userAddress` inside. v2 takes `srcAddress` and uses it.
|
|
124
|
+
|
|
125
|
+
## Verification
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pnpm -C <your-app-dir> checkTs
|
|
129
|
+
|
|
130
|
+
# Targeted scans:
|
|
131
|
+
grep -rE "spokeProvider:\s*\w+|account:\s*[`'][^`']+['\"`]" src/ # leftover v1 patterns
|
|
132
|
+
grep -rE "isStakingError\b|StakingError\b" src/
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Cross-references
|
|
136
|
+
|
|
137
|
+
- v2 staking usage: [`../../integration/features/staking.md`](../../integration/features/staking.md).
|
|
138
|
+
- Cross-cutting prerequisites listed in [`../README.md`](../README.md).
|