@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,189 @@
|
|
|
1
|
+
# Chain specifics — Non-EVM quirks
|
|
2
|
+
|
|
3
|
+
EVM chains (12 of them) work uniformly through `IEvmWalletProvider`. Non-EVM chains have particularities you need to handle. This file documents each chain family's quirks.
|
|
4
|
+
|
|
5
|
+
## Section index
|
|
6
|
+
|
|
7
|
+
1. [Stellar trustline](#1-stellar-trustline) — `STELLAR_MAINNET`. Required before receiving any non-XLM asset.
|
|
8
|
+
2. [Bitcoin PSBT and Radfi](#2-bitcoin-psbt-and-radfi) — `BITCOIN_MAINNET`. PSBT signing; trading wallet; Radfi auth/session.
|
|
9
|
+
3. [Solana PDA derivation](#3-solana-pda-derivation) — `SOLANA_MAINNET`. Deterministic addresses; one-time setup utilities.
|
|
10
|
+
4. [ICON Hana wallet](#4-icon-hana-wallet) — `ICON_MAINNET`. Low-level Hana-extension helpers; chain key string vs numeric ID.
|
|
11
|
+
5. [NEAR connector discovery](#5-near-connector-discovery) — `NEAR_MAINNET`. Account-id semantics; multiple wallet variants.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Stellar trustline
|
|
16
|
+
|
|
17
|
+
**Requirement:** before a Stellar account can hold or receive a non-XLM asset, it must establish a trustline to the asset issuer. SODAX's bridge / swap / money-market deliver stablecoin assets to Stellar — the destination Stellar account must have an active trustline for the asset, or the operation fails.
|
|
18
|
+
|
|
19
|
+
### How v2 handles it
|
|
20
|
+
|
|
21
|
+
`BridgeService` and other feature services delegate Stellar trustline / allowance handling to the Stellar spoke service internally — there is **no** public `checkStellarTrustline` / `requestStellarTrustline` method on `BridgeService`. When you call `sodax.bridge.approve({ params, raw: false, walletProvider })` with a Stellar `walletProvider`, the spoke service handles the trustline operation for you.
|
|
22
|
+
|
|
23
|
+
For direct trustline interaction outside the standard `approve` flow, reach for the Stellar spoke service:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { ChainKeys, type StellarSpokeService } from '@sodax/sdk';
|
|
27
|
+
|
|
28
|
+
const stellarSpoke = sodax.spoke.getSpokeService(ChainKeys.STELLAR_MAINNET) as StellarSpokeService;
|
|
29
|
+
// Use the spoke service's typed methods for Stellar-specific operations.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### When to gate
|
|
33
|
+
|
|
34
|
+
Before any swap / bridge / money-market operation that **delivers** a non-XLM asset to a Stellar destination, the destination wallet must already trust the asset. Failed trustline checks surface as `VALIDATION_FAILED` errors; use `error.context.reason` to disambiguate. The standard pattern for app code is to call `sodax.bridge.approve(...)` (or the matching feature's approve) on the Stellar wallet first, which establishes the trustline as a side effect.
|
|
35
|
+
|
|
36
|
+
### Pitfall
|
|
37
|
+
|
|
38
|
+
Stellar accounts that have never held the asset have **no** trustline — receiving will fail. The error surfaces as `VALIDATION_FAILED` with `context.reason: 'trustlineMissing'` (or similar). Treat the missing-trustline state as a one-time setup step gated by the standard `approve` flow.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 2. Bitcoin PSBT and Radfi
|
|
43
|
+
|
|
44
|
+
Bitcoin support uses Partially Signed Bitcoin Transactions (PSBT) — a different model than EVM tx signing. The wallet provider (`BTCWalletProvider`) handles PSBT construction and signing internally.
|
|
45
|
+
|
|
46
|
+
### Address types
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
type BtcAddressType = 'P2PKH' | 'P2SH' | 'P2WPKH' | 'P2TR';
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`BTCWalletProvider` config takes an `addressType`. `'P2WPKH'` (native SegWit) is the modern default; `'P2TR'` (Taproot) is supported but may have lower compatibility with some on-chain logic.
|
|
53
|
+
|
|
54
|
+
### Radfi (auth + trading wallet)
|
|
55
|
+
|
|
56
|
+
SODAX uses the Radfi infrastructure for Bitcoin. Each user gets a derived "trading wallet" funded from their main BTC address. Operations consume UTXOs from the trading wallet rather than directly from the user's main address.
|
|
57
|
+
|
|
58
|
+
The Radfi provider is owned by `BitcoinSpokeService`. Reach it via the spoke router:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { ChainKeys, type BitcoinSpokeService } from '@sodax/sdk';
|
|
62
|
+
|
|
63
|
+
const btcSpoke = sodax.spoke.getSpokeService(ChainKeys.BITCOIN_MAINNET) as BitcoinSpokeService;
|
|
64
|
+
const radfi = btcSpoke.radfi; // RadfiProvider instance
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Most consumer flows don't need to touch `radfi` directly — `sodax.bridge.bridge(...)`, `sodax.swaps.createIntent(...)`, etc. handle the Radfi auth + trading-wallet routing internally on the Bitcoin path. For explicit lifecycle management:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// Authenticate against Radfi (the wallet signs an auth message):
|
|
71
|
+
await radfi.authenticateWithWallet(/* args per RadfiProvider source */);
|
|
72
|
+
|
|
73
|
+
// Fetch the trading wallet for an address (creating it if needed):
|
|
74
|
+
const tradingWallet = await radfi.getTradingWallet(personalAddress);
|
|
75
|
+
|
|
76
|
+
// Read the trading wallet's BTC + token balances:
|
|
77
|
+
const balance = await radfi.getBalance(tradingWallet.address);
|
|
78
|
+
|
|
79
|
+
// Check whether a trading wallet exists without provisioning one:
|
|
80
|
+
const exists = await radfi.checkIfTradingWalletExists(personalAddress);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Other public methods on `RadfiProvider` you may need: `setRadfiAccessToken`, `refreshAccessToken`, `createTradingWallet`, `createWithdrawTransaction`, `requestRadfiSignature`, `getExpiredUtxos`, `buildRenewUtxoTransaction`, `signAndBroadcastRenewUtxo`, `withdrawToUser`, `signAndBroadcastWithdraw`, `getMaxWithdrawable`. Read `RadfiProvider` source for argument shapes — the API surface is broader than typical chain providers.
|
|
84
|
+
|
|
85
|
+
### Pitfall
|
|
86
|
+
|
|
87
|
+
`BitcoinSpokeService.radfi` is what feature services use under the hood. Bypassing the feature services and driving Radfi yourself works but is rarely needed — and you have to wire token balances + UTXO state manually. Prefer the standard feature flows unless you specifically need lifecycle control.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. Solana PDA derivation
|
|
92
|
+
|
|
93
|
+
Program Derived Addresses (PDAs) are deterministic Solana addresses derived from a program ID + seeds. SODAX uses PDAs for the user's spoke-side state on Solana.
|
|
94
|
+
|
|
95
|
+
The SDK derives PDAs internally — consumers don't usually need to do this manually. For advanced use cases (preflight, off-chain state lookups), reach into the Solana spoke service via the router:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { ChainKeys, type SolanaSpokeService } from '@sodax/sdk';
|
|
99
|
+
|
|
100
|
+
const solanaSpoke = sodax.spoke.getSpokeService(ChainKeys.SOLANA_MAINNET) as SolanaSpokeService;
|
|
101
|
+
// Use the spoke service's typed methods for any PDA-related read or write.
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `SolanaSpokeService` specifics
|
|
105
|
+
|
|
106
|
+
- Solana txs are different shape from EVM (multiple instructions per tx; signed once globally).
|
|
107
|
+
- `TxReturnType<typeof ChainKeys.SOLANA_MAINNET, false>` is the base58-encoded signature string (not `0x…`).
|
|
108
|
+
- `SolanaRawTransaction` is the raw-tx return for `raw: true` — base64-encoded message for downstream signing.
|
|
109
|
+
|
|
110
|
+
### Pitfall
|
|
111
|
+
|
|
112
|
+
Solana addresses are base58 PublicKey strings, not `0x…` hex. `GetAddressType<typeof ChainKeys.SOLANA_MAINNET>` resolves to a base58-typed string brand; passing a hex address is a TypeScript error.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 4. ICON Hana wallet
|
|
117
|
+
|
|
118
|
+
ICON uses the Hana browser-extension wallet for dApp signing. The SDK ships low-level helper functions (file: `HanaWalletConnector.ts`) that wrap the Hana extension's JSON-RPC interface; consumers compose them into an `IIconWalletProvider` implementation.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { requestAddress, requestSigning, requestJsonRpc } from '@sodax/sdk';
|
|
122
|
+
|
|
123
|
+
const result = await requestAddress(); // returns Result<IconAddress>
|
|
124
|
+
if (result.ok) {
|
|
125
|
+
const address = result.value; // 'hx…'
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
ICON spoke calls require an `IIconWalletProvider`. Implementations either compose the helper functions above into a class your app owns, or use the reference implementation that ships in `@sodax/wallet-sdk-core` (separate package, install separately).
|
|
130
|
+
|
|
131
|
+
### `ChainKeys.ICON_MAINNET` is a string `'0x1.icon'`
|
|
132
|
+
|
|
133
|
+
The ICON chain key is the **string** `'0x1.icon'`, not the legacy numeric chain id `0x1`. This trips up code that did `Number(chainId)` to coerce — that returns `NaN` for `'0x1.icon'`.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
ChainKeys.ICON_MAINNET === '0x1.icon'; // true
|
|
137
|
+
Number(ChainKeys.ICON_MAINNET); // NaN
|
|
138
|
+
chainKey === ChainKeys.ICON_MAINNET; // works
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Address types
|
|
142
|
+
|
|
143
|
+
- `hx…` — externally-owned account (user wallet).
|
|
144
|
+
- `cx…` — contract account.
|
|
145
|
+
|
|
146
|
+
`GetAddressType<typeof ChainKeys.ICON_MAINNET>` accepts both via a string brand.
|
|
147
|
+
|
|
148
|
+
### Injective (similar pattern)
|
|
149
|
+
|
|
150
|
+
Injective is a separate chain family but uses a similar wallet-extension model. Wallet helper: `Injective20Token` (in `@sodax/sdk`'s injective utilities). `IInjectiveWalletProvider` implementations can use Keplr, Leap, or other Cosmos-ecosystem wallets.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 5. NEAR connector discovery
|
|
155
|
+
|
|
156
|
+
NEAR has multiple wallet variants (NEAR Wallet, MyNearWallet, Meteor, Sender, …). NEAR spoke calls require an `INearWalletProvider`. The interface is exported from `@sodax/sdk`; consumers supply an object satisfying it. A reference implementation ships in `@sodax/wallet-sdk-core` (separate package — install separately); browser-side connector discovery for the multiple NEAR wallet variants happens there, not in `@sodax/sdk` itself.
|
|
157
|
+
|
|
158
|
+
### Account ID semantics
|
|
159
|
+
|
|
160
|
+
NEAR addresses come in two forms:
|
|
161
|
+
|
|
162
|
+
- **Named accounts** — `alice.near`, `mybiz.near`. Human-readable.
|
|
163
|
+
- **Implicit accounts** — 64-character hex strings.
|
|
164
|
+
|
|
165
|
+
Both are valid. `GetAddressType<typeof ChainKeys.NEAR_MAINNET>` accepts both via a string type.
|
|
166
|
+
|
|
167
|
+
### Pitfall
|
|
168
|
+
|
|
169
|
+
`NearWalletProvider` requires the `accountId` field at construction (alongside `privateKey`). Unlike EVM, NEAR can't derive an account from a key alone — keys are scoped to accounts.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Other non-EVM chains
|
|
174
|
+
|
|
175
|
+
| Chain | Notes |
|
|
176
|
+
|---|---|
|
|
177
|
+
| **Sui** (`SUI_MAINNET`) | Address: 32-byte `0x…` (different from EVM addresses despite the prefix). Wallet provider `ISuiWalletProvider` uses `@mysten/sui` under the hood. |
|
|
178
|
+
| **Stacks** (`STACKS_MAINNET`) | Address: `SP…` (mainnet) / `ST…` (testnet). Uses `@stacks/transactions` for tx construction. |
|
|
179
|
+
| **Injective** (`INJECTIVE_MAINNET`) | Cosmos-ecosystem chain. Address: `inj1…`. Wallet provider uses `@injectivelabs/sdk-ts`. |
|
|
180
|
+
|
|
181
|
+
Each has its own `I*WalletProvider` interface with chain-specific signing methods. The `chainType` discriminant on every `I*WalletProvider` instance lets you narrow at runtime without `instanceof`.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Cross-references
|
|
186
|
+
|
|
187
|
+
- `WalletProviderSlot` and chain narrowing: [`architecture.md`](architecture.md) §§ 5, 6.
|
|
188
|
+
- Per-chain wallet provider interfaces: [`reference/`](reference/) § 2.
|
|
189
|
+
- Cast-at-boundary pattern for chain-narrowed providers: [`recipes/`](recipes/) § 5.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Features — `@sodax/sdk` v2
|
|
2
|
+
|
|
3
|
+
One file per feature service. Each file documents the v2 API surface, common call shapes, return types, and error codes you can expect.
|
|
4
|
+
|
|
5
|
+
| Feature | Service class | What it does |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| [`swap.md`](swap.md) | `SwapService` | Intent-based swaps via the solver. Market and limit orders. Cross-chain by default. |
|
|
8
|
+
| [`money-market.md`](money-market.md) | `MoneyMarketService` | Cross-chain lending/borrowing. Supply, borrow, withdraw, repay. Reserves and user-position reads. |
|
|
9
|
+
| [`staking.md`](staking.md) | `StakingService` | SODA → xSoda staking via ERC-4626 vault. Stake, unstake (with penalty curve), instant unstake (slippage), claim, cancel. |
|
|
10
|
+
| [`bridge.md`](bridge.md) | `BridgeService` | Cross-chain token transfer via vault. `bridge` returns `TxHashPair = { srcChainTxHash, dstChainTxHash }`. Bridgeable-amount queries respect vault deposit limits. |
|
|
11
|
+
| [`dex.md`](dex.md) | `ClService` + `AssetService` | Uniswap-V3-style concentrated liquidity positions. Asset deposit/withdraw. Increase/decrease/claim flows. |
|
|
12
|
+
| [`icx-bnusd-baln.md`](icx-bnusd-baln.md) | `MigrationService` (the SDK module — not v1→v2 porting) | Legacy ICON ecosystem token migration. ICX ↔ SODA, legacy bnUSD ↔ new bnUSD, BALN → SODA with lockup multipliers. |
|
|
13
|
+
| [`auxiliary-services.md`](auxiliary-services.md) | `PartnerService` + `RecoveryService` + `BackendApiService` | Three small APIs grouped together: partner-fee claiming, hub-wallet asset recovery, backend HTTP client. |
|
|
14
|
+
|
|
15
|
+
All feature services are constructed and wired by the `Sodax` facade. You don't instantiate them directly — access them via `sodax.swaps`, `sodax.moneyMarket`, etc. See [`../architecture.md`](../architecture.md) for the service graph.
|
|
16
|
+
|
|
17
|
+
## Cross-references to migration
|
|
18
|
+
|
|
19
|
+
For the v1 → v2 port playbook on each feature, see the matching file in [`../../migration/features/`](../../migration/features/) — same filename, different angle.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Auxiliary services — `PartnerService`, `RecoveryService`, `BackendApiService`
|
|
2
|
+
|
|
3
|
+
Three small services grouped together. None has the surface area of the major features (swap, money market, etc.), but they're load-bearing in real consumer flows.
|
|
4
|
+
|
|
5
|
+
## `PartnerService` — `sodax.partners`
|
|
6
|
+
|
|
7
|
+
Partner-fee handling. `PartnerService` itself only exposes `feeClaim: PartnerFeeClaimService` and `config: ConfigService` as public fields. Every operation lives on `sodax.partners.feeClaim`.
|
|
8
|
+
|
|
9
|
+
**Feature tag for errors:** `'partner'`.
|
|
10
|
+
|
|
11
|
+
### Methods (all on `sodax.partners.feeClaim`)
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// Token approval
|
|
15
|
+
sodax.partners.feeClaim.isTokenApproved({ token, srcAddress }): Promise<Result<boolean, Error>>;
|
|
16
|
+
sodax.partners.feeClaim.approveToken<Raw>(args): Promise<Result<TxReturnType, Error>>;
|
|
17
|
+
|
|
18
|
+
// Auto-swap preferences (whether partner-collected fees auto-swap into a target asset)
|
|
19
|
+
sodax.partners.feeClaim.getAutoSwapPreferences(queryAddress): Promise<Result<AutoSwapPreferences, Error>>;
|
|
20
|
+
sodax.partners.feeClaim.setSwapPreference<K, Raw>(args): Promise<Result<TxReturnType, Error>>;
|
|
21
|
+
|
|
22
|
+
// Fee claim flows
|
|
23
|
+
sodax.partners.feeClaim.swap(args): Promise<Result<...>>; // immediate fee swap
|
|
24
|
+
sodax.partners.feeClaim.createIntentAutoSwap<Raw>(args): Promise<Result<...>>; // intent-driven auto-swap
|
|
25
|
+
|
|
26
|
+
// Reads
|
|
27
|
+
sodax.partners.feeClaim.fetchAssetsBalances(args): Promise<Result<...>>;
|
|
28
|
+
sodax.partners.feeClaim.getOriginalAssetAddress(chainId, hubAsset): OriginalAssetAddress | undefined;
|
|
29
|
+
sodax.partners.feeClaim.getSpokeTokenFromOriginalAssetAddress(...): /* … */;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Common call shape
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// 1. Check whether the partner's fee token is approved on the hub:
|
|
36
|
+
const approved = await sodax.partners.feeClaim.isTokenApproved({
|
|
37
|
+
token: '0x…', // hub asset address
|
|
38
|
+
srcAddress: partnerAddress,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// 2. Approve once if not:
|
|
42
|
+
if (approved.ok && !approved.value) {
|
|
43
|
+
await sodax.partners.feeClaim.approveToken({
|
|
44
|
+
params: { token: '0x…', amount: 2n ** 256n - 1n },
|
|
45
|
+
raw: false,
|
|
46
|
+
walletProvider: sonicWp,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 3. Configure auto-swap preference (one-time):
|
|
51
|
+
await sodax.partners.feeClaim.setSwapPreference({
|
|
52
|
+
params: { /* preference fields */ },
|
|
53
|
+
raw: false,
|
|
54
|
+
walletProvider: sonicWp,
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Error codes
|
|
59
|
+
|
|
60
|
+
`feature: 'partner'`. Action methods get the full exec set; reads get `LookupErrorCode` partitioned by `error.context.method`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## `RecoveryService` — `sodax.recovery`
|
|
65
|
+
|
|
66
|
+
Withdraw stuck assets from a user's hub wallet abstraction back to a spoke chain. Useful when a cross-chain operation deposited to the hub but the destination step failed (e.g. relay timeout after the spoke tx landed).
|
|
67
|
+
|
|
68
|
+
**Feature tag for errors:** `'recovery'`.
|
|
69
|
+
|
|
70
|
+
### Methods
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// Read: list balances of all known hub assets in a user's hub wallet abstraction.
|
|
74
|
+
sodax.recovery.fetchHubAssetBalances(args): Promise<Result<HubAssetBalance[], SodaxError>>;
|
|
75
|
+
|
|
76
|
+
// Mutation: withdraw a single hub asset back to a spoke chain.
|
|
77
|
+
// Returns a tx-pair when raw: false (the hub-side spend + the relayed spoke-side receive).
|
|
78
|
+
sodax.recovery.withdrawHubAsset<K extends SpokeChainKey, Raw extends boolean>(
|
|
79
|
+
action: WithdrawHubAssetAction<K, Raw>,
|
|
80
|
+
): Promise<Result<TxReturnType<K, Raw>, SodaxError>>;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Common call shape
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
// 1. Find what's stuck on the hub for this user:
|
|
87
|
+
const balances = await sodax.recovery.fetchHubAssetBalances({ /* user / hub-wallet args */ });
|
|
88
|
+
if (!balances.ok || balances.value.length === 0) return;
|
|
89
|
+
|
|
90
|
+
// 2. Withdraw one entry back to a spoke chain:
|
|
91
|
+
const result = await sodax.recovery.withdrawHubAsset({
|
|
92
|
+
params: {
|
|
93
|
+
/* hub-asset address, amount, destination spoke chain key, destination address */
|
|
94
|
+
},
|
|
95
|
+
raw: false,
|
|
96
|
+
walletProvider: sonicWp,
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Error codes
|
|
101
|
+
|
|
102
|
+
`feature: 'recovery'`. The mutation method returns the full exec set (including relay codes); the read method returns `LookupErrorCode` partitioned by `error.context.method`.
|
|
103
|
+
|
|
104
|
+
### When to use
|
|
105
|
+
|
|
106
|
+
Recovery is a workaround for failed cross-chain operations. Best used **after** investigating why the original operation failed — relay timeouts may resolve on retry; structural failures need fixing first.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## `BackendApiService` — `sodax.backendApi`
|
|
111
|
+
|
|
112
|
+
HTTP client for backend services. Provides intent lookup, swap-tx submission, solver orderbook queries, money-market position/reserve reads, and (internally) config fetching. Most consumer-side code uses just `submitSwapTx`, `getIntentByHash` / `getIntentByTxHash`, and the money-market read methods.
|
|
113
|
+
|
|
114
|
+
**Feature tag for errors:** appears under multiple features depending on the call site (`'swap'` for `submitSwapTx`); errors carry `error.context.api: 'backend'`.
|
|
115
|
+
|
|
116
|
+
### Methods
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
// Swap-related
|
|
120
|
+
sodax.backendApi.submitSwapTx(request, config?): Promise<Result<SubmitSwapTxResponse>>;
|
|
121
|
+
sodax.backendApi.getSubmitSwapTxStatus(...): Promise<Result<...>>;
|
|
122
|
+
sodax.backendApi.getOrderbook(...): Promise<Result<OrderbookEntry[]>>;
|
|
123
|
+
sodax.backendApi.getIntentByHash(intentHash, config?): Promise<Result<IntentResponse>>;
|
|
124
|
+
sodax.backendApi.getIntentByTxHash(txHash, config?): Promise<Result<IntentResponse>>;
|
|
125
|
+
sodax.backendApi.getUserIntents(...): Promise<Result<IntentResponse[]>>;
|
|
126
|
+
|
|
127
|
+
// Money-market reads (these are the canonical reads for MM positions/reserves;
|
|
128
|
+
// MoneyMarketService does NOT expose getReservesData / getUserReservesData / etc.)
|
|
129
|
+
sodax.backendApi.getMoneyMarketPosition(...): Promise<Result<...>>;
|
|
130
|
+
sodax.backendApi.getAllMoneyMarketAssets(config?): Promise<Result<MoneyMarketAsset[]>>;
|
|
131
|
+
sodax.backendApi.getMoneyMarketAsset(reserveAddress, config?): Promise<Result<MoneyMarketAsset>>;
|
|
132
|
+
sodax.backendApi.getMoneyMarketAssetBorrowers(...): Promise<Result<...>>;
|
|
133
|
+
sodax.backendApi.getMoneyMarketAssetSuppliers(...): Promise<Result<...>>;
|
|
134
|
+
sodax.backendApi.getAllMoneyMarketBorrowers(...): Promise<Result<...>>;
|
|
135
|
+
|
|
136
|
+
// Config-API methods (used internally by ConfigService — implements `IConfigApi`)
|
|
137
|
+
sodax.backendApi.getAllConfig(config?): Promise<Result<GetAllConfigApiResponse>>;
|
|
138
|
+
sodax.backendApi.getChains(config?): Promise<Result<GetChainsApiResponse>>;
|
|
139
|
+
sodax.backendApi.getSwapTokens(config?): Promise<Result<GetSwapTokensApiResponse>>;
|
|
140
|
+
sodax.backendApi.getSwapTokensByChainId(...): Promise<Result<XToken[]>>;
|
|
141
|
+
sodax.backendApi.getMoneyMarketTokens(config?): Promise<Result<GetMoneyMarketTokensApiResponse>>;
|
|
142
|
+
sodax.backendApi.getMoneyMarketReserveAssets(...): Promise<Result<...>>;
|
|
143
|
+
sodax.backendApi.getMoneyMarketTokensByChainId(...): Promise<Result<XToken[]>>;
|
|
144
|
+
sodax.backendApi.getRelayChainIdMap(config?): Promise<Result<GetRelayChainIdMapApiResponse>>;
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
All methods return `Result<T, SodaxError>` where the error carries `feature: 'swap' | …` (depending on call site) and `error.context.api === 'backend'`.
|
|
148
|
+
|
|
149
|
+
### Common call shape — `submitSwapTx`
|
|
150
|
+
|
|
151
|
+
After `sodax.swaps.createIntent({ params, raw: false, walletProvider })` returns:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const submitResult = await sodax.backendApi.submitSwapTx({
|
|
155
|
+
txHash: spokeTxHash as string,
|
|
156
|
+
srcChainKey: src.chain,
|
|
157
|
+
walletAddress: '0x…',
|
|
158
|
+
intent: swapIntentData,
|
|
159
|
+
relayData: relayData.payload, // string, not the RelayExtraData object
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (!submitResult.ok) return;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Custom backend (sandbox / fixtures)
|
|
166
|
+
|
|
167
|
+
Inject an `IConfigApi` implementation via `SodaxConfig.backendApi.api`:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const sandboxApi: IConfigApi = {
|
|
171
|
+
async getChains() { return { ok: true, value: [/* fixture */] }; },
|
|
172
|
+
// …
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const sodax = new Sodax({
|
|
176
|
+
backendApi: { url: 'unused', api: sandboxApi },
|
|
177
|
+
});
|
|
178
|
+
await sodax.config.initialize();
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Every method on `IConfigApi` returns `Promise<Result<T>>` in v2.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Cross-references
|
|
186
|
+
|
|
187
|
+
- v1 → v2 migration of these auxiliary services: [`../../migration/features/auxiliary-services.md`](../../migration/features/auxiliary-services.md).
|
|
188
|
+
- The full `submitSwapTx` flow with `createIntent` upstream: [`./swap.md`](swap.md) § "Backend submit-tx flow".
|
|
189
|
+
- Error model context fields (`error.context.api`, `error.context.method`): [`../reference/`](../reference/) § 3.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Bridge — `BridgeService`
|
|
2
|
+
|
|
3
|
+
Cross-chain token transfer via the hub-and-spoke vault architecture. Tokens are bridgeable if they share the same hub-side vault. The flow: spoke deposit → relay to hub → vault balance moves → spoke withdrawal on the destination chain.
|
|
4
|
+
|
|
5
|
+
Access: `sodax.bridge`. Service class: `BridgeService`. Feature tag for errors: `'bridge'`.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
A bridge call deposits the source token into its vault on the hub, then triggers a withdrawal of the same vault's destination-chain wrapper. Different tokens that share the same vault (e.g. multiple wrappings of the same underlying) are bridgeable to each other; tokens with different vaults are not.
|
|
10
|
+
|
|
11
|
+
`bridge()` handles the full lifecycle in one call. For custom relay control, use `createBridgeIntent()` (spoke-only) and call the relay layer manually.
|
|
12
|
+
|
|
13
|
+
## Public methods
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
sodax.bridge.bridge<K>(action: BridgeAction<K, false>): Promise<Result<TxHashPair, SodaxError>>;
|
|
17
|
+
sodax.bridge.createBridgeIntent<K, Raw>(action): Promise<Result<CreateBridgeIntentResult<K, Raw>, SodaxError>>;
|
|
18
|
+
sodax.bridge.approve<K, Raw>(args): Promise<Result<TxReturnType<K, Raw>, SodaxError>>;
|
|
19
|
+
sodax.bridge.isAllowanceValid<K, Raw>(args): Promise<Result<boolean, SodaxError>>;
|
|
20
|
+
|
|
21
|
+
sodax.bridge.getBridgeableAmount(from: XToken, to: XToken): Promise<Result<BridgeLimit, SodaxError>>;
|
|
22
|
+
sodax.bridge.getBridgeableTokens(from: SpokeChainKey, to: SpokeChainKey, token: string): Result<XToken[], SodaxError>;
|
|
23
|
+
// Plus the sync helper:
|
|
24
|
+
sodax.bridge.isBridgeable({ from: XToken, to: XToken }): boolean;
|
|
25
|
+
sodax.bridge.getFee(inputAmount: bigint): bigint;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Action params shape
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
type CreateBridgeParams<K extends SpokeChainKey> = {
|
|
32
|
+
srcChainKey: K;
|
|
33
|
+
srcAddress: GetAddressType<K>;
|
|
34
|
+
srcAsset: `0x${string}`; // source token address (spoke chain)
|
|
35
|
+
amount: bigint;
|
|
36
|
+
dstChainKey: SpokeChainKey;
|
|
37
|
+
dstAddress: string; // destination wallet (chain-specific format)
|
|
38
|
+
dstAsset: `0x${string}`; // destination token address (must share vault with srcAsset)
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Common call shapes
|
|
43
|
+
|
|
44
|
+
### Full bridge (recommended for most flows)
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const result = await sodax.bridge.bridge({
|
|
48
|
+
params: {
|
|
49
|
+
srcChainKey: ChainKeys.ARBITRUM_MAINNET,
|
|
50
|
+
srcAddress: '0x…',
|
|
51
|
+
srcAsset: USDC_ARBITRUM.address,
|
|
52
|
+
amount: parseUnits('100', 6),
|
|
53
|
+
dstChainKey: ChainKeys.STELLAR_MAINNET,
|
|
54
|
+
dstAddress: 'G…',
|
|
55
|
+
dstAsset: USDC_STELLAR.address,
|
|
56
|
+
},
|
|
57
|
+
raw: false,
|
|
58
|
+
walletProvider: evmWp,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!result.ok) return;
|
|
62
|
+
const { srcChainTxHash, dstChainTxHash } = result.value;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Create-intent (custom relay control)
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const result = await sodax.bridge.createBridgeIntent({
|
|
69
|
+
params: { /* same as above */ },
|
|
70
|
+
raw: false,
|
|
71
|
+
walletProvider: evmWp,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!result.ok) return;
|
|
75
|
+
const { tx, intent, relayData } = result.value;
|
|
76
|
+
// Submit relayData.payload via your own relay infrastructure if needed.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Bridgeable-amount check
|
|
80
|
+
|
|
81
|
+
Respects vault deposit limits (spoke→hub) and asset-manager balance (hub→spoke). Pass the source and destination tokens as full `XToken` objects (each carries its own `chainKey`):
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const result = await sodax.bridge.getBridgeableAmount(USDC_ARBITRUM, USDC_STELLAR);
|
|
85
|
+
if (result.ok) {
|
|
86
|
+
const { amount, decimals, type } = result.value; // BridgeLimit
|
|
87
|
+
console.log(`Up to ${amount} (${decimals} decimals) can be bridged`);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Find compatible tokens
|
|
92
|
+
|
|
93
|
+
Synchronous (config-derived). Pass source-chain key, destination-chain key, and the source token address; the SDK filters the destination's supported tokens by matching vault:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const result = sodax.bridge.getBridgeableTokens(
|
|
97
|
+
ChainKeys.ARBITRUM_MAINNET,
|
|
98
|
+
ChainKeys.STELLAR_MAINNET,
|
|
99
|
+
USDC_ARBITRUM.address,
|
|
100
|
+
);
|
|
101
|
+
if (result.ok) {
|
|
102
|
+
// result.value: XToken[] — Stellar-side tokens that share USDC_ARBITRUM's vault
|
|
103
|
+
for (const token of result.value) {
|
|
104
|
+
console.log(token.chainKey, token.symbol);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Return shapes
|
|
110
|
+
|
|
111
|
+
| Method | Success type |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `bridge` | `TxHashPair` |
|
|
114
|
+
| `createBridgeIntent` | `CreateBridgeIntentResult<K, Raw>` = `{ tx: TxReturnType<K, Raw>, intent, relayData }` |
|
|
115
|
+
| `approve` | `TxReturnType<K, Raw>` |
|
|
116
|
+
| `isAllowanceValid` | `boolean` |
|
|
117
|
+
| `getBridgeableAmount` | `BridgeLimit = { amount, decimals, type }` |
|
|
118
|
+
| `getBridgeableTokens` | `XToken[]` |
|
|
119
|
+
|
|
120
|
+
## Error codes
|
|
121
|
+
|
|
122
|
+
`feature: 'bridge'`. Per-method narrow unions:
|
|
123
|
+
|
|
124
|
+
| Method | Codes | `error.context` |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `bridge` | full exec set | `action: 'bridge'` |
|
|
127
|
+
| `createBridgeIntent` | `VALIDATION_FAILED`, `INTENT_CREATION_FAILED`, `UNKNOWN` | `action: 'bridge'` |
|
|
128
|
+
| `approve` | `VALIDATION_FAILED`, `APPROVE_FAILED`, `UNKNOWN` | n/a |
|
|
129
|
+
| `isAllowanceValid` | `VALIDATION_FAILED`, `ALLOWANCE_CHECK_FAILED`, `UNKNOWN` | n/a |
|
|
130
|
+
| `getBridgeableAmount`, `getBridgeableTokens` | `VALIDATION_FAILED`, `LOOKUP_FAILED`, `UNKNOWN` | `method: 'getBridgeableAmount' \| 'getBridgeableTokens'` |
|
|
131
|
+
|
|
132
|
+
## Cross-references
|
|
133
|
+
|
|
134
|
+
- v1 → v2 bridge migration: [`../../migration/features/bridge.md`](../../migration/features/bridge.md).
|
|
135
|
+
- Stellar destinations need a trustline first: [`../chain-specifics.md`](../chain-specifics.md).
|
|
136
|
+
- Hub-and-spoke vault architecture: [`../architecture.md`](../architecture.md) § 1.
|