@orbs-network/spot-ui 1.0.28 → 2.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.
- package/README.md +306 -0
- package/dist/spot-ui.js +2625 -2414
- package/dist/spot-ui.umd.cjs +11 -11
- package/dist/src/index.d.ts +8 -11
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/lib/analytics.d.ts +1 -1
- package/dist/src/lib/analytics.d.ts.map +1 -1
- package/dist/src/lib/build-repermit-order-data.d.ts +9 -8
- package/dist/src/lib/build-repermit-order-data.d.ts.map +1 -1
- package/dist/src/lib/calculations.d.ts +123 -0
- package/dist/src/lib/calculations.d.ts.map +1 -0
- package/dist/src/lib/client.d.ts +74 -0
- package/dist/src/lib/client.d.ts.map +1 -0
- package/dist/src/lib/consts.d.ts +4 -11
- package/dist/src/lib/consts.d.ts.map +1 -1
- package/dist/src/lib/lib.d.ts +11 -25
- package/dist/src/lib/lib.d.ts.map +1 -1
- package/dist/src/lib/networks.d.ts +27 -27
- package/dist/src/lib/networks.d.ts.map +1 -1
- package/dist/src/lib/order-form.d.ts +95 -0
- package/dist/src/lib/order-form.d.ts.map +1 -0
- package/dist/src/lib/orders/index.d.ts +5 -3
- package/dist/src/lib/orders/index.d.ts.map +1 -1
- package/dist/src/lib/orders/v1-orders.d.ts.map +1 -1
- package/dist/src/lib/orders/v2-orders.d.ts +3 -2
- package/dist/src/lib/orders/v2-orders.d.ts.map +1 -1
- package/dist/src/lib/submit-order.d.ts +1 -1
- package/dist/src/lib/submit-order.d.ts.map +1 -1
- package/dist/src/lib/types.d.ts +22 -5
- package/dist/src/lib/types.d.ts.map +1 -1
- package/dist/src/lib/utils.d.ts +7 -543
- package/dist/src/lib/utils.d.ts.map +1 -1
- package/package.json +7 -10
package/README.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Spot SDK
|
|
2
|
+
|
|
3
|
+
`@orbs-network/spot-ui` is the framework-agnostic Spot SDK. Use it from Vue,
|
|
4
|
+
Angular, Svelte, React, vanilla JavaScript, or a server-side TypeScript
|
|
5
|
+
application. It has no React dependency.
|
|
6
|
+
|
|
7
|
+
## Complete form calculation
|
|
8
|
+
|
|
9
|
+
Use `calculateOrderForm` as the primary calculation API. Pass the DEX-owned
|
|
10
|
+
form state and market data; the SDK returns one authoritative model for both
|
|
11
|
+
display and order execution.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {
|
|
15
|
+
calculateOrderForm,
|
|
16
|
+
Module,
|
|
17
|
+
toAmountWei,
|
|
18
|
+
} from "@orbs-network/spot-ui";
|
|
19
|
+
|
|
20
|
+
const form = calculateOrderForm({
|
|
21
|
+
module: Module.TWAP,
|
|
22
|
+
isMarketOrder: true,
|
|
23
|
+
|
|
24
|
+
// Raw input-token amount.
|
|
25
|
+
inputAmountWei: toAmountWei(typedInputAmount, inputToken.decimals),
|
|
26
|
+
inputTokenDecimals: inputToken.decimals,
|
|
27
|
+
outputTokenDecimals: outputToken.decimals,
|
|
28
|
+
|
|
29
|
+
// Raw output-token amount quoted for the complete input amount.
|
|
30
|
+
quotedOutputAmount,
|
|
31
|
+
inputUsdPrice,
|
|
32
|
+
outputUsdPrice,
|
|
33
|
+
minTradeSizeUsd,
|
|
34
|
+
|
|
35
|
+
// Optional user overrides. Omit them to use SDK defaults.
|
|
36
|
+
trades,
|
|
37
|
+
fillDelay,
|
|
38
|
+
duration,
|
|
39
|
+
limitPrice,
|
|
40
|
+
limitPricePercent,
|
|
41
|
+
triggerPrice,
|
|
42
|
+
triggerPricePercent,
|
|
43
|
+
isInverted,
|
|
44
|
+
|
|
45
|
+
priceProtection: 3, // 3%
|
|
46
|
+
displayFeePercent,
|
|
47
|
+
inputBalance, // raw input-token units
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The result includes:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
form.inputAmount; // raw, UI, and USD input amounts
|
|
55
|
+
form.outputAmount; // raw, UI, and USD output amounts
|
|
56
|
+
form.isInverted; // current price-display direction
|
|
57
|
+
form.trades; // trade count and structured per-trade input/output amounts
|
|
58
|
+
form.schedule; // resolved fill delay, duration, milliseconds, and errors
|
|
59
|
+
form.triggerPrice; // canonical raw value, display values, defaults, and validation
|
|
60
|
+
form.limitPrice; // canonical raw value, display values, defaults, and validation
|
|
61
|
+
form.minOutputAmountTotal; // raw, UI, and USD minimum total output
|
|
62
|
+
form.tradePrice; // raw, UI, and USD execution price
|
|
63
|
+
form.fees; // raw, UI, USD, and percentage fee values
|
|
64
|
+
form.values; // raw execution values used to build the order
|
|
65
|
+
form.errors; // structured errors, ordered list, and primary error
|
|
66
|
+
form.isReady;
|
|
67
|
+
form.canSubmit;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Display amounts use `{ raw, ui, usd }` objects and
|
|
71
|
+
trade-direction terminology. For `limitPrice` and `triggerPrice`, `amount`
|
|
72
|
+
is now named `raw` and remains the canonical protocol rate, while
|
|
73
|
+
`display.raw`, `display.ui`, and `display.usd` follow the current `isInverted`
|
|
74
|
+
display direction. `form.values` intentionally contains no `UI` or `Usd`
|
|
75
|
+
fields. Integration-facing calculation and client APIs use input/output names.
|
|
76
|
+
Existing source/destination names remain only on low-level protocol helpers and
|
|
77
|
+
protocol response models such as `Order`.
|
|
78
|
+
|
|
79
|
+
This calculation is synchronous and does not fetch configuration. Optional
|
|
80
|
+
fields can be passed while the user edits the form; the result always contains
|
|
81
|
+
the currently derivable values. `inputAmountWei` is the single input amount
|
|
82
|
+
input; the SDK derives its UI and USD representations from the token decimals
|
|
83
|
+
and USD price. Integrations that already have a per-input-token raw rate can
|
|
84
|
+
pass `marketPrice` instead of `quotedOutputAmount`.
|
|
85
|
+
|
|
86
|
+
`marketPrice` and `quotedOutputAmount` use different units:
|
|
87
|
+
|
|
88
|
+
- `marketPrice` is the raw output-token amount for exactly one whole input
|
|
89
|
+
token (for example, a 2 USDC rate with 6 output decimals is `"2000000"`);
|
|
90
|
+
- `quotedOutputAmount` is the raw output-token amount quoted for the complete
|
|
91
|
+
`inputAmountWei`.
|
|
92
|
+
|
|
93
|
+
Pass one or the other. When `quotedOutputAmount` is supplied, the SDK derives
|
|
94
|
+
the per-token rate using `inputAmountWei` and `inputTokenDecimals`.
|
|
95
|
+
|
|
96
|
+
`calculateOrderForm` is time-independent. Recalculate only when its form or
|
|
97
|
+
market inputs change. `prepareOrder` stamps the current start and deadline from
|
|
98
|
+
the calculated duration immediately before signing; those exact timestamps are
|
|
99
|
+
returned on `preparedOrder.values`.
|
|
100
|
+
|
|
101
|
+
`calculateOrderForm` is the only public order-calculation entry point. Its
|
|
102
|
+
smaller calculators are internal implementation details, so every integration
|
|
103
|
+
uses the same defaults, validation, and derived-value rules.
|
|
104
|
+
|
|
105
|
+
`minTradeSizeUsd` is a positive USD threshold owned by the integrating DEX.
|
|
106
|
+
Use the minimum approved for that partner/product; the SDK deliberately does
|
|
107
|
+
not guess protocol policy. `priceProtection` is a percentage, so `3` means 3%
|
|
108
|
+
(300 basis points), not 3 bps. `displayFeePercent` is also a percentage, but it
|
|
109
|
+
only calculates `form.fees` for display. It does not collect or subtract a fee;
|
|
110
|
+
collection must be configured by the partner/backend.
|
|
111
|
+
|
|
112
|
+
## Client and order submission
|
|
113
|
+
|
|
114
|
+
`createClient` loads RePermit configuration and returns a new initialized
|
|
115
|
+
client. It does not retain a module-level cache; the hosting application owns
|
|
116
|
+
client reuse, request deduplication, and refresh policy. Initialization rejects
|
|
117
|
+
chain mismatches and malformed or zero RePermit and exchange-adapter addresses
|
|
118
|
+
before exposing approval or cancellation values.
|
|
119
|
+
|
|
120
|
+
### Migrating from 1.x
|
|
121
|
+
|
|
122
|
+
Version 2 uses `calculateOrderForm` as the single calculation entry point and
|
|
123
|
+
an initialized `createClient` for order preparation, signing, submission,
|
|
124
|
+
cancellation requests, and configured history. Legacy low-level order-building
|
|
125
|
+
and submission exports were removed so integrations cannot bypass the shared
|
|
126
|
+
validated form and client configuration.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import {
|
|
130
|
+
calculateOrderForm,
|
|
131
|
+
createClient,
|
|
132
|
+
ensureWrappedToken,
|
|
133
|
+
getExplorerUrl,
|
|
134
|
+
isNativeAddress,
|
|
135
|
+
isTxRejected,
|
|
136
|
+
Module,
|
|
137
|
+
Partners,
|
|
138
|
+
toAmountWei,
|
|
139
|
+
} from "@orbs-network/spot-ui";
|
|
140
|
+
|
|
141
|
+
const client = await createClient(Partners.Quick, 137);
|
|
142
|
+
|
|
143
|
+
const form = calculateOrderForm({
|
|
144
|
+
module: Module.TWAP,
|
|
145
|
+
isMarketOrder: true,
|
|
146
|
+
inputAmountWei: toAmountWei(typedInputAmount, inputToken.decimals),
|
|
147
|
+
inputTokenDecimals: inputToken.decimals,
|
|
148
|
+
outputTokenDecimals: outputToken.decimals,
|
|
149
|
+
quotedOutputAmount,
|
|
150
|
+
inputUsdPrice,
|
|
151
|
+
outputUsdPrice,
|
|
152
|
+
minTradeSizeUsd,
|
|
153
|
+
trades,
|
|
154
|
+
fillDelay,
|
|
155
|
+
duration,
|
|
156
|
+
limitPrice,
|
|
157
|
+
limitPricePercent,
|
|
158
|
+
triggerPrice,
|
|
159
|
+
triggerPricePercent,
|
|
160
|
+
isInverted,
|
|
161
|
+
priceProtection: 3,
|
|
162
|
+
displayFeePercent,
|
|
163
|
+
inputBalance,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const amount = form.inputAmount.raw;
|
|
167
|
+
const approvalToken = ensureWrappedToken(inputToken, client.chainId);
|
|
168
|
+
const approvalRequest = {
|
|
169
|
+
tokenAddress: approvalToken.address,
|
|
170
|
+
amount,
|
|
171
|
+
spenderAddress: client.spenderAddress,
|
|
172
|
+
};
|
|
173
|
+
const { tokenAddress, spenderAddress } = approvalRequest;
|
|
174
|
+
const hasAllowance = async () =>
|
|
175
|
+
BigInt(await wallet.getAllowance({ tokenAddress, spenderAddress })) >=
|
|
176
|
+
BigInt(amount);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const approvalRequired = !(await hasAllowance());
|
|
180
|
+
|
|
181
|
+
// The signed order spends wrapped native tokens, so wrap before approval.
|
|
182
|
+
if (isNativeAddress(inputToken.address)) {
|
|
183
|
+
await wallet.wrapNativeToken(amount);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (approvalRequired) {
|
|
187
|
+
await wallet.approveToken(approvalRequest);
|
|
188
|
+
|
|
189
|
+
// Allow RPC allowance state to catch up after the confirmed approval.
|
|
190
|
+
for (let attempt = 0; attempt < 3 && !(await hasAllowance()); attempt++) {
|
|
191
|
+
await new Promise((resolve) => setTimeout(resolve, 3_000));
|
|
192
|
+
}
|
|
193
|
+
if (!(await hasAllowance())) throw new Error("Approval was not observed");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const preparedOrder = client.prepareOrder({
|
|
197
|
+
form,
|
|
198
|
+
inputTokenAddress: inputToken.address,
|
|
199
|
+
outputTokenAddress: outputToken.address,
|
|
200
|
+
swapperAddress: account,
|
|
201
|
+
});
|
|
202
|
+
const signature = await client.signOrder(
|
|
203
|
+
preparedOrder,
|
|
204
|
+
({ signerAddress, typedData }) =>
|
|
205
|
+
wallet.signTypedData(typedData, signerAddress),
|
|
206
|
+
);
|
|
207
|
+
const order = await client.submitOrder(preparedOrder, signature);
|
|
208
|
+
console.info("Order submitted", order, getExplorerUrl(order.txHash, 137));
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (isTxRejected(error)) console.info("The wallet request was rejected");
|
|
211
|
+
else throw error;
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`signOrder` only invokes the supplied wallet signer and returns its signature.
|
|
216
|
+
It never submits the order. `submitOrder` is the separate network operation.
|
|
217
|
+
The complete sequence is allowance check, native wrapping when required,
|
|
218
|
+
approval when required, allowance verification, signing, and submission.
|
|
219
|
+
|
|
220
|
+
`prepareOrder` does not recalculate form amounts, prices, trades, or schedules,
|
|
221
|
+
and it rejects a form whose `canSubmit` value is `false`.
|
|
222
|
+
It stamps the current start and deadline from the calculated duration, then
|
|
223
|
+
converts the supplied form into RePermit, signing, and approval data. The
|
|
224
|
+
returned `PreparedOrder` contains:
|
|
225
|
+
|
|
226
|
+
- `form`, the complete calculated form snapshot shown to the user;
|
|
227
|
+
- `values`, `form.values` plus the exact preparation-time timestamps used by the
|
|
228
|
+
signed order;
|
|
229
|
+
- `order`, the resulting protocol order;
|
|
230
|
+
- `signingRequest`, containing a framework-neutral `signerAddress` and
|
|
231
|
+
`typedData` EIP-712 payload;
|
|
232
|
+
- `approvalRequest`, ready for the wallet approval adapter.
|
|
233
|
+
|
|
234
|
+
Each `prepareOrder` call assigns a fresh monotonic nonce within that client
|
|
235
|
+
instance, so two orders prepared by the same instance cannot reuse a nonce. A
|
|
236
|
+
new client instance starts again from the current wall-clock value. The call
|
|
237
|
+
also assigns fresh `currentTimeMillis` and `deadlineMillis` values for that
|
|
238
|
+
submission attempt. Call it after wrapping and approval, immediately before
|
|
239
|
+
signing, so those time-dependent values remain fresh.
|
|
240
|
+
|
|
241
|
+
The signing request does not depend on Viem, Wagmi, Ethers, or another wallet
|
|
242
|
+
library. Viem adapters can spread `typedData` and map `signerAddress` to
|
|
243
|
+
`account`; Ethers adapters can pass `typedData.domain`, `typedData.types`, and
|
|
244
|
+
`typedData.message` to the signer.
|
|
245
|
+
|
|
246
|
+
`minTradeSizeUsd` is required calculation input owned by the integrating
|
|
247
|
+
application. The client does not read or infer it from partner configuration.
|
|
248
|
+
|
|
249
|
+
The complete form calculation remains a package-level function because it does
|
|
250
|
+
not depend on partner or chain configuration. The client exposes only
|
|
251
|
+
configured operations:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
calculateOrderForm(formParams);
|
|
255
|
+
client.getCancelOrderRequest(order);
|
|
256
|
+
client.getAccountOrders({ account });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Omitting `page` fetches every available history page. To fetch one page, pass
|
|
260
|
+
a zero-based `page` and an optional positive `limit`. Use `order.historyKey`
|
|
261
|
+
as the stable list/cache identity: legacy v1 numeric IDs can repeat across TWAP
|
|
262
|
+
contract deployments, while `order.id` remains the protocol order ID used for
|
|
263
|
+
display and cancellation.
|
|
264
|
+
|
|
265
|
+
The normalized history model keeps protocol response names (`src`/`dst` and
|
|
266
|
+
fill `in`/`out`) because it combines v1 and v2 payloads. Form and calculation
|
|
267
|
+
inputs use `input`/`output`. `OrderType` maps the selected module and execution
|
|
268
|
+
mode into `LIMIT`, `TWAP_LIMIT`, `TWAP_MARKET`, `STOP_LOSS_LIMIT`,
|
|
269
|
+
`STOP_LOSS_MARKET`, `TAKE_PROFIT_LIMIT`, or `TAKE_PROFIT_MARKET`.
|
|
270
|
+
`OrderFilter` provides `ALL`, `OPEN`, `COMPLETED`, `CANCELLED`, and `EXPIRED`
|
|
271
|
+
history filter values.
|
|
272
|
+
|
|
273
|
+
There is no authoritative single-order endpoint in the current service API.
|
|
274
|
+
To track an order, call `client.getAccountOrders({ account, page, limit })` on
|
|
275
|
+
the host's polling schedule and find it by `historyKey`. Supplying a page and
|
|
276
|
+
limit avoids fetching every history page when only a recent status window is
|
|
277
|
+
needed.
|
|
278
|
+
|
|
279
|
+
Use `client.spenderAddress` for allowance reads and approvals. Normalize a
|
|
280
|
+
native input with `ensureWrappedToken`, and approve `form.inputAmount.raw`.
|
|
281
|
+
The `approvalRequest` returned by `prepareOrder` records the same normalized
|
|
282
|
+
token, spender, and exact amount used by the signed order.
|
|
283
|
+
|
|
284
|
+
Native token addresses are normalized to the chain's wrapped token for order
|
|
285
|
+
input and approval requests. Protocol configuration fetching, RePermit order
|
|
286
|
+
construction, submission, cancellation request construction, and configured
|
|
287
|
+
history access are intentionally exposed only through `SpotClient`. This keeps
|
|
288
|
+
partner- and chain-derived values on one authoritative path.
|
|
289
|
+
|
|
290
|
+
Framework-neutral helpers `ensureWrappedToken`, `shouldWrapOnly`,
|
|
291
|
+
`shouldUnwrapOnly`, `isTxRejected`, and `getExplorerUrl` are also exported from
|
|
292
|
+
`spot-ui`; Vue, Angular, Svelte, and server integrations do not need to copy
|
|
293
|
+
React-specific utility code.
|
|
294
|
+
|
|
295
|
+
Every `createClient` call performs a new configuration request. Cache the
|
|
296
|
+
returned promise or client in the host application's normal data layer when it
|
|
297
|
+
should be reused. `spot-react` keeps one provider-scoped client resource keyed
|
|
298
|
+
by partner and chain; it does not require React Query. Vue, Angular, Svelte,
|
|
299
|
+
vanilla JavaScript, and server applications should apply their own lifecycle
|
|
300
|
+
and refresh policy.
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
// Illustrative host-owned cache; use the host framework's data layer where possible.
|
|
304
|
+
const clientPromise = createClient(Partners.Quick, 137);
|
|
305
|
+
const client = await clientPromise;
|
|
306
|
+
```
|