@orbs-network/spot-react 2.0.1 → 2.1.1

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 (141) hide show
  1. package/README.md +277 -148
  2. package/dist/spot-react/src/context/client-error-notice.d.ts +7 -0
  3. package/dist/spot-react/src/context/client-error-notice.d.ts.map +1 -0
  4. package/dist/spot-react/src/context/create-spot-store.d.ts +53 -0
  5. package/dist/spot-react/src/context/create-spot-store.d.ts.map +1 -0
  6. package/dist/spot-react/src/context/error-boundary.d.ts +7 -0
  7. package/dist/spot-react/src/context/error-boundary.d.ts.map +1 -0
  8. package/dist/spot-react/src/context/order-form-context.d.ts +7 -0
  9. package/dist/spot-react/src/context/order-form-context.d.ts.map +1 -0
  10. package/dist/spot-react/src/context/spot-form-synchronizer.d.ts +14 -0
  11. package/dist/spot-react/src/context/spot-form-synchronizer.d.ts.map +1 -0
  12. package/dist/spot-react/src/context/spot-provider.d.ts +3 -0
  13. package/dist/spot-react/src/context/spot-provider.d.ts.map +1 -0
  14. package/dist/spot-react/src/context/spot-runtime-context.d.ts +33 -0
  15. package/dist/spot-react/src/context/spot-runtime-context.d.ts.map +1 -0
  16. package/dist/spot-react/src/context/spot-store-context.d.ts +10 -0
  17. package/dist/spot-react/src/context/spot-store-context.d.ts.map +1 -0
  18. package/dist/spot-react/src/context/use-client.d.ts +10 -0
  19. package/dist/spot-react/src/context/use-client.d.ts.map +1 -0
  20. package/dist/spot-react/src/context/use-spot-analytics.d.ts +3 -0
  21. package/dist/spot-react/src/context/use-spot-analytics.d.ts.map +1 -0
  22. package/dist/spot-react/src/context/use-spot-provider-state.d.ts +11 -0
  23. package/dist/spot-react/src/context/use-spot-provider-state.d.ts.map +1 -0
  24. package/dist/spot-react/src/execute-order.d.ts +25 -0
  25. package/dist/spot-react/src/execute-order.d.ts.map +1 -0
  26. package/dist/spot-react/src/execution-state.d.ts +19 -0
  27. package/dist/spot-react/src/execution-state.d.ts.map +1 -0
  28. package/dist/spot-react/src/hooks/helper-hooks.d.ts +0 -547
  29. package/dist/spot-react/src/hooks/helper-hooks.d.ts.map +1 -1
  30. package/dist/spot-react/src/hooks/order-hooks.d.ts +28 -18
  31. package/dist/spot-react/src/hooks/order-hooks.d.ts.map +1 -1
  32. package/dist/spot-react/src/hooks/use-cancel-order.d.ts +5 -4
  33. package/dist/spot-react/src/hooks/use-cancel-order.d.ts.map +1 -1
  34. package/dist/spot-react/src/hooks/use-disclaimer.d.ts.map +1 -1
  35. package/dist/spot-react/src/hooks/use-duration.d.ts +3 -20
  36. package/dist/spot-react/src/hooks/use-duration.d.ts.map +1 -1
  37. package/dist/spot-react/src/hooks/use-execution.d.ts +30 -0
  38. package/dist/spot-react/src/hooks/use-execution.d.ts.map +1 -0
  39. package/dist/spot-react/src/hooks/use-fill-delay.d.ts +3 -22
  40. package/dist/spot-react/src/hooks/use-fill-delay.d.ts.map +1 -1
  41. package/dist/spot-react/src/hooks/use-history-order.d.ts +51 -28
  42. package/dist/spot-react/src/hooks/use-history-order.d.ts.map +1 -1
  43. package/dist/spot-react/src/hooks/use-input-errors.d.ts +1 -1
  44. package/dist/spot-react/src/hooks/use-input-errors.d.ts.map +1 -1
  45. package/dist/spot-react/src/hooks/use-limit-price.d.ts +14 -21
  46. package/dist/spot-react/src/hooks/use-limit-price.d.ts.map +1 -1
  47. package/dist/spot-react/src/hooks/use-output-amount.d.ts +5 -0
  48. package/dist/spot-react/src/hooks/use-output-amount.d.ts.map +1 -0
  49. package/dist/spot-react/src/hooks/use-price-display.d.ts +10 -0
  50. package/dist/spot-react/src/hooks/use-price-display.d.ts.map +1 -0
  51. package/dist/spot-react/src/hooks/use-submit-order.d.ts +1 -2
  52. package/dist/spot-react/src/hooks/use-submit-order.d.ts.map +1 -1
  53. package/dist/spot-react/src/hooks/use-swap-execution.d.ts +9 -10
  54. package/dist/spot-react/src/hooks/use-swap-execution.d.ts.map +1 -1
  55. package/dist/spot-react/src/hooks/use-trades.d.ts +7 -17
  56. package/dist/spot-react/src/hooks/use-trades.d.ts.map +1 -1
  57. package/dist/spot-react/src/hooks/use-trigger-price.d.ts +13 -20
  58. package/dist/spot-react/src/hooks/use-trigger-price.d.ts.map +1 -1
  59. package/dist/spot-react/src/index.d.ts +20 -11
  60. package/dist/spot-react/src/index.d.ts.map +1 -1
  61. package/dist/spot-react/src/order-history-data.d.ts +14 -0
  62. package/dist/spot-react/src/order-history-data.d.ts.map +1 -0
  63. package/dist/spot-react/src/types.d.ts +108 -123
  64. package/dist/spot-react/src/types.d.ts.map +1 -1
  65. package/dist/spot-react.js +1627 -6489
  66. package/dist/spot-react.umd.cjs +1 -64
  67. package/package.json +8 -13
  68. package/dist/spot-react/package.json.d.ts +0 -56
  69. package/dist/spot-react/src/hooks/use-amounts-usd.d.ts +0 -5
  70. package/dist/spot-react/src/hooks/use-amounts-usd.d.ts.map +0 -1
  71. package/dist/spot-react/src/hooks/use-build-order-info.d.ts +0 -62
  72. package/dist/spot-react/src/hooks/use-build-order-info.d.ts.map +0 -1
  73. package/dist/spot-react/src/hooks/use-deadline.d.ts +0 -2
  74. package/dist/spot-react/src/hooks/use-deadline.d.ts.map +0 -1
  75. package/dist/spot-react/src/hooks/use-default-values.d.ts +0 -3
  76. package/dist/spot-react/src/hooks/use-default-values.d.ts.map +0 -1
  77. package/dist/spot-react/src/hooks/use-dst-amount.d.ts +0 -11
  78. package/dist/spot-react/src/hooks/use-dst-amount.d.ts.map +0 -1
  79. package/dist/spot-react/src/hooks/use-dst-token-panel.d.ts +0 -7
  80. package/dist/spot-react/src/hooks/use-dst-token-panel.d.ts.map +0 -1
  81. package/dist/spot-react/src/hooks/use-fees.d.ts +0 -7
  82. package/dist/spot-react/src/hooks/use-fees.d.ts.map +0 -1
  83. package/dist/spot-react/src/hooks/use-form-data.d.ts +0 -57
  84. package/dist/spot-react/src/hooks/use-form-data.d.ts.map +0 -1
  85. package/dist/spot-react/src/hooks/use-input-with-percentage.d.ts +0 -16
  86. package/dist/spot-react/src/hooks/use-input-with-percentage.d.ts.map +0 -1
  87. package/dist/spot-react/src/hooks/use-limit-price-panel.d.ts +0 -22
  88. package/dist/spot-react/src/hooks/use-limit-price-panel.d.ts.map +0 -1
  89. package/dist/spot-react/src/hooks/use-partner-chains.d.ts +0 -2
  90. package/dist/spot-react/src/hooks/use-partner-chains.d.ts.map +0 -1
  91. package/dist/spot-react/src/hooks/use-price-panel.d.ts +0 -21
  92. package/dist/spot-react/src/hooks/use-price-panel.d.ts.map +0 -1
  93. package/dist/spot-react/src/hooks/use-repermit-data.d.ts +0 -7
  94. package/dist/spot-react/src/hooks/use-repermit-data.d.ts.map +0 -1
  95. package/dist/spot-react/src/hooks/use-repermit-order-data.d.ts +0 -20
  96. package/dist/spot-react/src/hooks/use-repermit-order-data.d.ts.map +0 -1
  97. package/dist/spot-react/src/hooks/use-spot.d.ts +0 -40
  98. package/dist/spot-react/src/hooks/use-spot.d.ts.map +0 -1
  99. package/dist/spot-react/src/hooks/use-src-amount.d.ts +0 -6
  100. package/dist/spot-react/src/hooks/use-src-amount.d.ts.map +0 -1
  101. package/dist/spot-react/src/hooks/use-submit-order-panel.d.ts +0 -28
  102. package/dist/spot-react/src/hooks/use-submit-order-panel.d.ts.map +0 -1
  103. package/dist/spot-react/src/hooks/use-trade-price.d.ts +0 -2
  104. package/dist/spot-react/src/hooks/use-trade-price.d.ts.map +0 -1
  105. package/dist/spot-react/src/hooks/use-trigger-price-panel.d.ts +0 -23
  106. package/dist/spot-react/src/hooks/use-trigger-price-panel.d.ts.map +0 -1
  107. package/dist/spot-react/src/spot-context.d.ts +0 -6
  108. package/dist/spot-react/src/spot-context.d.ts.map +0 -1
  109. package/dist/spot-react/src/store.d.ts +0 -11
  110. package/dist/spot-react/src/store.d.ts.map +0 -1
  111. package/dist/spot-react/src/utils.d.ts +0 -20
  112. package/dist/spot-react/src/utils.d.ts.map +0 -1
  113. package/dist/spot-ui/src/index.d.ts +0 -14
  114. package/dist/spot-ui/src/index.d.ts.map +0 -1
  115. package/dist/spot-ui/src/lib/abi/erc20.json.d.ts +0 -291
  116. package/dist/spot-ui/src/lib/abi/index.d.ts +0 -260
  117. package/dist/spot-ui/src/lib/abi/index.d.ts.map +0 -1
  118. package/dist/spot-ui/src/lib/abi/iweth.json.d.ts +0 -41
  119. package/dist/spot-ui/src/lib/abi/repermit.json.d.ts +0 -315
  120. package/dist/spot-ui/src/lib/analytics.d.ts +0 -111
  121. package/dist/spot-ui/src/lib/analytics.d.ts.map +0 -1
  122. package/dist/spot-ui/src/lib/build-repermit-order-data.d.ts +0 -35
  123. package/dist/spot-ui/src/lib/build-repermit-order-data.d.ts.map +0 -1
  124. package/dist/spot-ui/src/lib/consts.d.ts +0 -99
  125. package/dist/spot-ui/src/lib/consts.d.ts.map +0 -1
  126. package/dist/spot-ui/src/lib/lib.d.ts +0 -80
  127. package/dist/spot-ui/src/lib/lib.d.ts.map +0 -1
  128. package/dist/spot-ui/src/lib/networks.d.ts +0 -571
  129. package/dist/spot-ui/src/lib/networks.d.ts.map +0 -1
  130. package/dist/spot-ui/src/lib/orders/index.d.ts +0 -15
  131. package/dist/spot-ui/src/lib/orders/index.d.ts.map +0 -1
  132. package/dist/spot-ui/src/lib/orders/v1-orders.d.ts +0 -34
  133. package/dist/spot-ui/src/lib/orders/v1-orders.d.ts.map +0 -1
  134. package/dist/spot-ui/src/lib/orders/v2-orders.d.ts +0 -11
  135. package/dist/spot-ui/src/lib/orders/v2-orders.d.ts.map +0 -1
  136. package/dist/spot-ui/src/lib/submit-order.d.ts +0 -3
  137. package/dist/spot-ui/src/lib/submit-order.d.ts.map +0 -1
  138. package/dist/spot-ui/src/lib/types.d.ts +0 -318
  139. package/dist/spot-ui/src/lib/types.d.ts.map +0 -1
  140. package/dist/spot-ui/src/lib/utils.d.ts +0 -578
  141. package/dist/spot-ui/src/lib/utils.d.ts.map +0 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  React SDK for building TWAP, Limit, Stop-Loss, and Take-Profit order interfaces on top of the [Orbs Spot protocol](https://www.orbs.com/).
4
4
 
5
- `spot-react` owns Spot order state and exposes it through `SpotProvider` and `useSpot()`. The host DEX remains responsible for swap-form state, wallet access, components, styling, translations, routing, and modal shells.
5
+ `spot-react` owns Spot order state and exposes it through `SpotProvider` and focused named hooks. The host DEX remains responsible for swap-form state, wallet access, components, styling, translations, routing, and modal shells.
6
6
 
7
7
  For the complete integration workflow, see the [Spot React integration skill](https://github.com/orbs-network/spot-ui/tree/master/skills/spot-react-integration) and the [reference implementation](https://github.com/orbs-network/spot-ui/blob/master/apps/web/components/spot/spot-form.tsx).
8
8
 
@@ -12,9 +12,17 @@ Every DEX needs a member of the exported `Partners` enum and a server-side Orbs
12
12
 
13
13
  ### RePermit configuration security
14
14
 
15
- The `/config` endpoint is a security boundary. The SDK trusts successful JSON responses without client-side schema or contract-identity validation. It uses `domain.verifyingContract` as the ERC-20 approval spender and the v2 cancellation contract, and uses the returned adapter, reactor, and executor when constructing orders. Deployments must use the trusted Orbs endpoint over TLS, and contract-address changes require explicit approval from the protocol/security owner.
15
+ The `/config` endpoint is a security boundary. The SDK rejects configurations whose domain or order chain differs from the requested chain, and rejects zero or malformed RePermit and exchange-adapter addresses. It uses `domain.verifyingContract` as the ERC-20 approval spender and the v2 cancellation contract, and uses the returned adapter, reactor, and executor when constructing orders. The SDK does not independently verify deployed bytecode or contract identity, so deployments must use the trusted Orbs endpoint over TLS and contract-address changes require explicit approval from the protocol/security owner.
16
16
 
17
- `SpotProvider` starts a shared configuration query as soon as the partner and chain are known. Concurrent consumers reuse the same query, successful responses remain cached for that partner/chain/environment, and failed requests are retried twice. `useRePermitData()` exposes the query state and `refetch`; the standard submit panel exposes the same recovery action as `useSpot().submitOrderButton.retry`.
17
+ `SpotProvider` initializes the `spot-ui` client only when the connected chain supports the selected partner. It never substitutes another chain. Client state and order history are scoped to that provider, with no global cache or host query provider required. The framework-neutral `createClient` factory also retains no global state. The client owns configuration-dependent order preparation, signing/approval/cancellation request values, submission, and configured history access; calculations remain package-level functions. Form previews and order construction share the framework-agnostic `calculateOrderForm` model, so defaults, prices, trades, schedules, errors, raw/token-formatted/USD values, and signed execution values cannot diverge. Form calculation is time-independent; `prepareOrder` assigns fresh start, deadline, and nonce values after wrapping and approval, immediately before signing. Initialization is retried twice. After those retries, the provider keeps its children mounted and renders the retryable `clientErrorFallback` alongside them. Supply that component to localize and style the error for the host DEX.
18
+
19
+ The authoritative shared model is available from `useOrderForm()`.
20
+
21
+ Each mounted `SpotProvider` owns one stable store. Changing the module or token
22
+ pair reapplies form defaults without recreating the partner/chain client,
23
+ history cache, or cancellation state. If that scope changes during execution,
24
+ the reset waits until the frozen execution reaches a terminal phase. Changing
25
+ partner or chain reconfigures only the resources whose keys actually changed.
18
26
 
19
27
  The completed integration should:
20
28
 
@@ -36,56 +44,82 @@ npm install @orbs-network/spot-react@latest @orbs-network/swap-ui@latest
36
44
 
37
45
  If migrating from `@orbs-network/twap-ui`, remove it before installing the packages above.
38
46
 
39
- ### Peer Dependencies
47
+ ### Migrating from 1.x
40
48
 
41
- The host application must also provide:
49
+ Version 2 is a deliberately breaking, headless API. Replace the broad `useSpot`
50
+ and `useSwapExecution` interfaces with focused hooks such as `useOrderForm`,
51
+ `useExecution`, `useSubmitButton`, and `useOrders`. Provider values now use
52
+ `input`/`output` terminology (`inputToken`, `outputToken`, `inputBalanceRaw`,
53
+ and `minTradeSizeUsd`), and the host supplies wallet operations through
54
+ `walletInteractions`. Debug query parameters and the `isDev` option are no
55
+ longer supported.
42
56
 
43
- ```bash
44
- npm install @tanstack/react-query bignumber.js react-error-boundary zustand react react-dom
45
- ```
57
+ `@orbs-network/swap-ui` is a recommended companion for the documented order
58
+ progress modal, but it is not imported by `spot-react` and is not required for
59
+ a headless or custom-progress integration.
60
+
61
+ ### Peer Dependency
62
+
63
+ The host application must provide React. Zustand is an internal runtime
64
+ dependency of `spot-react`; integrators do not install or configure it.
46
65
 
47
66
  | Package | Version |
48
67
  | --- | --- |
49
- | `@tanstack/react-query` | `^5.90.12` |
50
- | `bignumber.js` | `^9.3.1` |
51
- | `react-error-boundary` | `^6.0.0` |
52
- | `zustand` | `^5.0.9` |
53
68
  | `react` | `^18 \|\| ^19` |
54
- | `react-dom` | `^18 \|\| ^19` |
55
69
 
56
70
  `viem` is not a dependency. Adapt the wallet library already used by the DEX through `walletInteractions`.
57
71
 
72
+ The published entry includes a `"use client"` directive, so it can be imported
73
+ directly from a Next.js App Router client component.
74
+
58
75
  ## Integration Model
59
76
 
60
77
  Keep the DEX swap form as the source of truth. Pass the following adapted values directly to `SpotProvider`:
61
78
 
62
79
  | Value | Expected shape |
63
80
  | --- | --- |
64
- | Selected tokens | `Token` objects with `address`, `symbol`, `decimals`, and `logoUrl` |
81
+ | Selected tokens | `Token` objects with `address`, `symbol`, `decimals`, and optional `logoUrl` |
82
+ | Wrapped native token | The connected chain's wrapped-native `Token`, supplied by the DEX |
65
83
  | Typed source amount | User-facing decimal string, for example `"1.25"` |
66
84
  | Quote output | Raw destination-token amount for the current typed input |
67
- | Balances | Raw integer strings |
85
+ | Input balance | Raw integer string |
68
86
  | USD prices | USD value of exactly one whole source/destination token |
69
87
  | Chain and account | Values from the connected wallet/account state |
70
88
 
71
89
  If several Spot components need the same DEX-owned values, expose a small DEX adapter context. Do not copy the swap state into a parallel Spot store, prop-drill long value lists, or create a hook whose only purpose is forwarding props to `SpotProvider`.
72
90
 
73
- Memoize objects with `useMemo` and functions with `useCallback`. In particular, keep stable identities for tokens, `marketReferencePrice`, `walletInteractions`, and `callbacks`.
91
+ Memoize objects with `useMemo` and functions with `useCallback`. In particular, keep stable identities for selected tokens, `wrappedNativeToken`, `marketQuote`, `walletInteractions`, and `callbacks`.
74
92
 
75
93
  ## Provider Setup
76
94
 
77
95
  ```tsx
78
96
  import { useMemo } from "react";
79
97
  import {
98
+ type ClientErrorFallbackProps,
80
99
  Module,
81
100
  Partners,
82
101
  SpotProvider,
83
102
  type Callbacks,
84
- type MarketReferencePrice,
103
+ type MarketQuote,
85
104
  type Token,
86
105
  type WalletInteractions,
87
106
  } from "@orbs-network/spot-react";
88
107
 
108
+ function ClientErrorFallback({
109
+ error,
110
+ retry,
111
+ isRetrying,
112
+ }: ClientErrorFallbackProps) {
113
+ return (
114
+ <div role="alert">
115
+ <p>{getLocalizedErrorMessage(error)}</p>
116
+ <button type="button" disabled={isRetrying} onClick={retry}>
117
+ {isRetrying ? t("retrying") : t("retry")}
118
+ </button>
119
+ </div>
120
+ );
121
+ }
122
+
89
123
  function SpotOrderForm({ module }: { module: Module }) {
90
124
  // Read these from the DEX's existing swap and wallet state.
91
125
  const {
@@ -93,19 +127,19 @@ function SpotOrderForm({ module }: { module: Module }) {
93
127
  chainId,
94
128
  inputCurrency,
95
129
  outputCurrency,
96
- inputBalance,
97
- outputBalance,
98
- typedInputAmount,
99
- quotedInputAmount,
100
- quoteOutputRaw,
130
+ wrappedNativeCurrency,
131
+ inputBalanceRaw,
132
+ inputAmountUi,
133
+ quotedInputAmountUi,
134
+ quotedOutputAmountRaw,
101
135
  isQuoteLoading,
102
- inputUsdPrice,
103
- outputUsdPrice,
136
+ inputTokenUsdPrice,
137
+ outputTokenUsdPrice,
104
138
  refetchBalances,
105
139
  dexWallet,
106
140
  } = useDexSpotAdapter();
107
141
 
108
- const srcToken = useMemo<Token | undefined>(() => {
142
+ const inputToken = useMemo<Token | undefined>(() => {
109
143
  if (!inputCurrency) return undefined;
110
144
  return {
111
145
  address: inputCurrency.address,
@@ -115,7 +149,7 @@ function SpotOrderForm({ module }: { module: Module }) {
115
149
  };
116
150
  }, [inputCurrency]);
117
151
 
118
- const dstToken = useMemo<Token | undefined>(() => {
152
+ const outputToken = useMemo<Token | undefined>(() => {
119
153
  if (!outputCurrency) return undefined;
120
154
  return {
121
155
  address: outputCurrency.address,
@@ -125,26 +159,38 @@ function SpotOrderForm({ module }: { module: Module }) {
125
159
  };
126
160
  }, [outputCurrency]);
127
161
 
128
- const marketReferencePrice = useMemo<MarketReferencePrice>(() => {
162
+ const wrappedNativeToken = useMemo<Token | undefined>(() => {
163
+ if (!wrappedNativeCurrency) return undefined;
164
+ return {
165
+ address: wrappedNativeCurrency.address,
166
+ symbol: wrappedNativeCurrency.symbol,
167
+ decimals: wrappedNativeCurrency.decimals,
168
+ logoUrl: wrappedNativeCurrency.logoUrl,
169
+ };
170
+ }, [wrappedNativeCurrency]);
171
+
172
+ const marketQuote = useMemo<MarketQuote>(() => {
129
173
  const shouldQuote = Boolean(
130
- typedInputAmount && inputCurrency && outputCurrency,
174
+ inputAmountUi && inputCurrency && outputCurrency,
131
175
  );
132
- const isStale = shouldQuote && typedInputAmount !== quotedInputAmount;
133
- const value = !shouldQuote || isStale ? undefined : quoteOutputRaw;
176
+ const isStale = shouldQuote && inputAmountUi !== quotedInputAmountUi;
177
+ const currentQuotedOutputAmountRaw =
178
+ !shouldQuote || isStale ? undefined : quotedOutputAmountRaw;
134
179
  const isLoading = shouldQuote && (isStale || isQuoteLoading);
135
180
 
136
181
  return {
137
- value,
182
+ quotedOutputAmountRaw: currentQuotedOutputAmountRaw,
138
183
  isLoading,
139
- noLiquidity: shouldQuote && !isLoading && !value,
184
+ noLiquidity:
185
+ shouldQuote && !isLoading && !currentQuotedOutputAmountRaw,
140
186
  };
141
187
  }, [
142
188
  inputCurrency,
143
189
  isQuoteLoading,
144
190
  outputCurrency,
145
- quoteOutputRaw,
146
- quotedInputAmount,
147
- typedInputAmount,
191
+ quotedOutputAmountRaw,
192
+ quotedInputAmountUi,
193
+ inputAmountUi,
148
194
  ]);
149
195
 
150
196
  const walletInteractions = useMemo<WalletInteractions>(
@@ -167,22 +213,23 @@ function SpotOrderForm({ module }: { module: Module }) {
167
213
  <SpotProvider
168
214
  partner={Partners.Quick}
169
215
  module={module}
170
- typedInputAmount={typedInputAmount}
171
- priceProtection={3}
172
- minChunkSizeUsd={5}
173
- marketReferencePrice={marketReferencePrice}
216
+ inputAmountUi={inputAmountUi}
217
+ priceProtectionPercent={3}
218
+ minTradeSizeUsd={5}
219
+ marketQuote={marketQuote}
174
220
  walletInteractions={walletInteractions}
175
221
  chainId={chainId}
176
222
  account={account}
177
- srcToken={srcToken}
178
- dstToken={dstToken}
179
- srcBalance={inputBalance?.toString()}
180
- dstBalance={outputBalance?.toString()}
181
- srcUsd1Token={inputUsdPrice}
182
- dstUsd1Token={outputUsdPrice}
223
+ inputToken={inputToken}
224
+ outputToken={outputToken}
225
+ wrappedNativeToken={wrappedNativeToken}
226
+ inputBalanceRaw={inputBalanceRaw}
227
+ inputTokenUsdPrice={inputTokenUsdPrice}
228
+ outputTokenUsdPrice={outputTokenUsdPrice}
183
229
  callbacks={callbacks}
230
+ clientErrorFallback={ClientErrorFallback}
184
231
  appId="my-dex"
185
- fees={0.25}
232
+ displayFeePercent={0.25}
186
233
  >
187
234
  <SpotFormContent />
188
235
  </SpotProvider>
@@ -190,9 +237,16 @@ function SpotOrderForm({ module }: { module: Module }) {
190
237
  }
191
238
  ```
192
239
 
193
- `marketReferencePrice.value` is the DEX quote's raw destination amount for the current `typedInputAmount`, not a standalone per-token price. If the quote belongs to an older input or token pair, omit `value` and report `isLoading: true` until a current quote arrives.
240
+ `marketQuote.quotedOutputAmountRaw` is the DEX quote's raw output amount for the current `inputAmountUi`, not a standalone per-token price. If the quote belongs to an older input or token pair, omit `quotedOutputAmountRaw` and report `isLoading: true` until a current quote arrives.
241
+
242
+ `spot-react` has no network registry. Always pass the `wrappedNativeToken` prop
243
+ from the DEX's own chain configuration; its value may be `undefined` only until
244
+ a chain is known. It is used for native/wrapped pair detection, allowance checks,
245
+ approval, and order preparation. Explorer links are also host-owned; callbacks
246
+ and history values expose transaction hashes so the DEX can format links with
247
+ its existing chain metadata.
194
248
 
195
- Use the connected wallet chain as the UI source of truth. The provider falls back internally to a supported partner chain for configuration lookups when `chainId` is absent or unsupported, so the DEX submit area must still show connect-wallet or switch-network controls and block submission.
249
+ Use the connected wallet chain as the source of truth. When it is absent or unsupported, the provider does not initialize a client and submission stays disabled. The DEX submit area should show its connect-wallet or switch-network control.
196
250
 
197
251
  ### SpotProvider Props
198
252
 
@@ -200,27 +254,36 @@ Use the connected wallet chain as the UI source of truth. The provider falls bac
200
254
  | --- | --- | --- | --- |
201
255
  | `partner` | `Partners` | Yes | DEX partner enum |
202
256
  | `module` | `Module` | Yes | `TWAP`, `LIMIT`, `STOP_LOSS`, or `TAKE_PROFIT` |
203
- | `typedInputAmount` | `string` | Yes | User-facing source amount from DEX state |
204
- | `priceProtection` | `number` | Yes | Price Protection percentage; this is not swap slippage |
205
- | `minChunkSizeUsd` | `number` | Yes | Minimum trade chunk size in USD |
206
- | `marketReferencePrice` | `MarketReferencePrice` | Yes | `{ value?, isLoading?, noLiquidity? }` for the current DEX quote |
257
+ | `inputAmountUi` | `string` | Yes | User-entered input-token amount |
258
+ | `priceProtectionPercent` | `number` | Yes | Price Protection percentage; this is not swap slippage |
259
+ | `minTradeSizeUsd` | `number` | Yes | Minimum individual trade size in USD |
260
+ | `marketQuote` | `MarketQuote` | Yes | `{ quotedOutputAmountRaw?, isLoading?, noLiquidity? }` for the current DEX quote |
207
261
  | `walletInteractions` | `WalletInteractions` | Yes | Five wallet methods implemented by the DEX |
208
262
  | `chainId` | `number` | No | Connected wallet chain ID |
209
- | `account` | `string` | No | Connected wallet address |
210
- | `appId` | `string` | No | Analytics app ID |
211
- | `srcToken` | `Token` | No | Source token metadata |
212
- | `dstToken` | `Token` | No | Destination token metadata |
213
- | `srcBalance` | `string` | No | Raw source-token balance |
214
- | `dstBalance` | `string` | No | Raw destination-token balance |
215
- | `srcUsd1Token` | `string` | No | USD value of one whole source token |
216
- | `dstUsd1Token` | `string` | No | USD value of one whole destination token |
217
- | `enableQueryParams` | `boolean` | No | Sync supported form state to URL query parameters |
263
+ | `account` | `Address` | No | Connected wallet address |
264
+ | `appId` | `string` | No | Stable host-defined analytics identifier (for example the DEX slug) |
265
+ | `inputToken` | `Token` | No | Input token metadata |
266
+ | `outputToken` | `Token` | No | Output token metadata |
267
+ | `wrappedNativeToken` | `Token \| undefined` | Yes | Host-provided wrapped-native token; pass `undefined` only before a chain is known |
268
+ | `inputBalanceRaw` | `string \| undefined` | Yes | Raw input-token balance; pass `undefined` while disconnected or loading |
269
+ | `inputTokenUsdPrice` | `string \| undefined` | Yes | USD value of one whole input token; pass `undefined` while loading |
270
+ | `outputTokenUsdPrice` | `string` | No | USD value of one whole output token |
218
271
  | `callbacks` | `Callbacks` | No | Lifecycle and field-change callbacks |
219
- | `fees` | `number` | No | Fee percentage, for example `0.25` |
220
- | `isDev` | `boolean` | No | Use development services/configuration |
272
+ | `displayFeePercent` | `number` | No | Display-only fee estimate percentage; does not collect or subtract fees |
221
273
  | `supportLegacyOrders` | `boolean` | No | Include supported legacy v1 orders in history |
274
+ | `clientErrorFallback` | `ComponentType<ClientErrorFallbackProps>` | No | Host-rendered client initialization error UI with `error`, `retry`, and `isRetrying` |
275
+ | `errorFallback` | `ComponentType<SpotErrorFallbackProps>` | No | Host-rendered fallback for unexpected calculation or rendering errors |
222
276
 
223
- Although balances and USD prices are optional in the TypeScript type, production integrations should pass them so validation, loading states, minimum trade size, and review details are correct.
277
+ `inputBalanceRaw` and `inputTokenUsdPrice` are required provider props; pass
278
+ `undefined` only while their values are genuinely unavailable. The output-token
279
+ USD price remains optional because it is used for display details rather than
280
+ submission readiness.
281
+
282
+ `minTradeSizeUsd` must be a positive USD threshold approved for the partner;
283
+ there is intentionally no SDK default. `priceProtectionPercent` uses percentage
284
+ units, so `3` means 3% (300 bps). `displayFeePercent` only populates `form.fees`
285
+ for the review UI. Protocol fee collection is configured separately by the
286
+ partner and backend.
224
287
 
225
288
  ## WalletInteractions
226
289
 
@@ -255,8 +318,8 @@ const walletInteractions: WalletInteractions = {
255
318
  return txHash;
256
319
  },
257
320
 
258
- signOrder: ({ domain, types, primaryType, message, account }) =>
259
- dexWallet.signTypedData({ domain, types, primaryType, message, account }),
321
+ signOrder: ({ signerAddress, typedData }) =>
322
+ dexWallet.signTypedData({ ...typedData, account: signerAddress }),
260
323
 
261
324
  getAllowance: ({ tokenAddress, spenderAddress }) =>
262
325
  dexWallet.getAllowance({ tokenAddress, spenderAddress }),
@@ -268,39 +331,64 @@ const walletInteractions: WalletInteractions = {
268
331
  | `wrapNativeToken(amountWei)` | Wrap native currency, wait for confirmation, return tx hash |
269
332
  | `approveToken({ tokenAddress, amount, spenderAddress })` | Approve the requested spender, wait for confirmation, return tx hash |
270
333
  | `cancelOrder({ order, contractAddress, args, abi })` | Call `cancel` with the supplied ABI and args, wait for confirmation, return tx hash |
271
- | `signOrder({ domain, types, primaryType, message, account })` | Sign the supplied EIP-712 data and return signature hex |
334
+ | `signOrder({ signerAddress, typedData })` | Adapt the framework-neutral EIP-712 request to the host wallet and return its original `0x`-prefixed signature |
272
335
  | `getAllowance({ tokenAddress, spenderAddress })` | Return the connected account's raw allowance as a string |
273
336
 
274
- ## Building the Form with useSpot()
337
+ The signature returned by `signOrder` is submitted unchanged. Do not split it into `{ v, r, s }`, rewrite its recovery byte, or otherwise normalize the wallet's byte representation.
338
+
339
+ ## Building with focused hooks
275
340
 
276
- Every component rendered under `SpotProvider` can call `useSpot()`:
341
+ Import only the hooks each component needs. Leaf components should call the
342
+ smallest hook that provides what they render:
277
343
 
278
344
  ```tsx
279
- const spot = useSpot();
345
+ import {
346
+ useExecution,
347
+ useLimitPrice,
348
+ useOrderForm,
349
+ useOrders,
350
+ useOutputAmount,
351
+ usePriceDisplay,
352
+ useTrades,
353
+ } from "@orbs-network/spot-react";
354
+
355
+ const calculatedForm = useOrderForm();
356
+ const tradesPanel = useTrades();
357
+ const limitPricePanel = useLimitPrice();
358
+ const outputAmount = useOutputAmount();
359
+
360
+ const execution = useExecution();
361
+ const history = useOrders();
280
362
  ```
281
363
 
364
+ `useOrderForm()` reads the calculated form shared by `OrderFormProvider`; it does not rerun the calculation. The other hooks expose smaller panel contracts for clearer component code. They currently share the same calculated-form context, so a form update can rerender every mounted form-panel hook.
365
+
282
366
  | Value | Key returns |
283
367
  | --- | --- |
284
- | `dstTokenPanel` | `value`, `valueWei`, `isLoading`, `usd` |
285
- | `tradesAmountPanel` | `totalTrades`, `maxTrades`, `onChange`, `error`, per-trade amounts, source/destination tokens |
286
- | `durationPanel` | `duration`, input/unit callbacks, `milliseconds`, `error` |
287
- | `fillDelayPanel` | `fillDelay`, input/unit callbacks, `milliseconds`, `error` |
288
- | `limitPricePanel` | Raw/UI price, percentage, toggle, input/reset callbacks, tokens, USD, loading/error state |
289
- | `triggerPricePanel` | Raw/UI trigger price, percentage, input/reset callbacks, tokens, per-chunk amounts, USD, error state |
290
- | `pricePanel` | Inversion state/callback, source/destination tokens, market-price state |
291
- | `disclaimerPanel` | Disclaimer translation key or `undefined` |
292
- | `inputError` | `{ type, args }` or `undefined` |
293
- | `submitOrderButton` | `disabled`, `loading`, configuration `error`, and `retry` |
294
- | `orderExecutionPanel` | Submission, status, steps, errors, resets, resolved tokens, and tx hashes |
295
- | `orderHistoryPanel` | Filtered order lists, loading state, and `refetchOrders` |
296
- | `derivedFormData` | Review amounts, prices, timing, fees, order type, spender, and RePermit data |
297
- | `supportedChains` | Supported chain IDs for the selected partner |
298
- | `module` | Current `Module` |
299
- | `refetchUntilStatusSynced` | Mutation used to reconcile cancellation status |
300
-
301
- Child components should call `useSpot()` themselves instead of receiving hook-returned panels through intermediate props.
302
-
303
- When `submitOrderButton.error` is set, render a translated retry label and call `submitOrderButton.retry()` instead of opening the review modal. Keep the button disabled while `loading` is true. Legacy v1 order history does not depend on RePermit configuration; v2 history, approvals, v2 cancellation, and submission resume after a successful retry.
368
+ | `useOutputAmount()` | `amount: { raw, ui, usd }`, `isLoading` |
369
+ | `useTrades()` | `totalTrades`, `maxTrades`, `onChange`, `error`, structured input/min-output/trigger-output amounts, input/output tokens |
370
+ | `useDuration()` | `duration`, input/unit callbacks, `milliseconds`, `error` |
371
+ | `useFillDelay()` | `fillDelay`, input/unit callbacks, `milliseconds`, `error` |
372
+ | `useLimitPrice()` | `price: { raw, ui, usd }`, canonical raw price, percentage, enable/toggle actions, tokens, loading/error state |
373
+ | `useTriggerPrice()` | `price: { raw, ui, usd }`, canonical raw price, percentage, structured output-per-trade amount, tokens, loading/error state |
374
+ | `usePriceDisplay()` | Inversion state/callback, actual and display-direction tokens, market-order state |
375
+ | `useDisclaimer()` | Disclaimer translation key or `undefined` |
376
+ | `useInputErrors()` | `{ type, args }` or `undefined` |
377
+ | `useSubmitButton()` | `disabled` and `loading` |
378
+ | `useExecution()` | Submission, status, steps, errors, resets, resolved tokens, and tx hashes |
379
+ | `useOrders()` | Provider-scoped categorized order lists and polling state |
380
+ | `useHistoryOrder()` | Display-ready values for one history order |
381
+ | `useCancelOrder()` | Per-order cancellation state and action |
382
+ | `useClient()` | Provider-scoped initialized client state |
383
+ | `useAmountUi()` | Raw-token amount formatting |
384
+
385
+ Child components should call the relevant focused hook themselves instead of receiving hook-returned panels through intermediate props. `useOrders()` activates order-history fetching while an orders consumer is mounted; form-only integrations do not fetch or poll history.
386
+
387
+ Keep the submit button disabled while `loading` is true. If client initialization fails, `SpotProvider` keeps child components mounted and renders the retryable `clientErrorFallback` alongside them.
388
+
389
+ ### Trade-count validation behavior
390
+
391
+ An explicitly selected TWAP trade count persists when `inputAmountUi` changes. If lowering the amount makes that count greater than the newly calculated `maxTrades`, Spot does not clamp or reset it: `useTrades().error` and `useInputErrors()` report `InputErrors.MAX_TRADES`, and submission remains disabled until the user selects a valid count. This is an intentional behavior change from integrations that silently reset the trade count on every amount edit. Hosts should render the returned validation error so the user can correct the value.
304
392
 
305
393
  ### Panel Visibility
306
394
 
@@ -316,87 +404,106 @@ Use `TimeUnit.Minutes`, `TimeUnit.Hours`, and `TimeUnit.Days` for duration and f
316
404
 
317
405
  ### Display Amounts
318
406
 
319
- Raw fields such as `dstTokenPanel.valueWei`, `tradesAmountPanel.amountPerTrade`, `derivedFormData.feesAmount`, and history fill amounts are integer strings. Convert them into the DEX's native amount type before display when possible:
407
+ Every calculated amount uses the same `{ raw, ui, usd }` shape. Convert `raw`
408
+ into the DEX's native amount type before display when possible:
320
409
 
321
410
  ```tsx
322
411
  const amount = CurrencyAmount.fromRawAmount(
323
412
  inputCurrency,
324
- spot.tradesAmountPanel.amountPerTrade,
413
+ tradesPanel.inputAmountPerTrade.raw,
325
414
  );
326
415
 
327
416
  return `${amount.toSignificant()} ${inputCurrency.symbol}`;
328
417
  ```
329
418
 
330
- Use `*UI` fields for editable text inputs or when the DEX has no amount object. Do not format raw integer strings directly for user display.
419
+ Use `.ui` for editable text inputs or when the DEX has no amount object. Do not
420
+ format `.raw` integer strings directly for user display.
331
421
 
332
422
  ## Submit and Progress Modal
333
423
 
334
- Use `useSpot().orderExecutionPanel` for execution state, `derivedFormData` for review details, and `@orbs-network/swap-ui`'s `SwapFlow` for the creation/progress UI.
424
+ Use `useExecution()` for execution state, `useOrderForm()` for review details, and `@orbs-network/swap-ui`'s `SwapFlow` for the creation/progress UI.
335
425
 
336
426
  ```tsx
337
- import { SwapFlow } from "@orbs-network/swap-ui";
338
- import { useSpot } from "@orbs-network/spot-react";
427
+ import { SwapFlow, SwapStatus as SwapUiStatus } from "@orbs-network/swap-ui";
428
+ import {
429
+ ExecutionStatus,
430
+ useExecution,
431
+ useOrderForm,
432
+ } from "@orbs-network/spot-react";
339
433
 
340
434
  function SpotOrderFlow() {
341
435
  const {
342
436
  status,
343
- parsedError,
344
- srcToken,
345
- dstToken,
346
- stepIndex,
437
+ error,
438
+ inputToken,
439
+ outputToken,
440
+ currentStepIndex,
347
441
  totalSteps,
348
- } = useSpot().orderExecutionPanel;
349
- const form = useSpot().derivedFormData;
442
+ } = useExecution();
443
+ const form = useOrderForm();
444
+ const swapStatus =
445
+ status === ExecutionStatus.SUCCESS
446
+ ? SwapUiStatus.SUCCESS
447
+ : status === ExecutionStatus.FAILED
448
+ ? SwapUiStatus.FAILED
449
+ : status === ExecutionStatus.LOADING
450
+ ? SwapUiStatus.LOADING
451
+ : undefined;
350
452
 
351
453
  return (
352
454
  <SwapFlow
353
- inAmount={form.srcAmountUI}
354
- outAmount={form.dstAmountUI}
355
- inToken={{ symbol: srcToken?.symbol, logoUrl: srcToken?.logoUrl }}
356
- outToken={{ symbol: dstToken?.symbol, logoUrl: dstToken?.logoUrl }}
357
- swapStatus={status}
358
- currentStepIndex={stepIndex}
455
+ inAmount={form.inputAmount.ui}
456
+ outAmount={form.outputAmount.ui}
457
+ inToken={{ symbol: inputToken?.symbol, logoUrl: inputToken?.logoUrl }}
458
+ outToken={{ symbol: outputToken?.symbol, logoUrl: outputToken?.logoUrl }}
459
+ swapStatus={swapStatus}
460
+ currentStepIndex={currentStepIndex}
359
461
  totalSteps={totalSteps}
360
462
  components={{
361
- Main: <SwapFlow.Main inUsd={form.srcAmountUsd} outUsd={form.dstAmountUsd} />,
463
+ Main: <SwapFlow.Main inUsd={form.inputAmount.usd} outUsd={form.outputAmount.usd} />,
362
464
  Success: <SwapFlow.Success title="Order created" />,
363
- Failed: <SwapFlow.Failed error={parsedError?.message} />,
465
+ Failed: <SwapFlow.Failed error={error?.message} />,
364
466
  }}
365
467
  />
366
468
  );
367
469
  }
368
470
  ```
369
471
 
370
- Wrap `SwapFlow` in the DEX's modal shell and skin it with DEX colors, surfaces, typography, token logos, and loaders. Once `orderExecutionPanel.status` is set, hide the review details, confirm button, duplicate title, and secondary footer actions so the progress/success/failure content owns the modal.
472
+ Wrap `SwapFlow` in the DEX's modal shell and skin it with DEX colors, surfaces, typography, token logos, and loaders. Once `useExecution().status` is set, hide the review details, confirm button, duplicate title, and secondary footer actions so the progress/success/failure content owns the modal.
371
473
 
372
474
  ### Execution State and Reset
373
475
 
374
- `orderExecutionPanel` includes:
476
+ `useExecution()` returns:
375
477
 
376
- - `onSubmit`, `status`, `isLoading`, `isSuccess`, `isFailed`, and `confirmButtonLoading`;
377
- - `step`, `stepIndex`, `totalSteps`, and `pendingSteps`;
378
- - `parsedError`, `error`, `srcToken`, `dstToken`, `wrapTxHash`, and `approveTxHash`;
379
- - `resetCurrentSwap()` and `resetState()`.
478
+ - `submitOrder`, `phase`, `status`, `isExecuting`, `isSuccess`, `isFailed`, `isRejected`, `isPreparingOrder`, and `canDismiss`;
479
+ - `currentStep`, `currentStepIndex`, `totalSteps`, and `executionSteps`;
480
+ - `error`, `inputToken`, `outputToken`, `chainId`, `wrapTxHash`, and `approvalTxHash`;
481
+ - `returnToOrderForm()` and `startNewOrder()`.
380
482
 
381
483
  `spot-react` does not clear the DEX input. Do that only when a successful modal closes:
382
484
 
383
485
  ```tsx
384
- const { status, isSuccess, resetCurrentSwap, resetState } =
385
- useSpot().orderExecutionPanel;
486
+ const { status, isSuccess, isExecuting, returnToOrderForm, startNewOrder } =
487
+ useExecution();
386
488
 
387
489
  const onClose = useCallback(() => {
490
+ if (isExecuting) return;
388
491
  setIsModalOpen(false);
389
492
 
390
493
  if (isSuccess) {
391
494
  setInputAmount("");
392
- setTimeout(resetState, 500);
495
+ setTimeout(startNewOrder, 500);
393
496
  } else if (status) {
394
- setTimeout(resetCurrentSwap, 500);
497
+ setTimeout(returnToOrderForm, 500);
395
498
  }
396
- }, [isSuccess, resetCurrentSwap, resetState, setInputAmount, status]);
499
+ }, [isExecuting, isSuccess, returnToOrderForm, setInputAmount, startNewOrder, status]);
397
500
  ```
398
501
 
399
- Failed or rejected submissions should keep the user's input. The short delay lets the close animation finish before state resets.
502
+ Neither reset action can clear an active execution: both return `false` while `isExecuting` is true. Keep the modal open during execution. `returnToOrderForm()` dismisses a failed or rejected execution while preserving the form and a completed native-token wrap for a safe retry. `startNewOrder()` resets Spot's internal form state and all retry metadata after a terminal execution; the host must still clear its own input amount. The short delay lets the close animation finish before state resets.
503
+
504
+ `phase` is the precise execution state: `idle → preparing → wrapping → approving → signing → submitting → success`, with `failed` and `rejected` terminal branches. `status` is the coarser `ExecutionStatus` value. Map it explicitly to the separately versioned `@orbs-network/swap-ui` `SwapStatus` enum as shown above. Each attempt has an internal identity, duplicate starts are rejected atomically, and stale async writes cannot replace a newer attempt. Host callbacks are observational: synchronous throws and rejected callback promises never change a wallet or API result.
505
+
506
+ Once submission begins, `useOrderForm()` exposes the frozen form and `useExecution()` exposes the frozen tokens and chain associated with that execution. The prepared order is frozen internally and reused through approval, wrapping, signing, and submission. Host quote, token, chain, or time updates cannot change the active order.
400
507
 
401
508
  ## Callbacks
402
509
 
@@ -405,9 +512,9 @@ Callbacks cover wallet notifications, analytics, field synchronization, and bala
405
512
  ```tsx
406
513
  const callbacks: Callbacks = {
407
514
  onWrapRequest: () => {},
408
- onWrapSuccess: ({ txHash, explorerUrl, amount }) => {},
515
+ onWrapSuccess: ({ txHash, amount }) => {},
409
516
  onApproveRequest: () => {},
410
- onApproveSuccess: ({ txHash, explorerUrl, token, amount }) => {},
517
+ onApproveSuccess: ({ txHash, token, amount }) => {},
411
518
  onSignOrderRequest: () => {},
412
519
  onSignOrderSuccess: (signature) => {},
413
520
  onSignOrderError: (error) => {},
@@ -417,16 +524,16 @@ const callbacks: Callbacks = {
417
524
  onSubmitOrderFailed: ({ code, message }) => {},
418
525
  onSubmitOrderRejected: () => {},
419
526
  onCancelOrderRequest: (order) => {},
420
- onCancelOrderSuccess: ({ order, txHash, explorerUrl }) => {},
527
+ onCancelOrderSuccess: ({ order, txHash }) => {},
421
528
  onCancelOrderFailed: (error) => {},
422
529
  onCopy: () => {},
423
530
  onLimitPriceChange: (price) => {},
424
531
  onLimitPricePercentChange: (percent) => {},
425
532
  onTriggerPriceChange: (price) => {},
426
533
  onTriggerPricePercentChange: (percent) => {},
427
- onDurationChange: (duration) => {},
428
- onFillDelayChange: (fillDelay) => {},
429
- onChunksChange: (chunks) => {},
534
+ onOrderDurationChange: (orderDuration) => {},
535
+ onTradeIntervalChange: (tradeInterval) => {},
536
+ onTradeCountChange: (tradeCount) => {},
430
537
  };
431
538
  ```
432
539
 
@@ -453,18 +560,31 @@ function CancelButton({ order }) {
453
560
  }
454
561
  ```
455
562
 
456
- For v2 orders, `disabled` remains true while RePermit configuration is loading or unavailable. Legacy v1 history and cancellation do not require that configuration. V2 history is added after configuration succeeds because its request needs the returned exchange adapter.
563
+ Cancellation uses the initialized client for both v1 and v2 orders, keeping request construction on one path; `disabled` remains true while that client is loading or unavailable. History starts after client initialization because v2 requests need the configured exchange adapter. When `supportLegacyOrders` is enabled, v1 history is loaded once and retained while v2 history continues polling.
457
564
 
458
- Order history is available from `useSpot().orderHistoryPanel`:
565
+ Order history is available directly from `useOrders()`. The query is enabled only while a component using this hook is mounted:
459
566
 
460
567
  ```tsx
461
- const { orders, isLoading, isRefetching, refetchOrders } =
462
- useSpot().orderHistoryPanel;
568
+ const { data: orders, isLoading, isRefetching, refetch } = useOrders();
463
569
 
464
- // orders.all, orders.open, orders.completed, orders.cancelled, orders.expired
570
+ // orders?.all, orders?.open, orders?.completed, orders?.cancelled, orders?.expired
465
571
  ```
466
572
 
467
- Use `useDerivedHistoryOrder(order, srcToken?, dstToken?)` for display fields. For large histories, use the virtualization library already present in the DEX for both the orders and fills lists. Store the selected order ID, then look up the current order from `orders.all`; do not store a stale copy of the order object.
573
+ Like `useClient()`, `data` is undefined before the first result. `refetch()`
574
+ resolves to the latest categorized orders or `undefined` when no request can be
575
+ made. Default the category you render to an empty array, for example
576
+ `const allOrders = orders?.all ?? []`.
577
+
578
+ Use `useHistoryOrder(order, inputToken?, outputToken?)` for display fields. For large histories, use the virtualization library already present in the DEX for both the orders and fills lists. Store the selected `order.historyKey`, then look up the current order from `orders?.all ?? []` by that key; do not store a stale copy of the order object. The key avoids collisions between numeric v1 IDs from different TWAP contracts; `order.id` remains the protocol ID used for display and cancellation.
579
+
580
+ Amounts returned by `useHistoryOrder` and its `fills` use `{ raw, ui }`, matching
581
+ the form model without fabricating unavailable historical USD values.
582
+
583
+ `OrderFilter` contains `ALL`, `OPEN`, `COMPLETED`, `CANCELLED`, and `EXPIRED`.
584
+ `OrderType` identifies the calculated module/mode combination: limit, TWAP
585
+ market/limit, stop-loss market/limit, or take-profit market/limit. Normalized
586
+ `Order` and `OrderFill` retain `src`/`dst` and `in`/`out` names because those are
587
+ protocol/history response models; form-facing APIs use `input`/`output`.
468
588
 
469
589
  Keep history, details, and modal portals under `SpotProvider` context. Details should include execution summary, order info, fills, explorer/copy actions, and cancellation for open orders.
470
590
 
@@ -478,35 +598,41 @@ Disclaimer keys are:
478
598
  - `marketOrderDisclaimer`
479
599
  - `triggerMarketPriceDisclaimer`
480
600
 
481
- Input errors have the shape `{ type, args }`. Resolve `type` through the DEX's i18n system and interpolate `args`, including values such as `maxChunks`, `minChunks`, `minTradeSize`, `duration`, and `fillDelay`. Current duration and fill-delay arguments are human-readable; custom or older integrations should convert raw milliseconds before display.
601
+ Input errors have the shape `{ type, args }`. Resolve `type` through the DEX's i18n system and interpolate `args`, including values such as `maxTrades`, `minTrades`, `minTradeSize`, `duration`, and `fillDelay`. Current duration and fill-delay arguments are human-readable; custom or older integrations should convert raw milliseconds before display.
482
602
 
483
- ## Helper and Advanced APIs
603
+ ## React helper APIs
484
604
 
485
605
  ```tsx
486
606
  import {
487
- useAmountUi, // (decimals?, rawAmount?) => formatted amount
488
- useExplorerLink, // (txHash?) => explorer URL
489
- useNetwork, // () => current network metadata
490
- useRePermitData, // () => { data, error, isLoading, refetch }
491
- useSignOrder, // low-level signing hook
492
- useSubmitOrder, // low-level submission mutation
493
- useSwapExecution, // low-level execution state
607
+ useAmountUi,
608
+ useClient,
494
609
  } from "@orbs-network/spot-react";
610
+
611
+ useAmountUi(decimals, rawAmount);
612
+ useClient();
495
613
  ```
496
614
 
497
- Normal integrations should submit through `useSpot().orderExecutionPanel`. Use the low-level hooks only when deliberately replacing the built-in execution flow. `useRePermitData()` must be called under `SpotProvider`; most integrations should use the configuration state already exposed by `useSpot().submitOrderButton`.
615
+ Submit through `useExecution()`. Internal mutations and execution-store hooks
616
+ are deliberately not exported, keeping one supported workflow and one error
617
+ contract. `useClient()` must be called under `SpotProvider`; every caller shares
618
+ the same partner/chain query, and RePermit configuration is available as
619
+ `client?.rePermitData`. Initialization recovery belongs to
620
+ `clientErrorFallback`, which receives the query error and retry action.
498
621
 
499
622
  Public utilities and constants include:
500
623
 
501
624
  ```tsx
502
625
  import {
503
- getMinChunkSizeUsd,
504
- getNetwork,
505
626
  getOrderExecutionRate,
506
627
  getOrderFillDelayMillis,
507
628
  getOrderLimitPriceRate,
508
629
  getPartnerChains,
509
630
  getPartners,
631
+ getTwapConfig,
632
+ calculateOrderForm,
633
+ toAmountRaw,
634
+ toAmountUI,
635
+ invertPriceInput,
510
636
  getTriggerPriceRate,
511
637
  eqIgnoreCase,
512
638
  isNativeAddress,
@@ -516,11 +642,14 @@ import {
516
642
  ORBS_LOGO,
517
643
  ORBS_WEBSITE_URL,
518
644
  SPOT_VERSION,
519
- networks,
520
645
  } from "@orbs-network/spot-react";
521
646
  ```
522
647
 
523
- Always import from `@orbs-network/spot-react`. Do not import from `dist/*` or package-internal source paths, and verify public exports when upgrading.
648
+ React integrations can import the single `calculateOrderForm` entry point and
649
+ its public calculated types directly from `@orbs-network/spot-react`. Non-React
650
+ integrations should import them from `@orbs-network/spot-ui`. Lower-level
651
+ calculators remain internal entry points; do not import from `dist/*` or
652
+ package-internal source paths.
524
653
 
525
654
  ## Integration Checklist
526
655