@switch-win/sdk 1.2.9 → 1.3.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.
- package/CHANGELOG.md +34 -0
- package/GASLESS.md +434 -0
- package/LIMIT-ORDERS.md +213 -205
- package/README.md +134 -105
- package/ROBINHOOD.md +201 -198
- package/abi/GaslessSwitchV2ABI.json +1095 -0
- package/dist/constants.d.ts +276 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +403 -0
- package/dist/constants.js.map +1 -0
- package/dist/gasless-client.d.ts +82 -0
- package/dist/gasless-client.d.ts.map +1 -0
- package/dist/gasless-client.js +467 -0
- package/dist/gasless-client.js.map +1 -0
- package/dist/gasless-index.d.ts +5 -0
- package/dist/gasless-index.d.ts.map +1 -0
- package/dist/gasless-index.js +5 -0
- package/dist/gasless-index.js.map +1 -0
- package/dist/gasless-operators.d.ts +32 -0
- package/dist/gasless-operators.d.ts.map +1 -0
- package/dist/gasless-operators.js +132 -0
- package/dist/gasless-operators.js.map +1 -0
- package/dist/gasless-store.d.ts +18 -0
- package/dist/gasless-store.d.ts.map +1 -0
- package/dist/gasless-store.js +83 -0
- package/dist/gasless-store.js.map +1 -0
- package/dist/gasless-types.d.ts +222 -0
- package/dist/gasless-types.d.ts.map +1 -0
- package/dist/gasless-types.js +2 -0
- package/dist/gasless-types.js.map +1 -0
- package/dist/gasless.d.ts +135 -0
- package/dist/gasless.d.ts.map +1 -0
- package/dist/gasless.js +513 -0
- package/dist/gasless.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/limit-orders.d.ts +440 -0
- package/dist/limit-orders.d.ts.map +1 -0
- package/dist/limit-orders.js +387 -0
- package/dist/limit-orders.js.map +1 -0
- package/dist/networks/index.d.ts +3 -0
- package/dist/networks/index.d.ts.map +1 -0
- package/dist/networks/index.js +2 -0
- package/dist/networks/index.js.map +1 -0
- package/dist/networks/robinhood.d.ts +536 -0
- package/dist/networks/robinhood.d.ts.map +1 -0
- package/dist/networks/robinhood.js +489 -0
- package/dist/networks/robinhood.js.map +1 -0
- package/dist/types.d.ts +436 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/examples/gasless-nextjs-proxy.ts +87 -0
- package/examples/gasless-operator.ts +76 -0
- package/examples/gasless-swap.ts +173 -0
- package/examples/limit-order-ethers.ts +195 -0
- package/examples/nextjs-proxy.ts +82 -0
- package/examples/react-hooks.tsx +396 -0
- package/examples/swap-ethers.ts +209 -0
- package/examples/swap-web3py.py +267 -0
- package/package.json +53 -12
- package/src/constants.ts +184 -182
- package/src/gasless-client.ts +824 -0
- package/src/gasless-index.ts +4 -0
- package/src/gasless-operators.ts +227 -0
- package/src/gasless-store.ts +137 -0
- package/src/gasless-types.ts +262 -0
- package/src/gasless.ts +756 -0
- package/src/index.ts +69 -63
- package/src/limit-orders.ts +206 -195
- package/src/types.ts +80 -55
package/LIMIT-ORDERS.md
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
# Switch Limit Orders — Integration Guide
|
|
2
2
|
|
|
3
|
-
> **Gasless EIP-712 signed limit orders on PulseChain and Robinhood Chain**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
> **Gasless EIP-712 signed limit orders on PulseChain and Robinhood Chain**
|
|
4
|
+
|
|
5
|
+
This guide describes ordinary limit orders. The dedicated GaslessSwitch swap
|
|
6
|
+
option has a separate domain, contract, fee, payout cap and approval target;
|
|
7
|
+
use [GASLESS.md](GASLESS.md) and the dedicated SDK helpers for that flow.
|
|
8
|
+
|
|
9
|
+
**Limit Order API:** `https://quote.switch.win`
|
|
10
|
+
|
|
11
|
+
| API network | Chain ID | Native currency | Current limit-order contract | Native flow contract |
|
|
12
|
+
|---|---:|---|---|---|
|
|
13
|
+
| `pulsechain` | 369 | PLS | `0x2afBf0aB8d958a0227742F7a8BdA00c96372E4D7` | `0x0362177FF2ad25a33a879c881a5055575C63a4cE` |
|
|
14
|
+
| `robinhood` | 4663 | ETH | `0x1E05115387f314398bbb1A808B25308E71150396` | `0x8170a3B0e2FD2e4333E0Ca9c9414B2D3dd6aF689` |
|
|
15
|
+
|
|
16
|
+
Pass `network: "pulsechain"` or `network: "robinhood"` to every SDK API
|
|
17
|
+
helper. Call `fetchLimitOrderConfig({ network })` at startup and treat its
|
|
18
|
+
addresses and EIP-712 domain as the live source of truth.
|
|
15
19
|
|
|
16
20
|
---
|
|
17
21
|
|
|
@@ -21,7 +25,7 @@ addresses and EIP-712 domain as the live source of truth.
|
|
|
21
25
|
1. [Overview](#overview)
|
|
22
26
|
2. [How It Works](#how-it-works)
|
|
23
27
|
3. [Creating a Limit Order](#creating-a-limit-order)
|
|
24
|
-
4. [Native Currency Limit Orders (SwitchPLSFlow)](#native-currency-limit-orders-switchplsflow)
|
|
28
|
+
4. [Native Currency Limit Orders (SwitchPLSFlow)](#native-currency-limit-orders-switchplsflow)
|
|
25
29
|
5. [Choosing `feeOnOutput`](#choosing-feeonoutput)
|
|
26
30
|
6. [Querying Limit Orders](#querying-limit-orders)
|
|
27
31
|
7. [Cancelling a Limit Order](#cancelling-a-limit-order)
|
|
@@ -60,37 +64,37 @@ Switch Limit Orders let users place **gasless, signed orders** that are filled a
|
|
|
60
64
|
- **Nonce-based replay protection** — each order has a unique nonce per maker
|
|
61
65
|
- **Optional expiry** — set a deadline or make the order valid forever
|
|
62
66
|
- **Custom recipient** — output tokens can be sent to a different address
|
|
63
|
-
- **Native unwrap** — WPLS can be unwrapped to PLS and Robinhood WETH can be
|
|
64
|
-
unwrapped to ETH by setting `unwrapOutput`
|
|
65
|
-
|
|
66
|
-
> **⚠️ Important:** The `SWITCH_LIMIT_ORDER` address is the **current** default. The contract may be redeployed (e.g. when the router is upgraded). Each order returned by the API includes a `limitOrderContract` field — **always use the contract address from the order for on-chain interactions (approvals, cancellations), not a hardcoded constant.** This ensures your integration works seamlessly across contract versions without code changes.
|
|
67
|
-
|
|
68
|
-
### Network-aware setup
|
|
69
|
-
|
|
70
|
-
```ts
|
|
71
|
-
import {
|
|
72
|
-
fetchLimitOrderConfig,
|
|
73
|
-
getLimitOrderApprovalTarget,
|
|
74
|
-
getNetworkEIP712SigningParams,
|
|
75
|
-
submitLimitOrder,
|
|
76
|
-
} from "@switch-win/sdk/limit-orders";
|
|
77
|
-
|
|
78
|
-
// `signer` and a completed `order` are assumed below.
|
|
79
|
-
const network = "robinhood" as const;
|
|
80
|
-
const live = await fetchLimitOrderConfig({ network });
|
|
81
|
-
const { domain, types } = getNetworkEIP712SigningParams(
|
|
82
|
-
network,
|
|
83
|
-
live.limitOrderContract,
|
|
84
|
-
);
|
|
85
|
-
const approvalTarget = getLimitOrderApprovalTarget(network, order.feeOnOutput, {
|
|
86
|
-
limitOrderContract: live.limitOrderContract,
|
|
87
|
-
});
|
|
88
|
-
const signature = await signer.signTypedData(domain, types, order);
|
|
89
|
-
await submitLimitOrder(
|
|
90
|
-
{ ...order, signature, limitOrderContract: live.limitOrderContract },
|
|
91
|
-
{ network },
|
|
92
|
-
);
|
|
93
|
-
```
|
|
67
|
+
- **Native unwrap** — WPLS can be unwrapped to PLS and Robinhood WETH can be
|
|
68
|
+
unwrapped to ETH by setting `unwrapOutput`
|
|
69
|
+
|
|
70
|
+
> **⚠️ Important:** The `SWITCH_LIMIT_ORDER` address is the **current** default. The contract may be redeployed (e.g. when the router is upgraded). Each order returned by the API includes a `limitOrderContract` field — **always use the contract address from the order for on-chain interactions (approvals, cancellations), not a hardcoded constant.** This ensures your integration works seamlessly across contract versions without code changes.
|
|
71
|
+
|
|
72
|
+
### Network-aware setup
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import {
|
|
76
|
+
fetchLimitOrderConfig,
|
|
77
|
+
getLimitOrderApprovalTarget,
|
|
78
|
+
getNetworkEIP712SigningParams,
|
|
79
|
+
submitLimitOrder,
|
|
80
|
+
} from "@switch-win/sdk/limit-orders";
|
|
81
|
+
|
|
82
|
+
// `signer` and a completed `order` are assumed below.
|
|
83
|
+
const network = "robinhood" as const;
|
|
84
|
+
const live = await fetchLimitOrderConfig({ network });
|
|
85
|
+
const { domain, types } = getNetworkEIP712SigningParams(
|
|
86
|
+
network,
|
|
87
|
+
live.limitOrderContract,
|
|
88
|
+
);
|
|
89
|
+
const approvalTarget = getLimitOrderApprovalTarget(network, order.feeOnOutput, {
|
|
90
|
+
limitOrderContract: live.limitOrderContract,
|
|
91
|
+
});
|
|
92
|
+
const signature = await signer.signTypedData(domain, types, order);
|
|
93
|
+
await submitLimitOrder(
|
|
94
|
+
{ ...order, signature, limitOrderContract: live.limitOrderContract },
|
|
95
|
+
{ network },
|
|
96
|
+
);
|
|
97
|
+
```
|
|
94
98
|
|
|
95
99
|
---
|
|
96
100
|
|
|
@@ -113,9 +117,9 @@ await submitLimitOrder(
|
|
|
113
117
|
│ Tokens are pulled from the maker's wallet and swapped. │
|
|
114
118
|
│ Output is sent to the maker (or custom recipient). │
|
|
115
119
|
│ │
|
|
116
|
-
│ 5. CANCEL (optional) │
|
|
117
|
-
│ invalidateNonce(nonce) on-chain — prevents execution │
|
|
118
|
-
│ The backend indexer observes the cancellation event. │
|
|
120
|
+
│ 5. CANCEL (optional) │
|
|
121
|
+
│ invalidateNonce(nonce) on-chain — prevents execution │
|
|
122
|
+
│ The backend indexer observes the cancellation event. │
|
|
119
123
|
└─────────────────────────────────────────────────────────────────────────┘
|
|
120
124
|
```
|
|
121
125
|
|
|
@@ -131,10 +135,12 @@ await submitLimitOrder(
|
|
|
131
135
|
> Your integration must:
|
|
132
136
|
> 1. Call `submitLimitOrder()` **immediately** after the user signs.
|
|
133
137
|
> 2. **Await** the response and confirm `success: true` before showing "order created".
|
|
134
|
-
> 3.
|
|
138
|
+
> 3. On a transient failure, look up that exact maker + nonce and verify its
|
|
139
|
+
> contract and signed fields. Duplicates return an error, not idempotent
|
|
140
|
+
> success. Retry only the original signed payload; never generate a new nonce.
|
|
135
141
|
> 4. Do **not** navigate away or close the signing flow until the backend confirms.
|
|
136
142
|
>
|
|
137
|
-
> *Native PLS/ETH flow orders are the exception — they are recorded on-chain*
|
|
143
|
+
> *Native PLS/ETH flow orders are the exception — they are recorded on-chain*
|
|
138
144
|
> *first, so the backend discovers them via event indexing even if the POST*
|
|
139
145
|
> *never arrives.*
|
|
140
146
|
|
|
@@ -146,28 +152,28 @@ The full lifecycle in code (ethers.js v6):
|
|
|
146
152
|
|
|
147
153
|
```ts
|
|
148
154
|
import { ethers } from "ethers";
|
|
149
|
-
import {
|
|
150
|
-
buildLimitOrder,
|
|
151
|
-
fetchLimitOrderConfig,
|
|
152
|
-
getLimitOrderApprovalTarget,
|
|
153
|
-
getNetworkEIP712SigningParams,
|
|
154
|
-
submitLimitOrder,
|
|
155
|
-
} from "@switch-win/sdk/limit-orders";
|
|
155
|
+
import {
|
|
156
|
+
buildLimitOrder,
|
|
157
|
+
fetchLimitOrderConfig,
|
|
158
|
+
getLimitOrderApprovalTarget,
|
|
159
|
+
getNetworkEIP712SigningParams,
|
|
160
|
+
submitLimitOrder,
|
|
161
|
+
} from "@switch-win/sdk/limit-orders";
|
|
156
162
|
import { ERC20_ABI } from "@switch-win/sdk/constants";
|
|
157
163
|
|
|
158
|
-
const network = "robinhood" as const;
|
|
159
|
-
const provider = new ethers.JsonRpcProvider("https://rpc.mainnet.chain.robinhood.com");
|
|
160
|
-
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
|
|
161
|
-
const maker = await signer.getAddress();
|
|
162
|
-
const live = await fetchLimitOrderConfig({ network });
|
|
164
|
+
const network = "robinhood" as const;
|
|
165
|
+
const provider = new ethers.JsonRpcProvider("https://rpc.mainnet.chain.robinhood.com");
|
|
166
|
+
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
|
|
167
|
+
const maker = await signer.getAddress();
|
|
168
|
+
const live = await fetchLimitOrderConfig({ network });
|
|
163
169
|
|
|
164
170
|
// ── Step 1: Build the order ──
|
|
165
171
|
const order = buildLimitOrder({
|
|
166
172
|
maker,
|
|
167
|
-
tokenIn: "0x0Bd7D308f8E1639FAb988Df18A8011f41EAcAD73", // WETH
|
|
168
|
-
tokenOut: "0x5fc5360D0400a0Fd4F2aF552aDD042D716F1d168", // USDG
|
|
169
|
-
amountIn: ethers.parseUnits("0.01", 18).toString(),
|
|
170
|
-
minAmountOut: ethers.parseUnits("30", 18).toString(),
|
|
173
|
+
tokenIn: "0x0Bd7D308f8E1639FAb988Df18A8011f41EAcAD73", // WETH
|
|
174
|
+
tokenOut: "0x5fc5360D0400a0Fd4F2aF552aDD042D716F1d168", // USDG
|
|
175
|
+
amountIn: ethers.parseUnits("0.01", 18).toString(),
|
|
176
|
+
minAmountOut: ethers.parseUnits("30", 18).toString(),
|
|
171
177
|
deadline: Math.floor(Date.now() / 1000) + 86400, // 24h expiry
|
|
172
178
|
// nonce: auto-generated from Date.now()
|
|
173
179
|
// feeOnOutput: false (default — fee taken from input)
|
|
@@ -176,11 +182,11 @@ const order = buildLimitOrder({
|
|
|
176
182
|
});
|
|
177
183
|
|
|
178
184
|
// ── Step 2: Approve the correct contract (one-time per token + fee mode) ──
|
|
179
|
-
// feeOnOutput=false (default) → approve this network's LO contract
|
|
180
|
-
// feeOnOutput=true → approve this network's SwitchRouter
|
|
181
|
-
const approvalTarget = getLimitOrderApprovalTarget(network, order.feeOnOutput, {
|
|
182
|
-
limitOrderContract: live.limitOrderContract,
|
|
183
|
-
});
|
|
185
|
+
// feeOnOutput=false (default) → approve this network's LO contract
|
|
186
|
+
// feeOnOutput=true → approve this network's SwitchRouter
|
|
187
|
+
const approvalTarget = getLimitOrderApprovalTarget(network, order.feeOnOutput, {
|
|
188
|
+
limitOrderContract: live.limitOrderContract,
|
|
189
|
+
});
|
|
184
190
|
const token = new ethers.Contract(order.tokenIn, ERC20_ABI, signer);
|
|
185
191
|
const allowance: bigint = await token.allowance(maker, approvalTarget);
|
|
186
192
|
|
|
@@ -190,23 +196,23 @@ if (allowance < BigInt(order.amountIn)) {
|
|
|
190
196
|
}
|
|
191
197
|
|
|
192
198
|
// ── Step 3: Sign via EIP-712 (gasless!) ──
|
|
193
|
-
const { domain, types } = getNetworkEIP712SigningParams(
|
|
194
|
-
network,
|
|
195
|
-
live.limitOrderContract,
|
|
196
|
-
);
|
|
197
|
-
const signature = await signer.signTypedData(domain, types, order);
|
|
199
|
+
const { domain, types } = getNetworkEIP712SigningParams(
|
|
200
|
+
network,
|
|
201
|
+
live.limitOrderContract,
|
|
202
|
+
);
|
|
203
|
+
const signature = await signer.signTypedData(domain, types, order);
|
|
198
204
|
|
|
199
205
|
// ── Step 4: Submit to the Switch backend ──
|
|
200
206
|
// ⚠️ CRITICAL: The signature is off-chain only — if this POST fails,
|
|
201
207
|
// the order is lost. Submit IMMEDIATELY after signing, await the
|
|
202
208
|
// response, and retry on network failure.
|
|
203
|
-
const result = await submitLimitOrder(
|
|
204
|
-
{ ...order, signature, limitOrderContract: live.limitOrderContract },
|
|
205
|
-
{ network },
|
|
206
|
-
);
|
|
209
|
+
const result = await submitLimitOrder(
|
|
210
|
+
{ ...order, signature, limitOrderContract: live.limitOrderContract },
|
|
211
|
+
{ network },
|
|
212
|
+
);
|
|
207
213
|
|
|
208
214
|
if ("error" in result) {
|
|
209
|
-
//
|
|
215
|
+
// Look up the exact order before retrying the identical signed payload.
|
|
210
216
|
console.error("Failed:", result.error);
|
|
211
217
|
} else {
|
|
212
218
|
console.log("Order created:", result.order.id);
|
|
@@ -227,68 +233,68 @@ if ("error" in result) {
|
|
|
227
233
|
| `nonce` | `number` | No | `Date.now()` | Unique per maker. Auto-generated if omitted |
|
|
228
234
|
| `feeOnOutput` | `boolean` | No | `false` | Fee mode — see [Choosing `feeOnOutput`](#choosing-feeonoutput) below |
|
|
229
235
|
| `recipient` | `string` | No | `maker` | Address to receive output tokens |
|
|
230
|
-
| `unwrapOutput` | `boolean` | No | `false` | Unwrap wrapped native output to PLS/ETH on the selected network |
|
|
236
|
+
| `unwrapOutput` | `boolean` | No | `false` | Unwrap wrapped native output to PLS/ETH on the selected network |
|
|
231
237
|
|
|
232
238
|
---
|
|
233
239
|
|
|
234
|
-
## Native Currency Limit Orders (SwitchPLSFlow)
|
|
235
|
-
|
|
236
|
-
When selling native **PLS** on PulseChain or native **ETH** on Robinhood
|
|
237
|
-
(rather than WPLS/WETH), use the selected network's native-flow contract
|
|
238
|
-
instead of the standard EIP-712 signing flow. The deployed contract retains
|
|
239
|
-
the legacy `SwitchPLSFlow` name on both networks, but its behavior is
|
|
240
|
-
native-currency neutral:
|
|
240
|
+
## Native Currency Limit Orders (SwitchPLSFlow)
|
|
241
|
+
|
|
242
|
+
When selling native **PLS** on PulseChain or native **ETH** on Robinhood
|
|
243
|
+
(rather than WPLS/WETH), use the selected network's native-flow contract
|
|
244
|
+
instead of the standard EIP-712 signing flow. The deployed contract retains
|
|
245
|
+
the legacy `SwitchPLSFlow` name on both networks, but its behavior is
|
|
246
|
+
native-currency neutral:
|
|
241
247
|
|
|
242
|
-
- **No approval needed** — users send the native currency directly to the contract
|
|
248
|
+
- **No approval needed** — users send the native currency directly to the contract
|
|
243
249
|
- **No EIP-712 signature** — the contract creates the order on-chain immediately
|
|
244
250
|
- **Single transaction** — wrap + approve + place order all in one tx
|
|
245
|
-
- **Fully indexed** — the backend discovers orders via `PLSOrderCreated` events
|
|
246
|
-
- **Drain-mode capable** — maintainers can disable new native deposits without
|
|
247
|
-
blocking fills or cancellations of existing orders
|
|
248
|
-
|
|
249
|
-
> **Current ABI:** native-flow deployments use the seven-argument
|
|
250
|
-
> `createOrder(tokenOut, minAmountOut, deadline, feeOnOutput, unwrapOutput,
|
|
251
|
-
> partnerAddress, recipient)` signature. The `recipient` argument must be
|
|
252
|
-
> present in calldata; pass the zero address to default it to `msg.sender`.
|
|
253
|
-
> The older six-argument selector is not compatible with current deployments.
|
|
254
|
-
|
|
255
|
-
### Native-flow contract address
|
|
251
|
+
- **Fully indexed** — the backend discovers orders via `PLSOrderCreated` events
|
|
252
|
+
- **Drain-mode capable** — maintainers can disable new native deposits without
|
|
253
|
+
blocking fills or cancellations of existing orders
|
|
254
|
+
|
|
255
|
+
> **Current ABI:** native-flow deployments use the seven-argument
|
|
256
|
+
> `createOrder(tokenOut, minAmountOut, deadline, feeOnOutput, unwrapOutput,
|
|
257
|
+
> partnerAddress, recipient)` signature. The `recipient` argument must be
|
|
258
|
+
> present in calldata; pass the zero address to default it to `msg.sender`.
|
|
259
|
+
> The older six-argument selector is not compatible with current deployments.
|
|
260
|
+
|
|
261
|
+
### Native-flow contract address
|
|
256
262
|
|
|
257
263
|
```ts
|
|
258
|
-
import { getNativeFlowAddress, PLS_FLOW_ABI } from "@switch-win/sdk";
|
|
259
|
-
|
|
260
|
-
const robinhoodNativeFlow = getNativeFlowAddress("robinhood");
|
|
261
|
-
// "0x8170a3B0e2FD2e4333E0Ca9c9414B2D3dd6aF689"
|
|
264
|
+
import { getNativeFlowAddress, PLS_FLOW_ABI } from "@switch-win/sdk";
|
|
265
|
+
|
|
266
|
+
const robinhoodNativeFlow = getNativeFlowAddress("robinhood");
|
|
267
|
+
// "0x8170a3B0e2FD2e4333E0Ca9c9414B2D3dd6aF689"
|
|
262
268
|
```
|
|
263
269
|
|
|
264
|
-
### Creating a native ETH limit order on Robinhood
|
|
270
|
+
### Creating a native ETH limit order on Robinhood
|
|
265
271
|
|
|
266
272
|
```ts
|
|
267
273
|
import { ethers } from "ethers";
|
|
268
|
-
import { getNativeFlowAddress, isNativeCurrency } from "@switch-win/sdk/limit-orders";
|
|
269
|
-
import { PLS_FLOW_ABI } from "@switch-win/sdk/constants";
|
|
270
|
-
|
|
271
|
-
const network = "robinhood" as const;
|
|
272
|
-
const provider = new ethers.JsonRpcProvider("https://rpc.mainnet.chain.robinhood.com");
|
|
273
|
-
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
|
|
274
|
-
|
|
275
|
-
const NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
276
|
-
if (isNativeCurrency(NATIVE)) {
|
|
277
|
-
const nativeFlow = new ethers.Contract(
|
|
278
|
-
getNativeFlowAddress(network),
|
|
279
|
-
PLS_FLOW_ABI,
|
|
280
|
-
signer,
|
|
281
|
-
);
|
|
274
|
+
import { getNativeFlowAddress, isNativeCurrency } from "@switch-win/sdk/limit-orders";
|
|
275
|
+
import { PLS_FLOW_ABI } from "@switch-win/sdk/constants";
|
|
276
|
+
|
|
277
|
+
const network = "robinhood" as const;
|
|
278
|
+
const provider = new ethers.JsonRpcProvider("https://rpc.mainnet.chain.robinhood.com");
|
|
279
|
+
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
|
|
280
|
+
|
|
281
|
+
const NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
282
|
+
if (isNativeCurrency(NATIVE)) {
|
|
283
|
+
const nativeFlow = new ethers.Contract(
|
|
284
|
+
getNativeFlowAddress(network),
|
|
285
|
+
PLS_FLOW_ABI,
|
|
286
|
+
signer,
|
|
287
|
+
);
|
|
282
288
|
|
|
283
|
-
const tokenOut = "0x5fc5360D0400a0Fd4F2aF552aDD042D716F1d168"; // USDG
|
|
284
|
-
const amountIn = ethers.parseEther("0.01");
|
|
285
|
-
const minAmountOut = ethers.parseUnits("30", 18);
|
|
289
|
+
const tokenOut = "0x5fc5360D0400a0Fd4F2aF552aDD042D716F1d168"; // USDG
|
|
290
|
+
const amountIn = ethers.parseEther("0.01");
|
|
291
|
+
const minAmountOut = ethers.parseUnits("30", 18);
|
|
286
292
|
const deadline = Math.floor(Date.now() / 1000) + 86400; // 24h expiry
|
|
287
293
|
const feeOnOutput = false;
|
|
288
294
|
const unwrapOutput = false;
|
|
289
295
|
|
|
290
296
|
// Single transaction — no approval, no signing
|
|
291
|
-
const tx = await nativeFlow.createOrder(
|
|
297
|
+
const tx = await nativeFlow.createOrder(
|
|
292
298
|
tokenOut,
|
|
293
299
|
minAmountOut,
|
|
294
300
|
deadline,
|
|
@@ -300,65 +306,65 @@ if (isNativeCurrency(NATIVE)) {
|
|
|
300
306
|
);
|
|
301
307
|
|
|
302
308
|
const receipt = await tx.wait();
|
|
303
|
-
console.log("Native ETH limit order created in tx:", receipt.hash);
|
|
309
|
+
console.log("Native ETH limit order created in tx:", receipt.hash);
|
|
304
310
|
|
|
305
311
|
// The backend will automatically detect this order via PLSOrderCreated event.
|
|
306
312
|
// No POST to /limit-orders required (though it's harmless if you do).
|
|
307
313
|
}
|
|
308
314
|
```
|
|
309
315
|
|
|
310
|
-
### Native flow vs EIP-712 orders
|
|
316
|
+
### Native flow vs EIP-712 orders
|
|
311
317
|
|
|
312
|
-
| Aspect | Native flow (PLS/ETH) | EIP-712 (ERC-20) |
|
|
318
|
+
| Aspect | Native flow (PLS/ETH) | EIP-712 (ERC-20) |
|
|
313
319
|
|---|---|---|
|
|
314
|
-
| **Input token** | Native currency | Any ERC-20 (including WPLS/WETH) |
|
|
320
|
+
| **Input token** | Native currency | Any ERC-20 (including WPLS/WETH) |
|
|
315
321
|
| **User experience** | Single transaction | Approve + Sign + Submit |
|
|
316
322
|
| **Gas cost** | User pays gas | Gasless signing (user pays on fill) |
|
|
317
323
|
| **Order discovery** | On-chain event | Requires successful POST |
|
|
318
|
-
| **Maker address** | Native-flow contract | User's wallet |
|
|
324
|
+
| **Maker address** | Native-flow contract | User's wallet |
|
|
319
325
|
| **Recipient** | User's wallet (or custom) | User's wallet (or custom) |
|
|
320
326
|
|
|
321
327
|
### Important: Order Discovery
|
|
322
328
|
|
|
323
|
-
For native-flow orders, the `maker` field in the order record is the
|
|
324
|
-
**native-flow contract address**, not the user's address. The actual user is
|
|
325
|
-
stored in the `recipient` field.
|
|
329
|
+
For native-flow orders, the `maker` field in the order record is the
|
|
330
|
+
**native-flow contract address**, not the user's address. The actual user is
|
|
331
|
+
stored in the `recipient` field.
|
|
326
332
|
|
|
327
333
|
When querying orders for a user, use the `owner` parameter instead of `maker`:
|
|
328
334
|
|
|
329
335
|
```ts
|
|
330
|
-
// ✅ Correct — finds both EIP-712 and native-flow orders
|
|
331
|
-
const { orders } = await fetchLimitOrders({
|
|
332
|
-
network: "robinhood",
|
|
336
|
+
// ✅ Correct — finds both EIP-712 and native-flow orders
|
|
337
|
+
const { orders } = await fetchLimitOrders({
|
|
338
|
+
network: "robinhood",
|
|
333
339
|
owner: "0xUserAddress", // matches maker OR recipient
|
|
334
340
|
status: "ACTIVE",
|
|
335
341
|
});
|
|
336
342
|
|
|
337
|
-
// ❌ Won't find native-flow orders
|
|
338
|
-
const { orders } = await fetchLimitOrders({
|
|
339
|
-
network: "robinhood",
|
|
343
|
+
// ❌ Won't find native-flow orders
|
|
344
|
+
const { orders } = await fetchLimitOrders({
|
|
345
|
+
network: "robinhood",
|
|
340
346
|
maker: "0xUserAddress", // only matches maker field
|
|
341
347
|
status: "ACTIVE",
|
|
342
348
|
});
|
|
343
349
|
```
|
|
344
350
|
|
|
345
|
-
### Cancelling a native-flow order
|
|
351
|
+
### Cancelling a native-flow order
|
|
346
352
|
|
|
347
|
-
Native-flow orders can be cancelled by the original creator (`recipient`):
|
|
353
|
+
Native-flow orders can be cancelled by the original creator (`recipient`):
|
|
348
354
|
|
|
349
355
|
```ts
|
|
350
|
-
const nativeFlow = new ethers.Contract(
|
|
351
|
-
getNativeFlowAddress("robinhood"),
|
|
352
|
-
PLS_FLOW_ABI,
|
|
353
|
-
signer,
|
|
354
|
-
);
|
|
356
|
+
const nativeFlow = new ethers.Contract(
|
|
357
|
+
getNativeFlowAddress("robinhood"),
|
|
358
|
+
PLS_FLOW_ABI,
|
|
359
|
+
signer,
|
|
360
|
+
);
|
|
355
361
|
|
|
356
362
|
// Cancel on-chain (only the original creator can call this)
|
|
357
|
-
const tx = await nativeFlow.cancelOrder(nonceToCancel);
|
|
363
|
+
const tx = await nativeFlow.cancelOrder(nonceToCancel);
|
|
358
364
|
await tx.wait();
|
|
359
365
|
|
|
360
366
|
// The backend will detect the NonceCancelled event and update the order status.
|
|
361
|
-
// No REST mutation is required.
|
|
367
|
+
// No REST mutation is required.
|
|
362
368
|
```
|
|
363
369
|
|
|
364
370
|
---
|
|
@@ -389,23 +395,23 @@ When `feeOnOutput=false`, the LO contract pulls all tokens to itself first — o
|
|
|
389
395
|
|
|
390
396
|
When `feeOnOutput=true`, the Router pulls tokens directly from the maker. If an operator also tries to take excess input via the LO contract, that requires a second approval the maker typically hasn't granted — the transaction would simply revert. The operator loses gas; the maker loses nothing and the order remains active for another operator. In practice, operators use output-side profit for `feeOnOutput=true` orders.
|
|
391
397
|
|
|
392
|
-
> **Impact on fillability:** `feeOnOutput=true` orders are still fully fillable, but operators have less flexibility. In tight-margin or low-liquidity situations, this *could* reduce the likelihood of a fill since operators cannot optimize their profit-taking strategy on both sides.
|
|
393
|
-
|
|
394
|
-
### Protected settlement
|
|
395
|
-
|
|
396
|
-
The current PulseChain and Robinhood deployments charge a 30 bps (0.30%)
|
|
397
|
-
limit-order fee. They are exempt from the Router's separate 10 bps regular-swap
|
|
398
|
-
fee, so the two fees are not stacked.
|
|
399
|
-
|
|
400
|
-
For routed fills, operator-retained surplus is capped at 5%. Input-side profit
|
|
401
|
-
is capped at 5% of executable input; output-side profit is capped at 5% of the
|
|
402
|
-
signed `minAmountOut`. Any additional routing improvement is returned to the
|
|
403
|
-
maker.
|
|
404
|
-
|
|
405
|
-
Direct fills are protected by the configured `directFillQuoter`. The maker must
|
|
406
|
-
receive at least the greater of the signed `minAmountOut` and 95% of the best
|
|
407
|
-
current direct-adapter quote. A missing or zero protected quote fails closed,
|
|
408
|
-
and any post-fee output sent above the protected floor belongs to the maker.
|
|
398
|
+
> **Impact on fillability:** `feeOnOutput=true` orders are still fully fillable, but operators have less flexibility. In tight-margin or low-liquidity situations, this *could* reduce the likelihood of a fill since operators cannot optimize their profit-taking strategy on both sides.
|
|
399
|
+
|
|
400
|
+
### Protected settlement
|
|
401
|
+
|
|
402
|
+
The current PulseChain and Robinhood deployments charge a 30 bps (0.30%)
|
|
403
|
+
limit-order fee. They are exempt from the Router's separate 10 bps regular-swap
|
|
404
|
+
fee, so the two fees are not stacked.
|
|
405
|
+
|
|
406
|
+
For routed fills, operator-retained surplus is capped at 5%. Input-side profit
|
|
407
|
+
is capped at 5% of executable input; output-side profit is capped at 5% of the
|
|
408
|
+
signed `minAmountOut`. Any additional routing improvement is returned to the
|
|
409
|
+
maker.
|
|
410
|
+
|
|
411
|
+
Direct fills are protected by the configured `directFillQuoter`. The maker must
|
|
412
|
+
receive at least the greater of the signed `minAmountOut` and 95% of the best
|
|
413
|
+
current direct-adapter quote. A missing or zero protected quote fails closed,
|
|
414
|
+
and any post-fee output sent above the protected floor belongs to the maker.
|
|
409
415
|
|
|
410
416
|
### Tax token decision guide
|
|
411
417
|
|
|
@@ -414,7 +420,7 @@ and any post-fee output sent above the protected floor belongs to the maker.
|
|
|
414
420
|
| **Neither token is taxed** | `false` (default) | Maximum operator flexibility → best chance of fill |
|
|
415
421
|
| **Input token is taxed** | `true` | Router sends tokens directly from maker → pool in one transfer (one tax). Default mode would do maker → LO → pool (two transfers, two taxes). |
|
|
416
422
|
| **Output token is taxed** | `false` (default) | Output goes directly to recipient in one transfer. |
|
|
417
|
-
| **Both tokens taxed** | `false` | Supported through tax-safe routes; fee on input avoids another output-token transfer. |
|
|
423
|
+
| **Both tokens taxed** | `false` | Supported through tax-safe routes; fee on input avoids another output-token transfer. |
|
|
418
424
|
|
|
419
425
|
> **Recommendation:** Use `feeOnOutput: false` (the default) unless the input token has a transfer tax. This gives operators the most flexibility and the best chance of filling your order.
|
|
420
426
|
|
|
@@ -474,18 +480,18 @@ const stats = await fetchLimitOrderStats();
|
|
|
474
480
|
|
|
475
481
|
## Cancelling a Limit Order
|
|
476
482
|
|
|
477
|
-
Cancellation is an **on-chain operation**:
|
|
483
|
+
Cancellation is an **on-chain operation**:
|
|
478
484
|
|
|
479
485
|
1. **On-chain:** Call `invalidateNonce(nonce)` on the **order's** SwitchLimitOrder contract. This is the authoritative cancellation — it prevents any operator from executing the order even if the backend hasn't been notified yet.
|
|
480
486
|
|
|
481
487
|
> **⚠️** Each order includes a `limitOrderContract` field. Always use that address — do not hardcode a single contract constant, as the contract may be redeployed across versions.
|
|
482
488
|
|
|
483
|
-
The backend indexer observes `NonceCancelled` and updates the orderbook. No
|
|
484
|
-
separate REST mutation is required.
|
|
489
|
+
The backend indexer observes `NonceCancelled` and updates the orderbook. No
|
|
490
|
+
separate REST mutation is required.
|
|
485
491
|
|
|
486
492
|
```ts
|
|
487
493
|
import { ethers } from "ethers";
|
|
488
|
-
import { LIMIT_ORDER_ABI } from "@switch-win/sdk/constants";
|
|
494
|
+
import { LIMIT_ORDER_ABI } from "@switch-win/sdk/constants";
|
|
489
495
|
|
|
490
496
|
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
|
|
491
497
|
const maker = await signer.getAddress();
|
|
@@ -497,7 +503,7 @@ const contract = new ethers.Contract(order.limitOrderContract, LIMIT_ORDER_ABI,
|
|
|
497
503
|
const tx = await contract.invalidateNonce(nonceToCancel);
|
|
498
504
|
await tx.wait();
|
|
499
505
|
|
|
500
|
-
// The backend indexer observes the event and marks the order CANCELLED.
|
|
506
|
+
// The backend indexer observes the event and marks the order CANCELLED.
|
|
501
507
|
```
|
|
502
508
|
|
|
503
509
|
To cancel **multiple orders** at once, use `invalidateNonces(uint256[])`:
|
|
@@ -507,7 +513,7 @@ const nonces = [1717171717, 1717171718, 1717171719];
|
|
|
507
513
|
const tx = await contract.invalidateNonces(nonces);
|
|
508
514
|
await tx.wait();
|
|
509
515
|
|
|
510
|
-
// The backend indexer observes each NonceCancelled event.
|
|
516
|
+
// The backend indexer observes each NonceCancelled event.
|
|
511
517
|
```
|
|
512
518
|
|
|
513
519
|
---
|
|
@@ -536,9 +542,11 @@ Submit a signed limit order. The backend verifies the EIP-712 signature before s
|
|
|
536
542
|
> Your integration must:
|
|
537
543
|
> 1. Call `submitLimitOrder()` **immediately** after the user signs.
|
|
538
544
|
> 2. **Await** the response and confirm `success: true` before showing "order created".
|
|
539
|
-
> 3.
|
|
545
|
+
> 3. On a transient failure, look up that exact maker + nonce and verify its
|
|
546
|
+
> contract and signed fields. Duplicates return an error, not idempotent
|
|
547
|
+
> success. Retry only the original signed payload; never generate a new nonce.
|
|
540
548
|
>
|
|
541
|
-
> Native PLS/ETH flow orders are the exception — they are recorded on-chain first,
|
|
549
|
+
> Native PLS/ETH flow orders are the exception — they are recorded on-chain first,
|
|
542
550
|
> so the backend discovers them via event indexing even if the POST never arrives.
|
|
543
551
|
|
|
544
552
|
**Request body:**
|
|
@@ -553,11 +561,11 @@ Submit a signed limit order. The backend verifies the EIP-712 signature before s
|
|
|
553
561
|
"deadline": 1717257600,
|
|
554
562
|
"nonce": 1717171717,
|
|
555
563
|
"feeOnOutput": false,
|
|
556
|
-
"recipient": "0x...",
|
|
557
|
-
"unwrapOutput": false,
|
|
558
|
-
"limitOrderContract": "0x...",
|
|
559
|
-
"signature": "0x..."
|
|
560
|
-
}
|
|
564
|
+
"recipient": "0x...",
|
|
565
|
+
"unwrapOutput": false,
|
|
566
|
+
"limitOrderContract": "0x...",
|
|
567
|
+
"signature": "0x..."
|
|
568
|
+
}
|
|
561
569
|
```
|
|
562
570
|
|
|
563
571
|
**Success response (201):**
|
|
@@ -599,7 +607,7 @@ Submit a signed limit order. The backend verifies the EIP-712 signature before s
|
|
|
599
607
|
| `"Signature does not match maker"` | The recovered signer doesn't match the `maker` field |
|
|
600
608
|
| `"Nonce N already used for maker 0x..."` | This nonce has already been used (filled or submitted) |
|
|
601
609
|
|
|
602
|
-
### `GET /limit-orders`
|
|
610
|
+
### `GET /limit-orders`
|
|
603
611
|
|
|
604
612
|
List orders with optional query filters. See [Query Filters](#query-filters) above.
|
|
605
613
|
|
|
@@ -669,13 +677,13 @@ Summary statistics.
|
|
|
669
677
|
| `LimitOrderParams` | Order fields for EIP-712 signing |
|
|
670
678
|
| `SignedLimitOrder` | `LimitOrderParams` + `signature` |
|
|
671
679
|
| `CreateLimitOrderRequest` | Alias for `SignedLimitOrder` (POST body) |
|
|
672
|
-
| `LimitOrderStatus` | `"ACTIVE" \| "FILLED" \| "CANCELLED" \| "EXPIRED"` |
|
|
680
|
+
| `LimitOrderStatus` | `"ACTIVE" \| "FILLED" \| "CANCELLED" \| "EXPIRED"` |
|
|
673
681
|
| `LimitOrderRecord` | Full order record from the API (includes `partnerAddress`, `limitOrderContract`, status, and timestamps) |
|
|
674
682
|
| `CreateLimitOrderResponse` | `{ success: true, order: LimitOrderRecord }` |
|
|
675
683
|
| `ListLimitOrdersResponse` | `{ total, limit, offset, orders: LimitOrderRecord[] }` |
|
|
676
684
|
| `LimitOrderPair` | Active pair with order count |
|
|
677
685
|
| `LimitOrderStats` | Global order statistics |
|
|
678
|
-
| `LimitOrderMutationResponse` | Submission response + `ErrorResponse` |
|
|
686
|
+
| `LimitOrderMutationResponse` | Submission response + `ErrorResponse` |
|
|
679
687
|
|
|
680
688
|
---
|
|
681
689
|
|
|
@@ -686,23 +694,23 @@ Available from `@switch-win/sdk/limit-orders`:
|
|
|
686
694
|
| Function | Description |
|
|
687
695
|
|---|---|
|
|
688
696
|
| `buildLimitOrder(options)` | Build a `LimitOrderParams` object with sensible defaults |
|
|
689
|
-
| `getEIP712SigningParams()` | Get the `{ domain, types }` for `signTypedData()` |
|
|
690
|
-
| `getNetworkEIP712SigningParams(network, contract?)` | Build the correct PulseChain or Robinhood EIP-712 domain |
|
|
691
|
-
| `getLimitOrderNetworkConfig(network)` | Get static chain, router, LO, wrapped-native, and native-flow defaults |
|
|
692
|
-
| `getLimitOrderApprovalTarget(network, feeOnOutput, overrides?)` | Resolve the correct maker approval target |
|
|
697
|
+
| `getEIP712SigningParams()` | Get the `{ domain, types }` for `signTypedData()` |
|
|
698
|
+
| `getNetworkEIP712SigningParams(network, contract?)` | Build the correct PulseChain or Robinhood EIP-712 domain |
|
|
699
|
+
| `getLimitOrderNetworkConfig(network)` | Get static chain, router, LO, wrapped-native, and native-flow defaults |
|
|
700
|
+
| `getLimitOrderApprovalTarget(network, feeOnOutput, overrides?)` | Resolve the correct maker approval target |
|
|
693
701
|
| `getApprovalTarget()` | Get approval target for `feeOnOutput: false` orders → SwitchLimitOrder |
|
|
694
702
|
| `getRouterApprovalTarget()` | Get approval target for `feeOnOutput: true` orders → SwitchRouter |
|
|
695
703
|
| `shouldUnwrapOutput(tokenOut)` | Returns `true` if tokenOut is WPLS (should set `unwrapOutput: true`) |
|
|
696
|
-
| `getPLSFlowAddress()` | Get the SwitchPLSFlow contract address for native PLS limit orders |
|
|
697
|
-
| `getNativeFlowAddress(network)` | Get the native PLS/ETH flow address for either chain |
|
|
698
|
-
| `isNativePLS(tokenIn)` | Returns `true` if tokenIn is native PLS (use PLSFlow instead of EIP-712) |
|
|
699
|
-
| `isNativeCurrency(tokenIn)` | Network-neutral native sentinel check |
|
|
704
|
+
| `getPLSFlowAddress()` | Get the SwitchPLSFlow contract address for native PLS limit orders |
|
|
705
|
+
| `getNativeFlowAddress(network)` | Get the native PLS/ETH flow address for either chain |
|
|
706
|
+
| `isNativePLS(tokenIn)` | Returns `true` if tokenIn is native PLS (use PLSFlow instead of EIP-712) |
|
|
707
|
+
| `isNativeCurrency(tokenIn)` | Network-neutral native sentinel check |
|
|
700
708
|
| `submitLimitOrder(signedOrder)` | POST a signed order to the Switch backend |
|
|
701
709
|
| `fetchLimitOrders(options?)` | GET orders with optional filters |
|
|
702
710
|
| `fetchLimitOrder(maker, nonce)` | GET a single order by maker + nonce |
|
|
703
|
-
| `fetchLimitOrderPairs()` | GET active pairs with order counts |
|
|
704
|
-
| `fetchLimitOrderConfig({ network })` | GET live deployments and EIP-712 domain |
|
|
705
|
-
| `fetchLimitOrderStats()` | GET global order statistics |
|
|
711
|
+
| `fetchLimitOrderPairs()` | GET active pairs with order counts |
|
|
712
|
+
| `fetchLimitOrderConfig({ network })` | GET live deployments and EIP-712 domain |
|
|
713
|
+
| `fetchLimitOrderStats()` | GET global order statistics |
|
|
706
714
|
|
|
707
715
|
---
|
|
708
716
|
|
|
@@ -729,12 +737,12 @@ The EIP-712 domain and types must match the on-chain contract exactly:
|
|
|
729
737
|
{ name: "minAmountOut", type: "uint256" },
|
|
730
738
|
{ name: "deadline", type: "uint256" },
|
|
731
739
|
{ name: "nonce", type: "uint256" },
|
|
732
|
-
{ name: "feeOnOutput", type: "bool" },
|
|
733
|
-
{ name: "recipient", type: "address" },
|
|
734
|
-
{ name: "unwrapOutput", type: "bool" },
|
|
735
|
-
{ name: "partnerAddress", type: "address" },
|
|
736
|
-
]
|
|
737
|
-
}
|
|
740
|
+
{ name: "feeOnOutput", type: "bool" },
|
|
741
|
+
{ name: "recipient", type: "address" },
|
|
742
|
+
{ name: "unwrapOutput", type: "bool" },
|
|
743
|
+
{ name: "partnerAddress", type: "address" },
|
|
744
|
+
]
|
|
745
|
+
}
|
|
738
746
|
```
|
|
739
747
|
|
|
740
748
|
These are exported as `LIMIT_ORDER_EIP712_DOMAIN` and `LIMIT_ORDER_EIP712_TYPES` from `@switch-win/sdk/constants`.
|
|
@@ -752,16 +760,16 @@ These are exported as `LIMIT_ORDER_EIP712_DOMAIN` and `LIMIT_ORDER_EIP712_TYPES`
|
|
|
752
760
|
| `InvalidTokens()` | tokenIn and tokenOut are the same, or zero address |
|
|
753
761
|
| `NonceAlreadyUsed()` | This nonce has already been filled or invalidated |
|
|
754
762
|
| `OrderExpired()` | `block.timestamp > deadline` (and deadline > 0) |
|
|
755
|
-
| `RouteInputExceedsMax()` | Route's total input exceeds the order's `amountIn` |
|
|
756
|
-
| `NativeFlowInputNotFullyConsumed()` | Native-flow output-surplus route did not consume all executable input |
|
|
757
|
-
| `RouteTokenInMismatch()` | A route does not start with the signed input token |
|
|
758
|
-
| `RouteTokenOutMismatch()` | A route does not end with the signed output token |
|
|
759
|
-
| `DirectFillQuoteUnavailable()` | The protected direct-fill quote is missing or zero |
|
|
760
|
-
| `DirectFillPriceTooLow()` | The maker would receive less than the protected direct-fill floor |
|
|
761
|
-
| `TransferFailed()` | Token transfer failed (insufficient balance or allowance) |
|
|
763
|
+
| `RouteInputExceedsMax()` | Route's total input exceeds the order's `amountIn` |
|
|
764
|
+
| `NativeFlowInputNotFullyConsumed()` | Native-flow output-surplus route did not consume all executable input |
|
|
765
|
+
| `RouteTokenInMismatch()` | A route does not start with the signed input token |
|
|
766
|
+
| `RouteTokenOutMismatch()` | A route does not end with the signed output token |
|
|
767
|
+
| `DirectFillQuoteUnavailable()` | The protected direct-fill quote is missing or zero |
|
|
768
|
+
| `DirectFillPriceTooLow()` | The maker would receive less than the protected direct-fill floor |
|
|
769
|
+
| `TransferFailed()` | Token transfer failed (insufficient balance or allowance) |
|
|
762
770
|
|
|
763
771
|
---
|
|
764
772
|
|
|
765
773
|
*See also: [README.md](README.md) for swap API docs, constants, and general integration info.*
|
|
766
774
|
|
|
767
|
-
*Last updated: August 2026*
|
|
775
|
+
*Last updated: August 2026*
|