@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.
Files changed (57) hide show
  1. package/README.md +91 -7
  2. package/ai-exported/AGENTS.md +99 -0
  3. package/ai-exported/integration/README.md +41 -0
  4. package/ai-exported/integration/ai-rules.md +75 -0
  5. package/ai-exported/integration/architecture.md +519 -0
  6. package/ai-exported/integration/chain-specifics.md +189 -0
  7. package/ai-exported/integration/features/README.md +19 -0
  8. package/ai-exported/integration/features/auxiliary-services.md +189 -0
  9. package/ai-exported/integration/features/bridge.md +136 -0
  10. package/ai-exported/integration/features/dex.md +182 -0
  11. package/ai-exported/integration/features/icx-bnusd-baln.md +181 -0
  12. package/ai-exported/integration/features/money-market.md +198 -0
  13. package/ai-exported/integration/features/staking.md +166 -0
  14. package/ai-exported/integration/features/swap.md +207 -0
  15. package/ai-exported/integration/quickstart.md +213 -0
  16. package/ai-exported/integration/recipes/README.md +21 -0
  17. package/ai-exported/integration/recipes/backend-server-init.md +69 -0
  18. package/ai-exported/integration/recipes/chain-key-narrowing.md +65 -0
  19. package/ai-exported/integration/recipes/gas-estimation.md +33 -0
  20. package/ai-exported/integration/recipes/initialize-sodax.md +53 -0
  21. package/ai-exported/integration/recipes/raw-tx-flow.md +71 -0
  22. package/ai-exported/integration/recipes/result-and-errors.md +104 -0
  23. package/ai-exported/integration/recipes/signed-tx-flow.md +46 -0
  24. package/ai-exported/integration/recipes/testing.md +101 -0
  25. package/ai-exported/integration/reference/README.md +18 -0
  26. package/ai-exported/integration/reference/chain-keys.md +67 -0
  27. package/ai-exported/integration/reference/error-codes.md +165 -0
  28. package/ai-exported/integration/reference/glossary.md +32 -0
  29. package/ai-exported/integration/reference/public-api.md +138 -0
  30. package/ai-exported/integration/reference/wallet-providers.md +62 -0
  31. package/ai-exported/migration/README.md +58 -0
  32. package/ai-exported/migration/ai-rules.md +80 -0
  33. package/ai-exported/migration/breaking-changes/architecture.md +342 -0
  34. package/ai-exported/migration/breaking-changes/result-and-errors.md +363 -0
  35. package/ai-exported/migration/breaking-changes/type-system.md +321 -0
  36. package/ai-exported/migration/checklist.md +61 -0
  37. package/ai-exported/migration/features/README.md +35 -0
  38. package/ai-exported/migration/features/auxiliary-services.md +156 -0
  39. package/ai-exported/migration/features/bridge.md +125 -0
  40. package/ai-exported/migration/features/dex.md +143 -0
  41. package/ai-exported/migration/features/icx-bnusd-baln.md +151 -0
  42. package/ai-exported/migration/features/money-market.md +214 -0
  43. package/ai-exported/migration/features/staking.md +138 -0
  44. package/ai-exported/migration/features/swap.md +198 -0
  45. package/ai-exported/migration/recipes.md +288 -0
  46. package/ai-exported/migration/reference/README.md +18 -0
  47. package/ai-exported/migration/reference/deleted-exports.md +126 -0
  48. package/ai-exported/migration/reference/error-code-crosswalk.md +104 -0
  49. package/ai-exported/migration/reference/return-shapes.md +49 -0
  50. package/ai-exported/migration/reference/sodax-config.md +52 -0
  51. package/dist/index.cjs +32076 -31544
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.cts +8604 -7136
  54. package/dist/index.d.ts +8604 -7136
  55. package/dist/index.mjs +31893 -31402
  56. package/dist/index.mjs.map +1 -1
  57. package/package.json +20 -12
@@ -0,0 +1,61 @@
1
+ # v1 → v2 cross-cutting migration checklist
2
+
3
+ Work this top-down. Each step is independent enough to land as its own commit; the order minimizes typecheck noise.
4
+
5
+ ```
6
+ [ ] 1. Replace every *_MAINNET_CHAIN_ID constant with ChainKeys.* (mechanical).
7
+ See breaking-changes/type-system.md § "Chain IDs".
8
+ [ ] 2. Replace every SpokeChainId / ChainId type alias with SpokeChainKey.
9
+ Mechanical rename. Same value union.
10
+ [ ] 3. Rename XToken.xChainId → XToken.chainKey. Same for any consumer types
11
+ that mirrored that field.
12
+ [ ] 4. Remove any direct dependency on @sodax/types from package.json.
13
+ v2 @sodax/sdk barrel re-exports the entire types surface.
14
+ [ ] 5. Delete every *SpokeProvider class instantiation. Replace with passing
15
+ walletProvider (an I*WalletProvider instance) directly into SDK call
16
+ payloads.
17
+ [ ] 6. Replace every isXxxSpokeProvider(provider) guard with a chain-key
18
+ compare: chainKey === ChainKeys.<X>_MAINNET, or use
19
+ is<Family>ChainKeyType(chainKey) from @sodax/sdk.
20
+ [ ] 7. Add { raw: false } discriminator to every signed call shape that
21
+ previously took a positional spoke provider:
22
+ { intentParams, spokeProvider } → { params, raw: false, walletProvider }
23
+ [ ] 8. Add srcChainKey + srcAddress to every action params object that didn't
24
+ carry them in v1 (most MM, staking, dex, bridge, migration param shapes).
25
+ [ ] 9. Convert every await sodax.<service>.<method>(...) call site that previously
26
+ threw to branch on result.ok / result.error. Use isSodaxError(e) for type
27
+ narrowing.
28
+ [ ] 10. Delete imports of MoneyMarketError, IntentError, StakingError, BridgeError,
29
+ MigrationError, AssetServiceError, ConcentratedLiquidityError, RelayError,
30
+ plus the five Partner error types and their type-guard helpers.
31
+ [ ] 11. Replace any walked global lookup (hubAssets[chainId][address],
32
+ moneyMarketSupportedTokens[chainId], SodaTokens[...]) with the equivalent
33
+ sodax.config.* / sodax.moneyMarket.getSupportedTokens*() calls.
34
+ [ ] 12. Initialize ConfigService at app startup: await sodax.config.initialize().
35
+ Falls back to packaged defaults if the backend is unreachable.
36
+ [ ] 13. Add { raw: true } to any read-only allowance check that previously took
37
+ a spoke provider but didn't actually consult it (the underlying SDK
38
+ method now requires WalletProviderSlot).
39
+ [ ] 14. For every CreateIntentResult consumer: stop destructuring as a tuple;
40
+ the v2 shape is { tx, intent, relayData }. Stop destructuring tx-pair
41
+ results as arrays — every cross-chain mutation returns
42
+ TxHashPair = { srcChainTxHash, dstChainTxHash }.
43
+ [ ] 15. For every backend-API call site: every method on IConfigApi now returns
44
+ Promise<Result<T>>. Wrap or unwrap accordingly.
45
+ [ ] 16. (Optional) Adopt isSodaxError(e) over instanceof SodaxError in cross-bundle
46
+ / cross-realm contexts.
47
+ [ ] 17. Run pnpm tsc --noEmit and start crossing items off the typecheck output.
48
+ Use each remaining error category to navigate to the relevant per-feature
49
+ migration file in features/.
50
+ ```
51
+
52
+ After items 1–17 land, the typecheck should be clean. The remaining work is per-feature behavior (cross-chain borrow / repay deltas, staking action discriminators, etc.) covered in [`features/`](features/).
53
+
54
+ ## Reading order while working the checklist
55
+
56
+ - Items 1–4: [`breaking-changes/type-system.md`](breaking-changes/type-system.md).
57
+ - Items 5–6, 11–12: [`breaking-changes/architecture.md`](breaking-changes/architecture.md).
58
+ - Items 7–8, 13: [`breaking-changes/architecture.md`](breaking-changes/architecture.md) § "WalletProviderSlot" + per-feature files.
59
+ - Items 9–10, 14, 16: [`breaking-changes/result-and-errors.md`](breaking-changes/result-and-errors.md).
60
+ - Item 15: [`features/auxiliary-services.md`](features/auxiliary-services.md) § "BackendApiService".
61
+ - Item 17 + the long tail: [`features/`](features/), one per feature you use.
@@ -0,0 +1,35 @@
1
+ # Per-feature migration playbooks — v1 → v2
2
+
3
+ One file per feature, paired with the v2 design counterpart in [`../../integration/features/`](../../integration/features/) (same filename).
4
+
5
+ | Feature | Migration file |
6
+ |---|---|
7
+ | Swap | [`swap.md`](swap.md) |
8
+ | Money Market | [`money-market.md`](money-market.md) |
9
+ | Staking | [`staking.md`](staking.md) |
10
+ | Bridge | [`bridge.md`](bridge.md) |
11
+ | DEX | [`dex.md`](dex.md) |
12
+ | ICX/bnUSD/BALN | [`icx-bnusd-baln.md`](icx-bnusd-baln.md) |
13
+ | Auxiliary services | [`auxiliary-services.md`](auxiliary-services.md) — `PartnerService` + `RecoveryService` + `BackendApiService`. The backend-API one is the load-bearing change: every method now returns `Promise<Result<T>>`. |
14
+
15
+ ## Reading order within a feature
16
+
17
+ Each per-feature file follows the same shape:
18
+
19
+ 1. **TL;DR** — the load-bearing changes in 5–10 bullets.
20
+ 2. **Type / symbol cheat sheet** — exact renames and shape diffs.
21
+ 3. **Per-method delta** — call shape v1 vs v2, return type v1 vs v2, error model v1 vs v2.
22
+ 4. **Worked example** — a representative migration of one call site, before / after.
23
+ 5. **Pitfalls** — the things that look right but compile or run wrong.
24
+ 6. **Verification** — what to run to confirm the port is complete.
25
+
26
+ ## Cross-cutting prerequisites
27
+
28
+ Before reading any feature file, finish the cross-cutting work in:
29
+
30
+ - [`../README.md`](../README.md) — top-level checklist (chain-id rename, type renames, `walletProvider` extraction).
31
+ - [`../breaking-changes/type-system.md`](../breaking-changes/type-system.md) — type imports must compile first.
32
+ - [`../breaking-changes/architecture.md`](../breaking-changes/architecture.md) — `*SpokeProvider` deletion, `ConfigService`.
33
+ - [`../breaking-changes/result-and-errors.md`](../breaking-changes/result-and-errors.md) — `Result<T>` and `SodaxError<C>`.
34
+
35
+ Per-feature work assumes those are done.
@@ -0,0 +1,156 @@
1
+ # Auxiliary services migration — v1 → v2
2
+
3
+ Pure-SDK migration playbook for `PartnerService`, `RecoveryService`, and `BackendApiService`. Three small services grouped because each has a small surface area.
4
+
5
+ Pair: [`../../integration/features/auxiliary-services.md`](../../integration/features/auxiliary-services.md).
6
+
7
+ ## TL;DR
8
+
9
+ 1. **`PartnerService`:** standard pattern. Drop `spokeProvider`; pass `walletProvider`. Add `srcChainKey` + `srcAddress` to claim params. v1's 5 typed errors collapse into `SodaxError<C>` with `feature: 'partner'`.
10
+ 2. **`RecoveryService`:** **new in v2.** No v1 equivalent — there's no migration to do for code that didn't exist before. Just integration: see [`../../integration/features/auxiliary-services.md`](../../integration/features/auxiliary-services.md).
11
+ 3. **`BackendApiService`:** the load-bearing change. **Every method now returns `Promise<Result<T>>`** (v1 returned plain `Promise<T>` and threw on failure). `IConfigApi` implementations must update method signatures.
12
+ 4. **`SubmitSwapTxRequest.srcChainId` → `srcChainKey`.** And `relayData` field is `string` (`relayData.payload`), not the `RelayExtraData` object. (Cross-cutting detail; covered in detail in [`swap.md`](swap.md).)
13
+
14
+ ## `PartnerService`
15
+
16
+ ### Type / symbol cheat sheet
17
+
18
+ | Type | v1 shape | v2 shape | Notes |
19
+ |---|---|---|---|
20
+ | Partner action params | non-generic | now generic `<K>` with `srcChainKey`, `srcAddress` | |
21
+ | Partner errors (5 types) | `PartnerFeeClaimError<...>` and 4 siblings | `SodaxError<C>` with `feature: 'partner'` | All 5 v1 typed errors collapse. |
22
+
23
+ ### v1 → v2 error code crosswalk
24
+
25
+ All 5 v1 partner error codes map to `EXECUTION_FAILED` with `error.context.action` discriminating between the operations.
26
+
27
+ ### Per-method delta
28
+
29
+ Partner operations live on `sodax.partners.feeClaim` (a `PartnerFeeClaimService`), not directly on `sodax.partners`. v1 method names also changed:
30
+
31
+ ```diff
32
+ - await sodax.partners.claimFees({ /* … */ }, spokeProvider);
33
+ + // Approve once, then configure auto-swap preference, then run swaps.
34
+ + // Full method list lives in integration/features/auxiliary-services.md.
35
+ + const approved = await sodax.partners.feeClaim.isTokenApproved({ token, srcAddress });
36
+ + if (approved.ok && !approved.value) {
37
+ + await sodax.partners.feeClaim.approveToken({
38
+ + params: { token, amount },
39
+ + raw: false,
40
+ + walletProvider,
41
+ + });
42
+ + }
43
+ ```
44
+
45
+ ### Pitfalls
46
+
47
+ 1. **Partner methods moved.** They live on `sodax.partners.feeClaim`, not `sodax.partners` directly. The parent only exposes `feeClaim` and `config` as public fields.
48
+ 2. **All 5 v1 partner errors collapse to `feature: 'partner'`.** Even though they share the `EXECUTION_FAILED` code with every other feature.
49
+
50
+ ---
51
+
52
+ ## `RecoveryService`
53
+
54
+ **No v1 equivalent.** v1 didn't have a public recovery service. Failed cross-chain operations had to be handled ad-hoc.
55
+
56
+ If you have v1 code that worked around this (e.g. manually walked the hub wallet abstraction to find stuck assets), replace it with `fetchHubAssetBalances` + `withdrawHubAsset`:
57
+
58
+ ```ts
59
+ const balances = await sodax.recovery.fetchHubAssetBalances({ /* user / hub-wallet args */ });
60
+ if (balances.ok && balances.value.length > 0) {
61
+ await sodax.recovery.withdrawHubAsset({
62
+ params: { /* hub-asset address, amount, destination spoke chain + address */ },
63
+ raw: false,
64
+ walletProvider: sonicWp,
65
+ });
66
+ }
67
+ ```
68
+
69
+ See [`../../integration/features/auxiliary-services.md`](../../integration/features/auxiliary-services.md) § "RecoveryService".
70
+
71
+ ---
72
+
73
+ ## `BackendApiService`
74
+
75
+ The load-bearing v1 → v2 change here is **`Result`-wrapping every method**.
76
+
77
+ ### Type / symbol cheat sheet
78
+
79
+ | Method | v1 return | v2 return |
80
+ |---|---|---|
81
+ | `submitSwapTx` | `Promise<SubmitSwapTxResponse>` | `Promise<Result<SubmitSwapTxResponse>>` |
82
+ | `getIntentByHash` | `Promise<IntentResponse>` | `Promise<Result<IntentResponse>>` |
83
+ | `getIntentByTxHash` | (n/a in v1) | `Promise<Result<IntentResponse>>` (v2-new) |
84
+ | `getOrderbook` (was `getSolverOrderbook`) | `Promise<OrderbookEntry[]>` | `Promise<Result<OrderbookEntry[]>>` |
85
+ | `getUserIntents` (was `getUserSwapHistory`) | `Promise<IntentResponse[]>` | `Promise<Result<IntentResponse[]>>` |
86
+ | `getChains` | `Promise<ChainConfig[]>` | `Promise<Result<GetChainsApiResponse>>` |
87
+ | `getSwapTokens` | `Promise<SwapTokenConfig>` | `Promise<Result<GetSwapTokensApiResponse>>` |
88
+ | `getSwapTokensByChainId` | `Promise<XToken[]>` | `Promise<Result<XToken[]>>` |
89
+ | `getMoneyMarketTokens` | `Promise<MMTokenConfig>` | `Promise<Result<GetMoneyMarketTokensApiResponse>>` |
90
+ | `getMoneyMarketTokensByChainId` | `Promise<XToken[]>` | `Promise<Result<XToken[]>>` |
91
+ | `SubmitSwapTxRequest.srcChainId` | numeric chain id | renamed → `srcChainKey: SpokeChainKey` |
92
+ | `SubmitSwapTxRequest.relayData` | `RelayExtraData` object | now `string` (use `relayData.payload`) |
93
+
94
+ ### Per-method delta
95
+
96
+ ```diff
97
+ - const response: SubmitSwapTxResponse = await sodax.backendApi.submitSwapTx(request);
98
+ - // throws on failure
99
+ + const result = await sodax.backendApi.submitSwapTx({
100
+ + txHash: spokeTxHash as string,
101
+ + srcChainKey: src.chain, // was: srcChainId
102
+ + walletAddress: '0x…',
103
+ + intent: swapIntentData,
104
+ + relayData: relayData.payload, // was: relayData (object)
105
+ + });
106
+ + if (!result.ok) {
107
+ + // result.error: SodaxError with feature: 'swap', context.api: 'backend'
108
+ + return;
109
+ + }
110
+ + const response = result.value;
111
+ ```
112
+
113
+ ### Custom `IConfigApi` (sandbox / test fixtures)
114
+
115
+ If you implemented `IConfigApi` for a sandbox or test fixture:
116
+
117
+ ```diff
118
+ const sandboxApi: IConfigApi = {
119
+ - async getChains(): Promise<ChainConfig[]> {
120
+ - return [/* fixture */];
121
+ - },
122
+ - async getSwapTokens(): Promise<SwapTokenConfig> { /* … */ },
123
+ + async getChains(): Promise<Result<ChainConfig[], unknown>> {
124
+ + return { ok: true, value: [/* fixture */] };
125
+ + },
126
+ + async getSwapTokens(): Promise<Result<SwapTokenConfig, unknown>> { /* … */ },
127
+ // …5 methods total
128
+ };
129
+ ```
130
+
131
+ Every method on the contract returns `Promise<Result<T>>` in v2.
132
+
133
+ ### Pitfalls
134
+
135
+ 1. **`SubmitSwapTxRequest.relayData` is `string`, not the `RelayExtraData` object.** v1 took the object; v2 takes the `payload` field as a string.
136
+ 2. **Backend errors carry `error.context.api === 'backend'`** but the `feature` reflects the call site (`'swap'` for `submitSwapTx`, `'moneyMarket'` for MM-related backend calls, etc.). Use both for logger tag pairs.
137
+ 3. **Custom `IConfigApi` implementations must return `Result<T>`** — old throw-on-error implementations will compile-error against the v2 interface.
138
+
139
+ ---
140
+
141
+ ## Verification
142
+
143
+ ```bash
144
+ pnpm -C <your-app-dir> checkTs
145
+
146
+ # Targeted scans:
147
+ grep -rE "spokeProvider:\s*\w+|isPartnerError\b|PartnerFeeClaimError\b" src/
148
+ grep -rE "srcChainId:\s*\w+|\.relayData(?!\s*\.payload)" src/ # legacy field name + non-string relayData
149
+ ```
150
+
151
+ ## Cross-references
152
+
153
+ - v2 auxiliary services usage: [`../../integration/features/auxiliary-services.md`](../../integration/features/auxiliary-services.md).
154
+ - `submitSwapTx` flow with `createIntent` upstream: [`./swap.md`](swap.md) (the swap migration covers the request-shape changes in detail).
155
+ - Result/error model: [`../breaking-changes/result-and-errors.md`](../breaking-changes/result-and-errors.md).
156
+ - `IConfigApi` Result-wrapping cross-cutting note: [`../breaking-changes/type-system.md`](../breaking-changes/type-system.md) § 6.
@@ -0,0 +1,125 @@
1
+ # Bridge migration — v1 → v2
2
+
3
+ Pure-SDK migration playbook for `BridgeService`.
4
+
5
+ Pair: [`../../integration/features/bridge.md`](../../integration/features/bridge.md).
6
+
7
+ ## TL;DR
8
+
9
+ 1. **Drop `spokeProvider`. Pass `walletProvider` directly.**
10
+ 2. **Add `srcChainKey` + `srcAddress` to `CreateBridgeParams<K>`.** Generic added.
11
+ 3. **`bridge()` returns `Result<TxHashPair>`.** v1 returned a single `string` tx hash; v2 returns `{ srcChainTxHash, dstChainTxHash }` (the spoke + hub tx hashes) wrapped in `Result`.
12
+ 4. **`createBridgeIntent()` is spoke-only — no relay.** Same shape as the swap `createIntent`: returns `{ tx, intent, relayData }` for the spoke transaction. Useful when you need manual relay control.
13
+ 5. **Read methods reshaped.** `getBridgeableAmount` returns `Promise<Result<BridgeLimit>>` (was `Promise<bigint>`) and now takes two `XToken` objects. `getBridgeableTokens` is synchronous (was async) and takes `(from, to, token)`.
14
+ 6. **Errors → `SodaxError` + `Result<T>`.** v1's `BridgeError<BridgeErrorCode>` is gone.
15
+
16
+ ## Type / symbol cheat sheet
17
+
18
+ ### Field-level renames
19
+
20
+ | Type | v1 shape | v2 shape | Notes |
21
+ |---|---|---|---|
22
+ | `CreateBridgeParams` | `{ srcAsset, amount, dstChainId, dstAddress, dstAsset }` | `{ srcChainKey, srcAddress, srcAsset, amount, dstChainKey, dstAddress, dstAsset }` | Now generic `<K>`. `srcChainId`/`dstChainId` (where they appeared) → `srcChainKey`/`dstChainKey`. |
23
+ | Bridge action wrapper | `{ params, spokeProvider }` | `{ params, raw: false, walletProvider }` | Same as every feature. |
24
+ | `bridge` return | `Promise<string>` (tx hash, throws on error) | `Promise<Result<TxHashPair, SodaxError>>` | Tx-pair + Result. |
25
+ | `getBridgeableAmount` | `Promise<bigint>` | `Promise<Result<BridgeLimit, SodaxError>>` where `BridgeLimit = { amount, decimals, type }` | Result-wrapped + richer return shape. Now takes `(from: XToken, to: XToken)` (was `(srcChainId, srcToken, dstChainId, dstToken)`). |
26
+ | `getBridgeableTokens` | `Promise<XToken[]>` | `Result<XToken[], SodaxError>` (synchronous) | Sync now (config-derived). Takes `(from: SpokeChainKey, to: SpokeChainKey, token: string)` — was `(srcToken: XToken)`. |
27
+
28
+ ### Deleted symbols
29
+
30
+ - `BridgeError<BridgeErrorCode>` and `isBridgeError` — replaced by `SodaxError<C>` + `feature: 'bridge'`.
31
+
32
+ ### v1 → v2 error code crosswalk (bridge-specific)
33
+
34
+ | v1 `BridgeErrorCode` | v2 code + context |
35
+ |---|---|
36
+ | `BRIDGE_FAILED` | `EXECUTION_FAILED` (`action: 'bridge'`) |
37
+ | `CREATE_BRIDGE_INTENT_FAILED` | `INTENT_CREATION_FAILED` (`action: 'bridge'`) |
38
+ | `GET_BRIDGEABLE_AMOUNT_FAILED` | `LOOKUP_FAILED` (`method: 'getBridgeableAmount'`) |
39
+ | `GET_BRIDGEABLE_TOKENS_FAILED` | `LOOKUP_FAILED` (`method: 'getBridgeableTokens'`) |
40
+
41
+ ## Per-method delta
42
+
43
+ ### `bridge`
44
+
45
+ ```diff
46
+ - const txHash: string = await sodax.bridge.bridge({
47
+ - params: { srcAsset, amount, dstChainId, dstAddress, dstAsset },
48
+ - spokeProvider,
49
+ - });
50
+ + const result = await sodax.bridge.bridge({
51
+ + params: {
52
+ + srcChainKey: ChainKeys.ARBITRUM_MAINNET,
53
+ + srcAddress: '0x…',
54
+ + srcAsset, amount,
55
+ + dstChainKey: ChainKeys.STELLAR_MAINNET,
56
+ + dstAddress: 'G…',
57
+ + dstAsset,
58
+ + },
59
+ + raw: false,
60
+ + walletProvider,
61
+ + });
62
+ + if (!result.ok) return;
63
+ + const { srcChainTxHash, dstChainTxHash } = result.value;
64
+ ```
65
+
66
+ ### `createBridgeIntent`
67
+
68
+ ```diff
69
+ - await sodax.bridge.createBridgeIntent({ params, spokeProvider });
70
+ + const result = await sodax.bridge.createBridgeIntent({ params, raw: false, walletProvider });
71
+ + if (!result.ok) return;
72
+ + const { tx, intent, relayData } = result.value;
73
+ + // Submit relayData.payload via your custom relay if needed.
74
+ ```
75
+
76
+ ### `getBridgeableAmount` / `getBridgeableTokens`
77
+
78
+ ```diff
79
+ - const amount: bigint = await sodax.bridge.getBridgeableAmount(srcChainId, srcToken.address, dstChainId, dstToken.address);
80
+ + const result = await sodax.bridge.getBridgeableAmount(srcToken, dstToken); // both are XToken objects (each carries chainKey)
81
+ + // result.value is BridgeLimit = { amount, decimals, type }, not a raw bigint.
82
+ + if (!result.ok) return 0n;
83
+ + const amount = result.value;
84
+ ```
85
+
86
+ ### `approve` / `isAllowanceValid`
87
+
88
+ Standard pattern:
89
+
90
+ ```ts
91
+ await sodax.bridge.approve({
92
+ params: { srcChainKey, srcAddress, srcAsset, amount },
93
+ raw: false,
94
+ walletProvider,
95
+ });
96
+
97
+ const allowed = await sodax.bridge.isAllowanceValid({
98
+ params: { srcChainKey, srcAddress, srcAsset, amount },
99
+ raw: true, // read-only
100
+ });
101
+ ```
102
+
103
+ ## Pitfalls
104
+
105
+ 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.
106
+
107
+ 1. **Treating `bridge` return as a string.** v2 returns `Result<TxHashPair>`. Destructure both elements; cast to string at the boundary if your downstream API expects a string.
108
+ 2. **`getBridgeableAmount` reshaped.** Resolves to `Result<BridgeLimit, SodaxError>` (with `BridgeLimit = { amount, decimals, type }`), not raw `Result<bigint>`. UI code that displayed the bigint directly needs `result.value.amount`.
109
+ 3. **`getBridgeableTokens` is synchronous now.** It returns `Result<XToken[]>` directly (no `await`). v1 was a `Promise`. `await` still typechecks but `.then(...)` is a runtime error.
110
+ 4. **Tokens are bridgeable iff they share the same vault.** Same chain pair, same underlying — but if you bridge USDC.e on chain A and the destination's USDC has a different vault, the call rejects with `VALIDATION_FAILED`. Use `getBridgeableTokens(srcChainKey, dstChainKey, srcAsset.address)` to enumerate compatible destinations.
111
+ 4. **`createBridgeIntent` is spoke-only — no relay.** If you call it expecting a finished bridge, you'll have a pending hub-side transfer that never executes. Either use `bridge()` for the full flow, or call the relay layer manually after `createBridgeIntent`.
112
+
113
+ ## Verification
114
+
115
+ ```bash
116
+ pnpm -C <your-app-dir> checkTs
117
+
118
+ grep -rE "spokeProvider:\s*\w+|isBridgeError\b|BridgeError\b" src/
119
+ ```
120
+
121
+ ## Cross-references
122
+
123
+ - v2 bridge usage: [`../../integration/features/bridge.md`](../../integration/features/bridge.md).
124
+ - Stellar destination trustline: [`../../integration/chain-specifics.md`](../../integration/chain-specifics.md).
125
+ - Cross-cutting prerequisites listed in [`../README.md`](../README.md).
@@ -0,0 +1,143 @@
1
+ # DEX migration — v1 → v2
2
+
3
+ Pure-SDK migration playbook for `DexService` (`AssetService` + `ClService`).
4
+
5
+ Pair: [`../../integration/features/dex.md`](../../integration/features/dex.md).
6
+
7
+ ## TL;DR
8
+
9
+ 1. **Drop `spokeProvider`. Pass `walletProvider` directly.** Same as every other feature.
10
+ 2. **Add `srcChainKey` + `srcAddress` to every action params.** All 6 DEX param types (`CreateAssetDepositParams`, `CreateAssetWithdrawParams`, `ClSupplyParams`, `ClIncreaseLiquidityParams`, `ClDecreaseLiquidityParams`, `ClClaimRewardsParams`) gained both fields and a `<K extends SpokeChainKey>` generic.
11
+ 3. **`getPools()` is synchronous in v2.** Was `Promise<PoolKey[]>`; now plain `PoolKey[]`. `.then(...)` is a runtime error.
12
+ 4. **`getAssetsForPool` is chain-key-first.** Was `getAssetsForPool(spokeProvider, poolKey)`; now `getAssetsForPool(srcChainKey, poolKey)`.
13
+ 5. **`getDeposit` is `Result`-wrapped.** Was `(token, spokeProvider) => Promise<bigint>`; now `(poolToken, walletAddress, chainKey) => Promise<Result<bigint>>`.
14
+ 6. **Read-only allowance checks pass `raw: true`.** `assetService.isAllowanceValid` requires the `WalletProviderSlot<K, Raw>` discriminator; the read doesn't actually consult the wallet provider, so `raw: true` is the contract for read-only access.
15
+ 7. **`getPoolData` and `getPositionInfo` use the hub publicClient.** Consumers can pass `sodax.hubProvider.publicClient` when needed.
16
+ 8. **Errors → `SodaxError` + `Result<T>`.** v1's `ConcentratedLiquidityError`, `AssetServiceError` and their type guards are gone.
17
+
18
+ ## Type / symbol cheat sheet
19
+
20
+ ### Field-level renames
21
+
22
+ | Type | v1 shape | v2 shape | Notes |
23
+ |---|---|---|---|
24
+ | `CreateAssetDepositParams` | `{ asset, amount, poolToken, dst? }` | `{ srcChainKey, srcAddress, asset, amount, poolToken, dst? }` | Now generic `<K>`. |
25
+ | `CreateAssetWithdrawParams` | same | same with `srcChainKey, srcAddress` | |
26
+ | `ClSupplyParams` | `{ poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, sqrtPriceX96 }` | + `srcChainKey, srcAddress` | |
27
+ | `ClIncreaseLiquidityParams` | `+ tokenId` | + `srcChainKey, srcAddress` | |
28
+ | `ClDecreaseLiquidityParams` | `{ poolKey, tokenId, liquidity, amount0Min, amount1Min }` | + `srcChainKey, srcAddress` | |
29
+ | `ClClaimRewardsParams` | `{ poolKey, tokenId, tickLower, tickUpper }` | + `srcChainKey, srcAddress` | |
30
+ | `getAssetsForPool` | `(spokeProvider, poolKey)` | `(srcChainKey, poolKey)` | Chain-key-first. Sync. |
31
+ | `getPools` | `Promise<PoolKey[]>` | `PoolKey[]` | Sync now. |
32
+ | `getDeposit` | `(token, spokeProvider) => Promise<bigint>` | `(poolToken, walletAddress, chainKey) => Promise<Result<bigint>>` | Result-wrapped. |
33
+
34
+ ### Deleted symbols
35
+
36
+ - `ConcentratedLiquidityError<ConcentratedLiquidityErrorCode>` and `AssetServiceError<AssetServiceErrorCode>` plus their type guards — replaced by `SodaxError<C>` + `feature: 'dex'`.
37
+ - v1 `getDeposit(token, spokeProvider)` overload — replaced by the chain-key-first signature.
38
+
39
+ ### v1 → v2 error code crosswalk (DEX-specific)
40
+
41
+ | v1 code | v2 code + context |
42
+ |---|---|
43
+ | `DEPOSIT_FAILED` | `EXECUTION_FAILED` (`action: 'deposit'`) |
44
+ | `WITHDRAW_FAILED` | `EXECUTION_FAILED` (`action: 'withdraw'`) |
45
+ | `SUPPLY_LIQUIDITY_FAILED` | `EXECUTION_FAILED` (`action: 'supplyLiquidity'`) |
46
+ | `INCREASE_LIQUIDITY_FAILED` | `EXECUTION_FAILED` (`action: 'increaseLiquidity'`) |
47
+ | `DECREASE_LIQUIDITY_FAILED` | `EXECUTION_FAILED` (`action: 'decreaseLiquidity'`) |
48
+ | `CLAIM_REWARDS_FAILED` | `EXECUTION_FAILED` (`action: 'claimRewards'`) |
49
+ | `GET_POOL_DATA_FAILED` | `LOOKUP_FAILED` (`method: 'getPoolData'`) |
50
+ | `GET_POSITION_INFO_FAILED` | `LOOKUP_FAILED` (`method: 'getPositionInfo'`) |
51
+
52
+ ## Per-method delta
53
+
54
+ ### `deposit` / `withdraw`
55
+
56
+ ```diff
57
+ - await sodax.dex.assetService.deposit({ params, spokeProvider });
58
+ + const result = await sodax.dex.assetService.deposit({
59
+ + params: { srcChainKey, srcAddress, asset, amount, poolToken },
60
+ + raw: false,
61
+ + walletProvider,
62
+ + });
63
+ + if (!result.ok) return;
64
+ + const { srcChainTxHash, dstChainTxHash } = result.value;
65
+ ```
66
+
67
+ ### `supplyLiquidity` (mint new) and `increaseLiquidity` (existing)
68
+
69
+ The pure-helper `createSupplyLiquidityParamsProps` returns the helper-relevant subset (no `srcChainKey`/`srcAddress`). Spread it at the call site:
70
+
71
+ ```diff
72
+ - const params = createSupplyLiquidityParamsProps({ /* … */ });
73
+ - await sodax.dex.clService.supplyLiquidity({ params, spokeProvider });
74
+ + const helperOutput = createSupplyLiquidityParamsProps({ /* … */ });
75
+ + const srcAddress = (await walletProvider.getWalletAddress()) as `0x${string}`;
76
+ + const params = { ...helperOutput, srcChainKey, srcAddress };
77
+ + const result = await sodax.dex.clService.supplyLiquidity({ params, raw: false, walletProvider });
78
+ + if (!result.ok) return;
79
+ + const { dstChainTxHash } = result.value;
80
+ ```
81
+
82
+ ### `getAssetsForPool`
83
+
84
+ ```diff
85
+ - const { token0, token1 } = sodax.dex.clService.getAssetsForPool(spokeProvider, poolKey);
86
+ + const { token0, token1 } = sodax.dex.clService.getAssetsForPool(srcChainKey, poolKey);
87
+ ```
88
+
89
+ ### `getPools`
90
+
91
+ ```diff
92
+ - const pools = await sodax.dex.clService.getPools();
93
+ + const pools = sodax.dex.clService.getPools(); // sync
94
+ ```
95
+
96
+ The `await` form still compiles (TS allows `await` on non-promises) but `.then(...)` is a runtime error.
97
+
98
+ ### `getDeposit`
99
+
100
+ ```diff
101
+ - const balance: bigint = await sodax.dex.assetService.getDeposit(poolToken, spokeProvider);
102
+ + const result = await sodax.dex.assetService.getDeposit(poolToken, walletAddress, chainKey);
103
+ + if (!result.ok) return 0n;
104
+ + const balance = result.value;
105
+ ```
106
+
107
+ ### Allowance (read-only `raw: true`)
108
+
109
+ ```diff
110
+ - const allowed = await sodax.dex.assetService.isAllowanceValid({ params, spokeProvider });
111
+ + const result = await sodax.dex.assetService.isAllowanceValid({ params, raw: true });
112
+ + if (!result.ok) return false;
113
+ + const allowed = result.value;
114
+ ```
115
+
116
+ ## Pitfalls
117
+
118
+ 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.
119
+
120
+ 1. **Forgetting `raw: true` on `isAllowanceValid`.** TypeScript error: `Property 'walletProvider' is missing`.
121
+ 2. **Passing `spokeProvider` to `getAssetsForPool`.** Type error — pass `srcChainKey` instead.
122
+ 3. **`getPools().then(...)` runtime error** — sync now.
123
+ 4. **Reading `spokeProvider.chainConfig.chain.name` for display.** Gone. Use `baseChainInfo[chainKey]?.name` from `@sodax/sdk`.
124
+ 5. **Reading `spokeProvider.walletProvider.getWalletAddress()`.** Gone. The wallet provider you passed is the same one you read from — call its `.getWalletAddress()` directly.
125
+ 6. **`walletProvider.getWalletAddress()` returns `Promise<string>`, not the EVM-branded `` `0x${string}` ``.** SDK params want `GetAddressType<K>`, which for EVM resolves to `` `0x${string}` ``. Cast at the boundary: `(await walletProvider.getWalletAddress()) as \`0x${string}\``.
126
+ 7. **Mint-new vs increase-existing are two distinct SDK methods.** v2 has `clService.supplyLiquidity` (mint) and `clService.increaseLiquidity` (existing tokenId). Pick the right one at the call site based on whether you have an existing position id.
127
+ 8. **`assetService.deposit` and `withdraw` always relay to hub.** If you need spoke-only execution (custom orchestration), use `assetService.executeDeposit` directly — but it's not surfaced through the higher-level wrappers.
128
+
129
+ ## Verification
130
+
131
+ ```bash
132
+ pnpm -C <your-app-dir> checkTs
133
+
134
+ # Targeted scans:
135
+ grep -rE "spokeProvider:\s*\w+|getAssetsForPool\([^,]*Provider" src/
136
+ grep -rE "isConcentratedLiquidityError\b|ConcentratedLiquidityError\b|AssetServiceError\b" src/
137
+ grep -rE "getPools\(\)\.then\b" src/
138
+ ```
139
+
140
+ ## Cross-references
141
+
142
+ - v2 DEX usage: [`../../integration/features/dex.md`](../../integration/features/dex.md).
143
+ - Cross-cutting prerequisites listed in [`../README.md`](../README.md).