@domfi/sdk 0.1.0

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 (93) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +786 -0
  3. package/dist/actions.cjs +1 -0
  4. package/dist/actions.d.cts +17 -0
  5. package/dist/actions.d.ts +17 -0
  6. package/dist/actions.js +1 -0
  7. package/dist/api.cjs +1 -0
  8. package/dist/api.d.cts +664 -0
  9. package/dist/api.d.ts +664 -0
  10. package/dist/api.js +0 -0
  11. package/dist/chunk-2VSPOLVO.js +1 -0
  12. package/dist/chunk-5YZVCHRU.js +0 -0
  13. package/dist/chunk-77XMXAXE.js +1 -0
  14. package/dist/chunk-7OVWNPZQ.js +1 -0
  15. package/dist/chunk-BMYMNNJK.js +1 -0
  16. package/dist/chunk-C5Y2WM7C.js +1 -0
  17. package/dist/chunk-C64LJM7D.js +2 -0
  18. package/dist/chunk-CLOFJTBP.js +1 -0
  19. package/dist/chunk-CMREWGOI.js +1 -0
  20. package/dist/chunk-D5VE2O4F.js +1 -0
  21. package/dist/chunk-LDE3JHVE.js +1 -0
  22. package/dist/chunk-N4Q67DTE.js +1 -0
  23. package/dist/chunk-SZE5DE54.js +1 -0
  24. package/dist/chunk-XHQVWI2J.js +1 -0
  25. package/dist/chunk-YU5DN3PI.js +1 -0
  26. package/dist/config.cjs +2 -0
  27. package/dist/config.d.cts +132 -0
  28. package/dist/config.d.ts +132 -0
  29. package/dist/config.js +1 -0
  30. package/dist/contracts.cjs +1 -0
  31. package/dist/contracts.d.cts +13073 -0
  32. package/dist/contracts.d.ts +13073 -0
  33. package/dist/contracts.js +1 -0
  34. package/dist/delegation.cjs +1 -0
  35. package/dist/delegation.d.cts +7 -0
  36. package/dist/delegation.d.ts +7 -0
  37. package/dist/delegation.js +0 -0
  38. package/dist/errors.cjs +3 -0
  39. package/dist/errors.d.cts +155 -0
  40. package/dist/errors.d.ts +155 -0
  41. package/dist/errors.js +3 -0
  42. package/dist/index.cjs +2 -0
  43. package/dist/index.d.cts +216 -0
  44. package/dist/index.d.ts +216 -0
  45. package/dist/index.js +1 -0
  46. package/dist/math.cjs +1 -0
  47. package/dist/math.d.cts +273 -0
  48. package/dist/math.d.ts +273 -0
  49. package/dist/math.js +1 -0
  50. package/dist/position-BnhSFyGe.d.cts +56 -0
  51. package/dist/position-BnhSFyGe.d.ts +56 -0
  52. package/dist/rawSchemas-MAT7EZCV.js +1 -0
  53. package/dist/referrals.cjs +1 -0
  54. package/dist/referrals.d.cts +66 -0
  55. package/dist/referrals.d.ts +66 -0
  56. package/dist/referrals.js +1 -0
  57. package/dist/serde.cjs +1 -0
  58. package/dist/serde.d.cts +24 -0
  59. package/dist/serde.d.ts +24 -0
  60. package/dist/serde.js +1 -0
  61. package/dist/token.cjs +1 -0
  62. package/dist/token.d.cts +32 -0
  63. package/dist/token.d.ts +32 -0
  64. package/dist/token.js +0 -0
  65. package/dist/trading.cjs +1 -0
  66. package/dist/trading.d.cts +20 -0
  67. package/dist/trading.d.ts +20 -0
  68. package/dist/trading.js +1 -0
  69. package/dist/types-7s13ZSql.d.ts +533 -0
  70. package/dist/types-BK6dYOXr.d.cts +118 -0
  71. package/dist/types-CMtixBIP.d.cts +464 -0
  72. package/dist/types-DB1r_Ppi.d.ts +464 -0
  73. package/dist/types-DDgJiEFg.d.ts +780 -0
  74. package/dist/types-DJBX-p4X.d.cts +780 -0
  75. package/dist/types-DQft82W7.d.cts +533 -0
  76. package/dist/types-DhCzEwnS.d.ts +118 -0
  77. package/dist/types-veTDfbhq.d.ts +436 -0
  78. package/dist/types-x1MvxSdy.d.cts +436 -0
  79. package/dist/units-BYd75H7U.d.cts +22 -0
  80. package/dist/units-BYd75H7U.d.ts +22 -0
  81. package/dist/units.cjs +1 -0
  82. package/dist/units.d.cts +248 -0
  83. package/dist/units.d.ts +248 -0
  84. package/dist/units.js +1 -0
  85. package/dist/valuation.cjs +1 -0
  86. package/dist/valuation.d.cts +5 -0
  87. package/dist/valuation.d.ts +5 -0
  88. package/dist/valuation.js +1 -0
  89. package/dist/vault.cjs +1 -0
  90. package/dist/vault.d.cts +34 -0
  91. package/dist/vault.d.ts +34 -0
  92. package/dist/vault.js +1 -0
  93. package/package.json +181 -0
package/README.md ADDED
@@ -0,0 +1,786 @@
1
+ # @domfi/sdk
2
+
3
+ TypeScript SDK for the Domination Finance onchain perpetual futures exchange.
4
+
5
+ The SDK gives applications one typed client for Domination Finance REST reads, viem-backed RPC reads, wallet-signed trading transactions, token approvals, position valuation, and private vault flows. Wallets remain application-owned; the SDK never takes custody of private keys or relays transactions.
6
+
7
+ ## Capabilities
8
+
9
+ | What you need | Where it lives |
10
+ |---|---|
11
+ | Typed REST reads — markets, account, prices, trades | [`domfi.api`](#read-the-api) |
12
+ | Raw, schema-shaped REST + generic `request()` | [`domfi.raw`](#read-the-api) |
13
+ | High-level trade & vault actions with lifecycle handles | [`domfi.actions`](#high-level-actions) |
14
+ | Explicit trading — prepare → simulate → send → track | [`domfi.trading`](#trading) |
15
+ | ERC-20 collateral allowance checks & approvals | [`domfi.token`](#token-approval) |
16
+ | Position snapshots — PnL, mark value, liquidation, `watch()` | [`domfi.valuation`](#valuation) |
17
+ | Vault LP flows — deposit, withdraw, redeem, locked deposits | [`domfi.vault`](#vault) |
18
+ | Delegated trading — EIP-712 grants, `delegatedAction` | [`domfi.delegation`](#delegated-trading) |
19
+ | Referral code lookups & eligibility | [`domfi.referrals`](#referrals) |
20
+ | API/protocol status & SDK compatibility | `domfi.system` |
21
+ | Resolved chain config — addresses, collateral, chain id | `domfi.chain` |
22
+ | Pure bigint protocol math (no network) | [`@domfi/sdk/math`](#math-helpers) |
23
+
24
+ The SDK is one typed client over DomFi REST reads, viem RPC reads, and wallet-signed writes. Wallets stay application-owned — it never holds keys or relays. Both REST and RPC are optional; see [Client Modes](#client-modes).
25
+
26
+ ## Install
27
+
28
+ Use the DomFi workspace, a packed artifact, or a configured private package source. `viem` is a peer dependency supplied by the application.
29
+
30
+ ```bash
31
+ # pnpm
32
+ pnpm add @domfi/sdk viem
33
+
34
+ # yarn
35
+ yarn add @domfi/sdk viem
36
+
37
+ # bun
38
+ bun add @domfi/sdk viem
39
+
40
+ # npm
41
+ npm install @domfi/sdk viem
42
+ ```
43
+
44
+ ## Requirements
45
+
46
+ - **Node.js** >= 20 (or any modern runtime with a global `fetch`).
47
+ - **viem** >= 2.21 — a peer dependency your application supplies.
48
+ - **TypeScript** 5.6+ recommended.
49
+
50
+ `@domfi/sdk` ships **dual ESM + CJS** builds with type declarations for both, is side-effect-free (tree-shakeable), and pulls in no Node-only APIs — so it runs in Node and in modern browsers. Its only bundled dependency is `valibot`; `viem` is the peer dependency you supply.
51
+
52
+ ## Quick Start
53
+
54
+ Create an API-enabled client for read-only REST calls:
55
+
56
+ ```ts
57
+ import { createDomfiClient } from "@domfi/sdk";
58
+
59
+ const domfi = createDomfiClient({
60
+ chain: "testnet",
61
+ // Built-in `testnet`/`mainnet` chains ship a default API base URL
62
+ // (https://testnet-api.domination.finance / https://api.domination.finance),
63
+ // so `api.baseUrl` is optional. Pass `api: { baseUrl }` only to override it.
64
+ transport: {
65
+ maxConcurrentRest: 8,
66
+ retryBudget: 2,
67
+ },
68
+ });
69
+
70
+ const status = await domfi.system.apiStatus();
71
+ const markets = await domfi.api.markets.list();
72
+ const headPrices = await domfi.api.prices.head();
73
+
74
+ console.log(status.data.ok, markets.data.length, headPrices.data.length);
75
+ ```
76
+
77
+ Add viem clients when you need RPC reads or wallet-signed writes:
78
+
79
+ ```ts
80
+ import { createPublicClient, createWalletClient, http } from "viem";
81
+ import { privateKeyToAccount } from "viem/accounts";
82
+ import { baseSepolia } from "viem/chains";
83
+ import { createDomfiClient } from "@domfi/sdk";
84
+
85
+ const account = privateKeyToAccount(process.env.DOMFI_TEST_PRIVATE_KEY as `0x${string}`);
86
+
87
+ const publicClient = createPublicClient({
88
+ chain: baseSepolia,
89
+ transport: http(process.env.BASE_SEPOLIA_RPC_URL),
90
+ });
91
+
92
+ const walletClient = createWalletClient({
93
+ account,
94
+ chain: baseSepolia,
95
+ transport: http(process.env.BASE_SEPOLIA_RPC_URL),
96
+ });
97
+
98
+ const domfi = createDomfiClient({
99
+ chain: "testnet",
100
+ publicClient,
101
+ walletClient,
102
+ account: account.address,
103
+ // `testnet` supplies its API base URL by default; override only if needed:
104
+ // api: { baseUrl: process.env.DOMFI_API_BASE_URL },
105
+ transport: {
106
+ maxConcurrentRest: 8,
107
+ maxConcurrentRpc: 4,
108
+ retryBudget: 2,
109
+ },
110
+ });
111
+ ```
112
+
113
+ > **API base URL.** Built-in `testnet`/`mainnet` target the hosted DomFi API and need no `api.baseUrl`. The SDK appends each route's path (e.g. `/markets`) to your base URL — trailing slashes are trimmed, but it does **not** add a version segment — so to point at a custom or self-hosted deployment, pass the full API base **including** any version path it serves under, e.g. `api: { baseUrl: "https://your-host/api/v2" }`. (The referral read routes are served from the unversioned `/api` root, so a trailing `/v2` is stripped for those.)
114
+
115
+ > **Wallets.** `privateKeyToAccount` above is for server-side scripts and tests. In browser/frontend apps, build the wallet client from the user's wallet connector or provider (injected, WalletConnect, …) — never load private keys into frontend code.
116
+
117
+ ## Client Modes
118
+
119
+ API-enabled clients expose typed REST reads under `api` (and schema-shaped reads under `raw`), plus `actions`, `trading`, `token`, `valuation`, `vault`, `delegation`, `referrals`, `system`, and `chain`:
120
+
121
+ ```ts
122
+ domfi.api.markets.list;
123
+ domfi.api.account.positions;
124
+ domfi.api.prices.ticksIterator;
125
+ domfi.api.status.get;
126
+ domfi.raw.request;
127
+ domfi.trading.prepareOpenTrade;
128
+ domfi.token.approvalNeed;
129
+ domfi.valuation.positionSnapshot;
130
+ domfi.vault.stats;
131
+ domfi.actions.openMarket;
132
+ domfi.actions.closePosition;
133
+ domfi.actions.updateTakeProfit;
134
+ domfi.delegation.setDelegateWithSignature;
135
+ domfi.referrals.checkEligibility;
136
+ domfi.system.apiStatus;
137
+ domfi.chain.chainId;
138
+ ```
139
+
140
+ With `api: false`, the `api`, `raw`, `actions`, `vault`, and `referrals` namespaces are intentionally absent at the type level. `chain`, `token`, `delegation`, `trading` (receipt-only — no REST-backed order tracking), `valuation` (`unverifiedSlotSnapshot` only), and `system` remain.
141
+
142
+ ```ts
143
+ import { PairIndex, TradeIndex } from "@domfi/sdk";
144
+
145
+ const offlineDomfi = createDomfiClient({
146
+ chain: "testnet",
147
+ publicClient,
148
+ walletClient,
149
+ account: account.address,
150
+ api: false,
151
+ });
152
+
153
+ await offlineDomfi.valuation.unverifiedSlotSnapshot({
154
+ wallet: account.address,
155
+ pairIndex: PairIndex.from(7),
156
+ index: TradeIndex.from(0),
157
+ });
158
+ ```
159
+
160
+ ## Core Concepts
161
+
162
+ A handful of ideas show up throughout the SDK. Learn them once here.
163
+
164
+ ### Branded units
165
+
166
+ Money, prices, leverage, and percentages are **branded fixed-point values**, not raw numbers — so you can't accidentally pass a price where collateral is expected, and there is never a floating-point rounding surprise. Build them from decimal strings; convert to the raw `bigint` the contracts use when you need it:
167
+
168
+ ```ts
169
+ import { Collateral, Leverage, Price } from "@domfi/sdk";
170
+
171
+ const collateral = Collateral.fromDecimal("25"); // 25 USDC (6dp)
172
+ const leverage = Leverage.fromDecimal("4"); // 4x (2dp)
173
+ const price = Price.fromDecimal("58.5"); // $58.50 (18dp)
174
+
175
+ Collateral.toRaw(collateral); // 25000000n — on-chain scale
176
+ Collateral.toRawString(collateral); // "25000000"
177
+ ```
178
+
179
+ Most units share `fromDecimal` / `fromRaw` / `toRaw` / `toRawString` / `format`. A few add purpose-built constructors: `SlippagePercent.fromBps("50")` (0.50%), `ClosePercent.full()` (100%), `UsdP6.fromDollar(...)`. Number-like brands (`PairIndex`, `TradeIndex`) use `.from(n)`; id brands (`TradeId`, `OrderId`) use `.parse(s)`. Import codecs from the root or `@domfi/sdk/units`.
180
+
181
+ ### Result envelopes, freshness & readiness
182
+
183
+ REST reads return an envelope: `result.data` is the mapped value and `result.freshness` describes block lag/staleness. Each read accepts a `readinessPolicy` of `"strict" | "warn" | "ignore"` that decides what happens when the API read model is behind the chain — throw, emit a warning via [`onEvent`](#errors-and-diagnostics), or pass through. Use `"strict"` before writes; `"warn"` is a sensible default for UI reads. (Paginated iterators yield page items whose mapped value is on `.item`, not `.data`.)
184
+
185
+ ### Verified position refs
186
+
187
+ Position mutations (close, take-profit, collateral, …) require a `VerifiedPositionRef` — a provenance-tagged handle to one open position slot (wallet + pair + slot index + tradeId + chainId). Obtain one from:
188
+
189
+ - an account position — `verifiedPositionRefFromAccountPosition(position, domfi.chain.chainId)`;
190
+ - a tracked/recovered order — `domfi.trading.track()` / `recoverTransaction()`, and the `closePosition` / `removeCollateral` action outcomes expose `.positionRef`;
191
+ - explicit input — `verifiedPositionRef({ wallet, pairIndex, index, tradeId, chainId })`.
192
+
193
+ This stops the SDK from acting on an unverified slot index.
194
+
195
+ ### Approval & simulation policies
196
+
197
+ High-level `domfi.actions.*` writes take two policies (imported from `@domfi/sdk/actions`):
198
+
199
+ - **Approval** — `ActionApprovalPolicy.RequireExisting` (default; never sends an approval), `AutoExact` (approve exactly what's needed), or `UnsafeMaxUint256` (approve max — convenient, higher risk).
200
+ - **Simulation** — `ActionSimulationPolicy.Required` (default; simulate before send) or `Skip`.
201
+
202
+ Delegated actions must use `RequireExisting` — a delegate cannot approve the trader's collateral.
203
+
204
+ ### Handles
205
+
206
+ - **Action handles** (`domfi.actions.*`) are synchronous and non-thenable: calling the method *starts* the write, and `txHash()`, `approval()`, `waitReceipt()`, `outcome()`, and `watch()` observe that same started action.
207
+ - **Prepared transactions** (`domfi.trading.prepare*`, `domfi.vault.prepare*`) are explicit: `summary()` → `simulate()` → `send()`, where `send()` returns a tx handle for receipt and lifecycle tracking.
208
+
209
+ ## Read The API
210
+
211
+ Domain clients map raw API responses into branded SDK values. Use them for application code:
212
+
213
+ ```ts
214
+ import { PairIndex, TradeId } from "@domfi/sdk";
215
+
216
+ const pairIndex = PairIndex.from(7);
217
+ const wallet = account.address;
218
+
219
+ const market = await domfi.api.markets.get(pairIndex);
220
+ const state = await domfi.api.markets.state(pairIndex);
221
+ const positions = await domfi.api.account.positions(wallet, { limit: 25 });
222
+ const orders = await domfi.api.account.orders(wallet, { limit: 25 });
223
+
224
+ for await (const entry of domfi.api.account.tradesIterator(wallet, { limit: 100 })) {
225
+ // Iterators yield page items: the mapped value is on `.item` (not `.data`).
226
+ console.log(entry.item.tradeId, entry.item.notional);
227
+ }
228
+
229
+ const events = await domfi.api.trades.events(TradeId.parse("12345"), { wallet }, { limit: 50 });
230
+
231
+ console.log(market.data.symbol, state.data.longOpenInterest, positions.data.length, orders.data.length);
232
+ console.log(events.data.events.length);
233
+ ```
234
+
235
+ Use `domfi.raw.*` or `domfi.raw.request(...)` only when you need schema-shaped API payloads or route-level debugging.
236
+
237
+ ```ts
238
+ const rawStatus = await domfi.raw.status.get();
239
+ const rawMarket = await domfi.raw.request("markets.get", { pairIndex: 7 });
240
+
241
+ console.log(rawStatus.data, rawMarket.data);
242
+ ```
243
+
244
+ ## Token Approval
245
+
246
+ Trading and vault writes use ERC-20 collateral allowances. Check the required allowance first, then approve only when needed.
247
+
248
+ ```ts
249
+ import { Collateral } from "@domfi/sdk";
250
+
251
+ const amount = Collateral.fromDecimal("25");
252
+ const spender = domfi.chain.collateral.tradingStorageSpender;
253
+
254
+ const need = await domfi.token.approvalNeed(account.address, spender, amount);
255
+
256
+ if (need.action === "approve") {
257
+ const approval = await domfi.token.approve(spender, need.requiredAllowance, {
258
+ account: account.address,
259
+ });
260
+ await approval.waitReceipt({ confirmations: 1, pollingIntervalMs: 1_000 });
261
+ }
262
+ ```
263
+
264
+ ## Delegated Trading
265
+
266
+ Delegated trading uses DomFi Trading's `delegatedAction(trader, callData)` flow. The trader signs EIP-712 delegation typed data, the delegate signs delegated trading transactions, and `delegation.trader` is the affected wallet for positions, orders, collateral allowance, and receipts. `setDelegateWithSignature` can be submitted by the delegate or any relayer; this example uses the delegate as submitter.
267
+
268
+ ```ts
269
+ import {
270
+ Collateral,
271
+ Leverage,
272
+ PairIndex,
273
+ SlippagePercent,
274
+ type Address,
275
+ type DomfiClient,
276
+ } from "@domfi/sdk";
277
+ import { ActionApprovalPolicy } from "@domfi/sdk/actions";
278
+ import type { WalletClient } from "@domfi/sdk/trading";
279
+
280
+ declare const domfi: DomfiClient; // created with the delegate wallet client
281
+ declare const traderAccount: { address: Address };
282
+ declare const traderWalletClient: WalletClient; // wallet client for traderAccount
283
+ declare const delegate: Address; // delegate transaction signer and example submitter
284
+
285
+ const trader = traderAccount.address;
286
+ const expiry = BigInt(Math.floor(Date.now() / 1000) + 60 * 60);
287
+
288
+ const signed = await domfi.delegation.sign(
289
+ { delegator: trader, delegate, expiry },
290
+ traderWalletClient,
291
+ );
292
+
293
+ await (await domfi.delegation.setDelegateWithSignature(signed, { account: delegate })).waitReceipt();
294
+
295
+ const opened = domfi.actions.openMarket(
296
+ {
297
+ pairIndex: PairIndex.from(7),
298
+ direction: "long",
299
+ collateral: Collateral.fromDecimal("25"),
300
+ leverage: Leverage.fromDecimal("4"),
301
+ slippage: SlippagePercent.fromBps("50"),
302
+ },
303
+ {
304
+ account: delegate,
305
+ delegation: { trader },
306
+ approval: ActionApprovalPolicy.RequireExisting,
307
+ },
308
+ );
309
+
310
+ const hash = await opened.txHash();
311
+ console.log(hash);
312
+ ```
313
+
314
+ Delegated high-level actions require the trader to have pre-approved collateral allowance to the trading storage spender. Auto-approval policies are rejected because a delegate cannot approve ERC-20 collateral for the trader.
315
+
316
+ ## Trading
317
+
318
+ The low-level trading flow is explicit: prepare, optionally simulate, send, wait for the transaction receipt, then track the protocol lifecycle through the API.
319
+
320
+ ```ts
321
+ import { PairIndex, SlippagePercent } from "@domfi/sdk";
322
+ import { TradeDirection } from "@domfi/sdk/trading";
323
+
324
+ const prepared = await domfi.trading.prepareOpenTrade(
325
+ {
326
+ orderKind: "market",
327
+ pairIndex: PairIndex.from(7),
328
+ direction: TradeDirection.Long,
329
+ collateral: "10",
330
+ leverage: "4",
331
+ slippage: SlippagePercent.fromBps("50"),
332
+ },
333
+ { account: account.address },
334
+ );
335
+
336
+ const summary = await prepared.summary();
337
+ console.log(summary.approvals, prepared.estimatedOracleFeeWei);
338
+
339
+ await prepared.simulate({ account: account.address });
340
+
341
+ const maxOracleFeeWei = prepared.estimatedOracleFeeWei ?? 1_000_000_000_000_000n;
342
+ const handle = await prepared.send({
343
+ account: account.address,
344
+ maxOracleFeeWei,
345
+ });
346
+
347
+ const receipt = await handle.waitReceipt({ confirmations: 1 });
348
+ const current = await handle.track({ maxDurationMs: 60_000 });
349
+ const terminal = await handle.waitTerminal({ maxDurationMs: 180_000, intervalMs: 3_000 });
350
+
351
+ console.log(receipt.status, current.status, terminal.status);
352
+ ```
353
+
354
+ Use verified refs for position and trigger mutations. A verified position ref can come from account state, receipt recovery, or explicit caller input (see [Core Concepts](#core-concepts)).
355
+
356
+ ```ts
357
+ import { ClosePercent, SlippagePercent } from "@domfi/sdk";
358
+ import { verifiedPositionRefFromAccountPosition } from "@domfi/sdk/trading";
359
+
360
+ const page = await domfi.api.account.positions(account.address, { limit: 25 });
361
+ const openPosition = page.data.find((position) => position.kind === "open_position");
362
+
363
+ if (openPosition !== undefined) {
364
+ const positionRef = verifiedPositionRefFromAccountPosition(openPosition, domfi.chain.chainId);
365
+
366
+ const updateTakeProfit = await domfi.trading.prepareUpdateTakeProfit(positionRef, {
367
+ takeProfit: "1.25",
368
+ });
369
+ await (await updateTakeProfit.send({ account: account.address })).waitReceipt();
370
+
371
+ const close = await domfi.trading.prepareClosePosition(positionRef, {
372
+ closePercent: ClosePercent.full(),
373
+ slippage: SlippagePercent.fromBps("50"),
374
+ });
375
+ await (await close.send({ account: account.address })).waitTerminal({
376
+ maxDurationMs: 180_000,
377
+ });
378
+ }
379
+ ```
380
+
381
+ ### High-Level Actions
382
+
383
+ API-enabled clients expose `domfi.actions` for the ten trading write flows:
384
+
385
+ - `openMarket`, `openLimit`, `openStop`
386
+ - `closePosition`
387
+ - `updateTakeProfit`, `updateStopLoss`
388
+ - `topUpCollateral`, `removeCollateral`
389
+ - `updateTriggerOrder`, `cancelTriggerOrder`
390
+
391
+ Each method returns a synchronous, non-thenable `ActionHandle`. Calling an action method starts/stages the write pipeline immediately; `txHash()`, `approval()`, `waitReceipt()`, `outcome()`, and `watch()` observe that started action. Do not create an action handle for preview-only flows; use math helpers or low-level prepare/preflight APIs instead.
392
+
393
+ Open trigger placement actions resolve to `status: "placed"` with a receipt-verified trigger order ref. Receipt-only mutations resolve to `status: "confirmed"` with matched event evidence. Market-style actions resolve protocol terminal outcomes such as `executed`, `canceled`, `timed_out`, `identity_mismatch`, `timeout_available`, or `unknown`.
394
+
395
+ For most app writes, start with a high-level action and choose explicit approval and simulation policies:
396
+
397
+ ~~~ts
398
+ import {
399
+ Collateral,
400
+ Leverage,
401
+ PairIndex,
402
+ SlippagePercent,
403
+ } from "@domfi/sdk";
404
+ import { ActionApprovalPolicy, ActionSimulationPolicy } from "@domfi/sdk/actions";
405
+
406
+ const opened = domfi.actions.openMarket(
407
+ {
408
+ pairIndex: PairIndex.from(7),
409
+ direction: "long",
410
+ collateral: Collateral.fromDecimal("25"),
411
+ leverage: Leverage.fromDecimal("4"),
412
+ slippage: SlippagePercent.fromBps("50"),
413
+ },
414
+ {
415
+ account: account.address,
416
+ approval: ActionApprovalPolicy.AutoExact,
417
+ simulation: ActionSimulationPolicy.Required,
418
+ maxOracleFeeWei: 1_000_000_000_000_000n,
419
+ },
420
+ );
421
+
422
+ // The action pipeline has already started/staged; these observe the same action.
423
+ const hash = await opened.txHash();
424
+ const receipt = await opened.waitReceipt({ confirmations: 1 });
425
+ const outcome = await opened.outcome({ maxDurationMs: 180_000, intervalMs: 3_000 });
426
+
427
+ console.log(hash, receipt.status, outcome.status);
428
+ ~~~
429
+
430
+ Example outcome handling:
431
+
432
+ ~~~ts
433
+ import {
434
+ Collateral,
435
+ Leverage,
436
+ PairIndex,
437
+ Price,
438
+ SlippagePercent,
439
+ type DomfiClient,
440
+ } from "@domfi/sdk";
441
+ import type { VerifiedPositionRef } from "@domfi/sdk/trading";
442
+
443
+ declare const domfi: DomfiClient;
444
+ declare const positionRef: VerifiedPositionRef;
445
+
446
+ const placed = await domfi.actions.openLimit({
447
+ pairIndex: PairIndex.from(7),
448
+ direction: "long",
449
+ collateral: Collateral.fromDecimal("10"),
450
+ leverage: Leverage.fromDecimal("5"),
451
+ entryPrice: Price.fromDecimal("58"),
452
+ }).outcome();
453
+ if (placed.status === "placed") {
454
+ console.log("trigger order", placed.ref);
455
+ const canceled = await domfi.actions.cancelTriggerOrder(placed.ref).outcome();
456
+ console.log("matched cancel event", canceled.eventEvidence);
457
+ }
458
+
459
+ const closed = await domfi.actions.closePosition(positionRef, {
460
+ slippage: SlippagePercent.fromBps("50"),
461
+ }).outcome();
462
+ switch (closed.status) {
463
+ case "executed":
464
+ console.log("closed", closed.positionRef, closed.result);
465
+ break;
466
+ case "timeout_available":
467
+ console.log("timeout can be handled", closed.result.order);
468
+ break;
469
+ default:
470
+ console.log("terminal or unknown close outcome", closed.status);
471
+ }
472
+ ~~~
473
+
474
+ Use `ActionHandle.watch()` when you need a streaming lifecycle instead of only the final `outcome()`. Market-style actions can emit `submitted`, `mined`, `indexed`, `tracking`, `terminal`, or `unknown` phases; receipt-only actions emit the phases that apply to receipt confirmation.
475
+
476
+ ~~~ts
477
+ const closeAction = domfi.actions.closePosition(positionRef, {
478
+ slippage: SlippagePercent.fromBps("50"),
479
+ });
480
+
481
+ for await (const state of closeAction.watch({ maxDurationMs: 180_000, intervalMs: 3_000 })) {
482
+ if (state.phase === "indexed") console.log("indexed order", state.orderId);
483
+ if (state.phase === "tracking") console.log("tracking", state.result.status);
484
+ if (state.phase === "terminal") console.log("terminal", state.outcome.status);
485
+ }
486
+ ~~~
487
+
488
+ ## Valuation
489
+
490
+ Verified valuation combines API discovery with block-pinned onchain reads. Use it for UI position snapshots, actionability, PnL, mark value, and liquidation data.
491
+
492
+ ```ts
493
+ const snapshot = await domfi.valuation.positionSnapshot(positionRef, {
494
+ readinessPolicy: "warn",
495
+ });
496
+
497
+ if (snapshot.status === "live") {
498
+ console.log(snapshot.actionability, snapshot.markValue, snapshot.unrealizedPnl);
499
+ }
500
+
501
+ const bulk = await domfi.valuation.positionSnapshots([positionRef], {
502
+ chunkSize: 25,
503
+ });
504
+
505
+ const watcher = domfi.valuation.watchPositionSnapshot(
506
+ positionRef,
507
+ { intervalMs: 3_000, emitImmediately: true },
508
+ (event) => {
509
+ if (event.type === "snapshot") console.log(event.data.status);
510
+ if (event.type === "error") console.warn(event.error.message);
511
+ },
512
+ );
513
+
514
+ watcher.stop();
515
+ domfi.valuation.clearConfigCache();
516
+ console.log(bulk.length);
517
+ ```
518
+
519
+ `api:false` clients expose `unverifiedSlotSnapshot` (display-only slot inspection — it does not prove API identity) plus `clearConfigCache`. Verified snapshots, bulk reads, and watching require an API-enabled client.
520
+
521
+ ## Math Helpers
522
+
523
+ Use `@domfi/sdk/math` for pure, bigint-based protocol math in local previews and preflight UI. Math helpers do not perform REST or RPC calls.
524
+
525
+ ```ts
526
+ import { Collateral, FeePercent, Leverage, Price } from "@domfi/sdk";
527
+ import { currentPercentProfit, tradeFee } from "@domfi/sdk/math";
528
+
529
+ const percentProfitRaw = currentPercentProfit({
530
+ openPriceRaw: Price.toRaw(Price.fromDecimal("58")),
531
+ markPriceRaw: Price.toRaw(Price.fromDecimal("60")),
532
+ buy: true,
533
+ leverageRaw: Leverage.toRaw(Leverage.fromDecimal("4")),
534
+ initialLeverageRaw: Leverage.toRaw(Leverage.fromDecimal("4")),
535
+ });
536
+
537
+ const flatFee = tradeFee({
538
+ collateral: Collateral.fromDecimal("25"),
539
+ leverage: Leverage.fromDecimal("4"),
540
+ feeP: FeePercent.fromDecimal("0.08"),
541
+ });
542
+
543
+ console.log(percentProfitRaw, Collateral.toRawString(flatFee));
544
+ ```
545
+
546
+ Use `tradeFee` only when you already know the single flat fee rate to apply. For the protocol's
547
+ real opening cost use `openingFee` (the maker/taker model) below.
548
+
549
+ Target-leverage helpers compute the collateral adjustment to reach a leverage. Submit
550
+ `requestedAmount` on-chain (via `domfi.actions.topUpCollateral` / `removeCollateral`);
551
+ `effectiveAmount` is the collateral that actually lands after the contract's rounding, and
552
+ `newLeverage` previews the result (always at or below `targetLeverage`).
553
+
554
+ ```ts
555
+ import { Collateral, Leverage } from "@domfi/sdk";
556
+ import { topUpToReachLeverage, unrealizedPnlPercentFromMarkValue } from "@domfi/sdk/math";
557
+
558
+ const adjustment = topUpToReachLeverage({
559
+ collateral: Collateral.fromDecimal("100"),
560
+ leverage: Leverage.fromDecimal("4"),
561
+ targetLeverage: Leverage.fromDecimal("2"),
562
+ });
563
+ // Collateral.toRawString(adjustment.requestedAmount) -> amount to submit to topUpCollateral.
564
+
565
+ // Net PnL as a signed percent in P6 scale: 100% === 100_000_000n, floored at -100%.
566
+ const pnlPercentP6 = unrealizedPnlPercentFromMarkValue({
567
+ collateralRaw: Collateral.toRaw(Collateral.fromDecimal("100")),
568
+ markValueRaw: Collateral.toRaw(Collateral.fromDecimal("150")),
569
+ });
570
+
571
+ console.log(Leverage.toRawString(adjustment.newLeverage), pnlPercentP6);
572
+ ```
573
+
574
+ `openingFee` is the full maker/taker model (the imbalance-reducing portion of a trade is charged
575
+ the maker rate, the rest taker); it returns the total base fee the trader pays. `priceImpact`
576
+ mirrors the contract's execution-price function: `priceAfterImpact` is the spread-adjusted price
577
+ (for opens it becomes your stored `openPrice`; for closes it's the close execution price), and
578
+ `priceImpactP` is the raw guard-scale magnitude (`1% == 1e18`). Note that at the current contract
579
+ commit the OI-skew scaling is inert, so the spread is a flat 0.001% against the trader regardless
580
+ of open interest. Feed real OI from `marketState.data.rawState?.oiLong` / `oiShort` (both `UnsignedP18`,
581
+ and note `rawState` is optional), not the display `longOpenInterest` / `shortOpenInterest`.
582
+
583
+ ```ts
584
+ import { FeePercent, Leverage, Price, SignedP6, UnsignedP18, Collateral } from "@domfi/sdk";
585
+ import { openingFee, priceImpact } from "@domfi/sdk/math";
586
+
587
+ const fee = openingFee({
588
+ tradeSize: SignedP6.fromDecimal("500"), // +$500 notional (long); negative for short
589
+ oiDelta: SignedP6.fromDecimal("1000"), // oiLong - oiShort, in USD
590
+ leverage: Leverage.fromDecimal("10"),
591
+ makerFeeP: FeePercent.fromDecimal("0.1"),
592
+ takerFeeP: FeePercent.fromDecimal("0.2"),
593
+ makerMaxLeverage: Leverage.fromDecimal("50"),
594
+ });
595
+
596
+ const { priceAfterImpact } = priceImpact({
597
+ price: Price.fromDecimal("58"),
598
+ oiLong: UnsignedP18.fromDecimal("100"),
599
+ oiShort: UnsignedP18.fromDecimal("50"),
600
+ collateral: Collateral.fromDecimal("1000"),
601
+ leverage: Leverage.fromDecimal("10"),
602
+ isOpen: true,
603
+ isLong: true,
604
+ });
605
+
606
+ console.log(Collateral.toRawString(fee), Price.toRawString(priceAfterImpact));
607
+ ```
608
+
609
+ ## Vault
610
+
611
+ The SDK includes a first-class private vault client. Read flows can run with an API-only client; prepare and write flows need public and wallet clients for approval checks, simulation, receipt parsing, and indexing.
612
+
613
+ ```ts
614
+ import { VaultAsset, VaultShare } from "@domfi/sdk";
615
+
616
+ const stats = await domfi.vault.stats();
617
+ const vaultAccount = await domfi.vault.account(account.address);
618
+
619
+ const preparedDeposit = await domfi.vault.prepareDeposit(
620
+ {
621
+ assets: VaultAsset.fromDecimal("10"),
622
+ receiver: account.address,
623
+ minShares: VaultShare.fromDecimal("9.95"),
624
+ },
625
+ { account: account.address },
626
+ );
627
+
628
+ const vaultSummary = await preparedDeposit.summary();
629
+ await preparedDeposit.simulate({ account: account.address });
630
+
631
+ const vaultHandle = await preparedDeposit.send({ account: account.address });
632
+ const outcome = await vaultHandle.outcome();
633
+ const indexed = await vaultHandle.waitIndexed({ maxDurationMs: 60_000 });
634
+ const tracked = await vaultHandle.track({ maxDurationMs: 60_000 });
635
+
636
+ console.log(stats.data.currentEpoch, vaultAccount.data.totalShares, vaultSummary.approvals);
637
+ console.log(outcome.eventName, indexed.event.opType, tracked.receipt.status);
638
+ ```
639
+
640
+ ### High-Level Vault Actions
641
+
642
+ Direct send helpers mirror prepared methods for user-facing vault writes. They return `Promise<VaultTxHandle>` and are useful when you prefer an explicit async send call:
643
+
644
+ ```ts
645
+ const directDeposit = await domfi.vault.deposit(
646
+ { assets: VaultAsset.fromDecimal("10"), receiver: account.address },
647
+ { account: account.address },
648
+ );
649
+
650
+ await directDeposit.track({ maxDurationMs: 60_000 });
651
+ ```
652
+
653
+ The user-facing vault direct-send methods are `deposit`, `mint`, `makeWithdrawRequest`, `cancelWithdrawRequest`, `withdraw`, `redeem`, `depositWithDiscountAndLock`, `mintWithDiscountAndLock`, and `unlockDeposit`. Admin, governance, callback, `openPnl`, and `distributeReward` contract methods are not SDK user-facing methods.
654
+
655
+ For synchronous action handles with `approval()`, `txHash()`, `waitReceipt()`, `outcome()`, and `watch()`, use `domfi.actions.vaultDeposit`, `vaultMint`, `vaultMakeWithdrawRequest`, `vaultCancelWithdrawRequest`, `vaultWithdraw`, `vaultRedeem`, `vaultDepositWithDiscountAndLock`, `vaultMintWithDiscountAndLock`, and `vaultUnlockDeposit`.
656
+
657
+ ## Referrals
658
+
659
+ API-enabled clients expose referral lookup helpers. Import referral-specific types from the stable `@domfi/sdk/referrals` subpath when annotating wrappers or application state.
660
+
661
+ ```ts
662
+ import { ReferralEligibilityStatus, type ReferralEligibility } from "@domfi/sdk/referrals";
663
+
664
+ const eligibility: ReferralEligibility = await domfi.referrals.checkEligibility({
665
+ account: account.address,
666
+ code: "ALPHA123",
667
+ });
668
+
669
+ if (eligibility.status === ReferralEligibilityStatus.Usable) {
670
+ console.log("referral code id", eligibility.referral.codeId);
671
+ }
672
+ ```
673
+
674
+ ## Errors And Diagnostics
675
+
676
+ SDK errors have stable public fields: `code`, `kind`, `transient`, required `diagnostics`, optional `hint`, optional `hash`, and original `cause` when available.
677
+ Use `isDomfiError()` instead of `instanceof` so error handling remains stable across package subpaths and test/bundler module boundaries.
678
+
679
+ ```ts
680
+ import { isDomfiError } from "@domfi/sdk/errors";
681
+
682
+ try {
683
+ await domfi.system.apiStatus({ timeoutMs: 5_000 });
684
+ } catch (error) {
685
+ if (isDomfiError(error)) {
686
+ console.error(error.code, error.kind, error.transient, error.hint);
687
+ }
688
+ throw error;
689
+ }
690
+ ```
691
+
692
+ Pass `onEvent` when creating a client to observe REST diagnostics and readiness warnings:
693
+
694
+ ```ts
695
+ const observed = createDomfiClient({
696
+ chain: "testnet",
697
+ api: { baseUrl: "https://api.example.com/api/v2" },
698
+ onEvent(event) {
699
+ if (event.type === "warning") console.warn(event.code, event.message);
700
+ },
701
+ });
702
+
703
+ await observed.system.apiStatus({ readinessPolicy: "warn" });
704
+ ```
705
+
706
+ ### Common Errors
707
+
708
+ Every SDK error exposes a stable `code` and a `kind`; `transient: true` marks a retryable condition. Branch on `code` (stable) rather than the class. Narrow with `isDomfiError(err)` first.
709
+
710
+ | `code` | `kind` | Typical cause & fix |
711
+ |---|---|---|
712
+ | `MISSING_API_CLIENT` | config | Used an `api`/`actions`/`vault`/`referrals` namespace on an `api: false` client. Recreate with `api`. |
713
+ | `MISSING_PUBLIC_CLIENT` | config | A read/prepare needs RPC. Pass a viem `publicClient`. |
714
+ | `MISSING_WALLET_CLIENT` | wallet | A write needs signing. Pass a viem `walletClient`. |
715
+ | `MISSING_ACCOUNT` | wallet | No account for the write. Pass `account` (client option or per-call). |
716
+ | `WALLET_CHAIN_MISMATCH` | wallet | Wallet client is on a different chain than the configured `chain`. |
717
+ | `WALLET_ACCOUNT_MISMATCH` | wallet | The wallet's account differs from the `account` you passed. |
718
+ | `WALLET_DISCONNECTED` | wallet | _(transient)_ Wallet/transport dropped. Reconnect and retry. |
719
+ | `READ_MODEL_NOT_READY` | api | _(transient)_ API read model is behind the chain. Retry, or relax `readinessPolicy`. |
720
+ | `PROTOCOL_COMPATIBILITY_MISMATCH` | compatibility | On-chain contracts don't match the SDK's expected protocol. Align SDK/chain versions. |
721
+ | `IDENTITY_MISMATCH` | identity | A tracked tx resolved to a different trader/trade than expected (often a delegation mixup). |
722
+ | `VALIDATION_ERROR` | validation | Bad input, or an API response that failed schema validation. |
723
+ | `ORACLE_FEE_CAP_EXCEEDED` | validation | Required oracle fee exceeded your `maxOracleFeeWei`. Raise the cap or retry when fees drop. |
724
+ | `API_ERROR` / `API_*` | api | REST call returned an error; inspect `httpStatus` / `details`. |
725
+ | `CONTRACT_REVERT` / `REVERT_*` | contract | The contract reverted; `REVERT_*` is the decoded reason. |
726
+ | `TRANSPORT_ERROR` | transport | _(transient by default)_ Network/HTTP failure. |
727
+ | `TIMEOUT` | timeout | _(transient)_ Operation exceeded `timeoutMs` / `maxDurationMs`. |
728
+ | `ABORTED` | abort | _(transient)_ An `AbortSignal` you passed fired. |
729
+
730
+ ## Package Subpaths
731
+
732
+ The root export is the normal application entry point. Stable subpaths are available for focused imports:
733
+
734
+ ```ts
735
+ import { createDomfiClient } from "@domfi/sdk";
736
+ import { currentPercentProfit, tradeFee } from "@domfi/sdk/math";
737
+ import type { DomfiRawClient } from "@domfi/sdk/api";
738
+ import { domfiChains } from "@domfi/sdk/config";
739
+ import { abis } from "@domfi/sdk/contracts";
740
+ import { ApiError } from "@domfi/sdk/errors";
741
+ import { ReferralEligibilityStatus, type ReferralClient } from "@domfi/sdk/referrals";
742
+ import { coerceBigInt } from "@domfi/sdk/serde";
743
+ import type { TokenClient } from "@domfi/sdk/token";
744
+ import { verifiedPositionRef } from "@domfi/sdk/trading";
745
+ import { Collateral, PairIndex } from "@domfi/sdk/units";
746
+ import type { ValuationClient } from "@domfi/sdk/valuation";
747
+ import { VaultAsset } from "@domfi/sdk/vault";
748
+
749
+ declare const raw: DomfiRawClient;
750
+ declare const referrals: ReferralClient;
751
+ declare const token: TokenClient;
752
+ declare const valuation: ValuationClient;
753
+
754
+ console.log(createDomfiClient, raw, referrals, token, valuation, domfiChains.testnet.chainId);
755
+ console.log(
756
+ abis,
757
+ ApiError,
758
+ ReferralEligibilityStatus,
759
+ coerceBigInt,
760
+ currentPercentProfit,
761
+ tradeFee,
762
+ verifiedPositionRef,
763
+ Collateral,
764
+ PairIndex,
765
+ VaultAsset,
766
+ );
767
+ ```
768
+
769
+ ## Examples & Further Reading
770
+
771
+ Runnable, type-checked examples live in [`examples/`](./examples) (run via `pnpm test:examples`):
772
+
773
+ - [`examples/quickstart.ts`](./examples/quickstart.ts) — create a client and read API status.
774
+ - [`examples/trading-lifecycle.ts`](./examples/trading-lifecycle.ts) — high-level actions plus the low-level prepare/send flow.
775
+ - [`examples/valuation.ts`](./examples/valuation.ts) — position snapshots and watching.
776
+ - [`examples/vault.ts`](./examples/vault.ts) — vault reads and the deposit prepare flow.
777
+
778
+ For a feature-oriented tour of the SDK, see [`docs/sdk-showcase.md`](./docs/sdk-showcase.md).
779
+
780
+ Jump to a domain: [Core Concepts](#core-concepts) · [Read The API](#read-the-api) · [Token Approval](#token-approval) · [Delegated Trading](#delegated-trading) · [Trading](#trading) · [High-Level Actions](#high-level-actions) · [Valuation](#valuation) · [Math Helpers](#math-helpers) · [Vault](#vault) · [Referrals](#referrals) · [Errors](#errors-and-diagnostics).
781
+
782
+ ## License
783
+
784
+ Copyright (c) 2026 Domination Finance Ltd. All rights reserved.
785
+
786
+ This package is proprietary and confidential — it is **not** open source. No license is granted to use, copy, modify, or distribute it except under a separate written agreement. See [`LICENSE`](./LICENSE) for the full terms.