@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/dist/types.d.ts
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Switch DEX Aggregator -- API Response Types
|
|
3
|
+
*
|
|
4
|
+
* TypeScript interfaces for all objects returned by the /swap/quote endpoint.
|
|
5
|
+
* Import these to get full type safety when integrating with the Switch API.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import type { BestPathResponse } from "@switch-win/sdk/types";
|
|
10
|
+
*
|
|
11
|
+
* const res = await fetch("https://quote.switch.win/swap/quote?...");
|
|
12
|
+
* const quote: BestPathResponse = await res.json();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
/** Full response from `GET /swap/quote` */
|
|
16
|
+
export interface BestPathResponse {
|
|
17
|
+
/** Input token address */
|
|
18
|
+
fromToken: string;
|
|
19
|
+
/** Output token address */
|
|
20
|
+
toToken: string;
|
|
21
|
+
/** Recipient address for output tokens. Same as `sender` unless a custom `receiver` was specified. */
|
|
22
|
+
receiver?: string;
|
|
23
|
+
/** Total input amount in wei */
|
|
24
|
+
totalAmountIn: string;
|
|
25
|
+
/**
|
|
26
|
+
* Quoted route output in wei before the Switch protocol fee and slippage.
|
|
27
|
+
* Adapter-native quotes may already include token-tax effects (for example,
|
|
28
|
+
* the Flap Portal quote).
|
|
29
|
+
*/
|
|
30
|
+
totalAmountOut: string;
|
|
31
|
+
/**
|
|
32
|
+
* Expected output the user will actually receive, in wei.
|
|
33
|
+
* Accounts for sell tax, protocol fee, and buy tax -- but NOT slippage.
|
|
34
|
+
*
|
|
35
|
+
* Use this for UI display of the estimated received amount. For tax tokens,
|
|
36
|
+
* this is significantly more accurate than applying taxes as linear
|
|
37
|
+
* multipliers to `totalAmountOut`.
|
|
38
|
+
*/
|
|
39
|
+
expectedOutputAmount: string;
|
|
40
|
+
/**
|
|
41
|
+
* Minimum acceptable output after taxes, fee, and slippage.
|
|
42
|
+
*
|
|
43
|
+
* Tax accounting is adapter-specific. Use this API value directly rather
|
|
44
|
+
* than recomputing it from the reported tax rates. This is encoded in the
|
|
45
|
+
* `tx` calldata as `_minTotalAmountOut`.
|
|
46
|
+
*/
|
|
47
|
+
minAmountOut: string;
|
|
48
|
+
/** Human-readable path descriptions */
|
|
49
|
+
paths: SwapPath[];
|
|
50
|
+
/** Human-readable structured route breakdown for display and analytics. */
|
|
51
|
+
routeAllocation?: RouteAllocationPlan;
|
|
52
|
+
/** Ready-to-send transaction with fee on **input** (default). Only present when `sender` query param is provided. */
|
|
53
|
+
tx?: SwapTransaction;
|
|
54
|
+
/**
|
|
55
|
+
* Ready-to-send transaction with fee on **output**.
|
|
56
|
+
* Only present when `sender` query param is provided.
|
|
57
|
+
*
|
|
58
|
+
* Use this variant when fee-on-output is preferred (e.g. to collect fees in
|
|
59
|
+
* the output token). See the README for recommended strategies.
|
|
60
|
+
*/
|
|
61
|
+
txFeeOnOutput?: SwapTransaction;
|
|
62
|
+
/** Transaction matching the requested fee mode. Never broadcast this for a gasless order. */
|
|
63
|
+
selectedTx?: SwapTransaction;
|
|
64
|
+
protocolFeeBps?: number;
|
|
65
|
+
feeOnOutput?: boolean;
|
|
66
|
+
quoteGasPrice?: string;
|
|
67
|
+
quoteBlockNumber?: number;
|
|
68
|
+
gaslessExecution?: import("./gasless-types.js").GaslessPreviewResult;
|
|
69
|
+
/** Conservative server recommendation; normal quotes report not_checked. */
|
|
70
|
+
gaslessEligibility?: import("./gasless-types.js").GaslessEligibility;
|
|
71
|
+
/**
|
|
72
|
+
* Transfer tax info for the input token.
|
|
73
|
+
* Present when the API detects whether the token has a fee-on-transfer mechanism.
|
|
74
|
+
*/
|
|
75
|
+
fromTokenTax?: TokenTaxResponse;
|
|
76
|
+
/**
|
|
77
|
+
* Transfer tax info for the output token.
|
|
78
|
+
* Present when the API detects whether the token has a fee-on-transfer mechanism.
|
|
79
|
+
* If `toTokenTax.isTaxToken` is true, `minAmountOut` already accounts for the buy tax.
|
|
80
|
+
*/
|
|
81
|
+
toTokenTax?: TokenTaxResponse;
|
|
82
|
+
/**
|
|
83
|
+
* Effective slippage in basis points = user slippage + sell tax + buy tax + auto-liq buffer.
|
|
84
|
+
*
|
|
85
|
+
* For example, if a user requests 50 bps (0.5%) slippage but the input token has
|
|
86
|
+
* a 120 bps (1.2%) sell tax, effectiveSlippageBps = 170.
|
|
87
|
+
*
|
|
88
|
+
* Display this in your UI so users understand the total tolerance applied.
|
|
89
|
+
*/
|
|
90
|
+
effectiveSlippageBps: number;
|
|
91
|
+
/**
|
|
92
|
+
* Human-readable effective slippage percentage.
|
|
93
|
+
*
|
|
94
|
+
* Examples: `"0.5"` (no tax), `"1.7"` (0.5% user + 1.2% sell tax), `"4.5"` (0.5% + 2% sell + 2% buy).
|
|
95
|
+
*/
|
|
96
|
+
effectiveSlippagePercent: string;
|
|
97
|
+
}
|
|
98
|
+
/** Transfer tax information for a token (fee-on-transfer) */
|
|
99
|
+
export interface TokenTaxResponse {
|
|
100
|
+
/** Whether this token has a transfer tax */
|
|
101
|
+
isTaxToken: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Buy tax in basis points.
|
|
104
|
+
* Applied when the token is acquired (i.e. when it is the output token).
|
|
105
|
+
* For example, 500 = 5% buy tax.
|
|
106
|
+
*/
|
|
107
|
+
buyTaxBps: number;
|
|
108
|
+
/**
|
|
109
|
+
* Sell tax in basis points.
|
|
110
|
+
* Applied when the token is sold (i.e. when it is the input token).
|
|
111
|
+
* For example, 500 = 5% sell tax.
|
|
112
|
+
*/
|
|
113
|
+
sellTaxBps: number;
|
|
114
|
+
}
|
|
115
|
+
/** Transaction object ready to be sent on-chain via `signer.sendTransaction()` */
|
|
116
|
+
export interface SwapTransaction {
|
|
117
|
+
/** SwitchRouter contract address */
|
|
118
|
+
to: string;
|
|
119
|
+
/** ABI-encoded `goSwitch()` calldata */
|
|
120
|
+
data: string;
|
|
121
|
+
/** Native currency to send (wei). `"0"` for ERC-20 input tokens. */
|
|
122
|
+
value: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Human-readable summary of a single route split.
|
|
126
|
+
* Useful for UI display (e.g. "60% via PulseX, 40% via 9inch").
|
|
127
|
+
*/
|
|
128
|
+
export interface SwapPath {
|
|
129
|
+
/** Human-readable DEX adapter name (e.g. `"PulseXV2"`, `"UniswapV3"`, `"9inchV2"`) */
|
|
130
|
+
adapter: string;
|
|
131
|
+
/** Input amount for this path (wei) */
|
|
132
|
+
amountIn: string;
|
|
133
|
+
/** Expected output for this path (wei) */
|
|
134
|
+
amountOut: string;
|
|
135
|
+
/** Ordered list of token addresses traversed (e.g. `[tokenIn, intermediate, tokenOut]`) */
|
|
136
|
+
path: string[];
|
|
137
|
+
/** Human-readable adapter names used at each hop */
|
|
138
|
+
adapters: string[];
|
|
139
|
+
/** Percentage of total input routed through this path */
|
|
140
|
+
percentage?: number;
|
|
141
|
+
/** Detailed breakdown of each hop-leg in this path */
|
|
142
|
+
legs?: SwapPathLeg[];
|
|
143
|
+
}
|
|
144
|
+
/** A single leg within a swap path */
|
|
145
|
+
export interface SwapPathLeg {
|
|
146
|
+
/** Leg input token */
|
|
147
|
+
tokenIn: string;
|
|
148
|
+
/** Leg output token */
|
|
149
|
+
tokenOut: string;
|
|
150
|
+
/** Human-readable DEX adapter name */
|
|
151
|
+
adapter?: string;
|
|
152
|
+
/** Input amount (wei) */
|
|
153
|
+
amountIn: string;
|
|
154
|
+
/** Output amount (wei) */
|
|
155
|
+
amountOut: string;
|
|
156
|
+
/** Percentage of the hop routed through this adapter */
|
|
157
|
+
percentage?: number;
|
|
158
|
+
}
|
|
159
|
+
/** Top-level route allocation plan */
|
|
160
|
+
export interface RouteAllocationPlan {
|
|
161
|
+
/** Total input in wei */
|
|
162
|
+
amountIn: string;
|
|
163
|
+
/** Total expected output in wei */
|
|
164
|
+
totalAmountOut: string;
|
|
165
|
+
/** Array of split routes */
|
|
166
|
+
routes: SingleRouteAllocation[];
|
|
167
|
+
}
|
|
168
|
+
/** A single route within the split */
|
|
169
|
+
export interface SingleRouteAllocation {
|
|
170
|
+
/** Input amount for this route portion (wei) */
|
|
171
|
+
amountIn: string;
|
|
172
|
+
/** Sequential hops in this route */
|
|
173
|
+
hops: HopAllocationPlan[];
|
|
174
|
+
}
|
|
175
|
+
/** A single hop within a route */
|
|
176
|
+
export interface HopAllocationPlan {
|
|
177
|
+
/** Hop input token */
|
|
178
|
+
tokenIn: string;
|
|
179
|
+
/** Hop output token */
|
|
180
|
+
tokenOut: string;
|
|
181
|
+
/** Total output from all legs in this hop (wei) */
|
|
182
|
+
totalAmountOut: string;
|
|
183
|
+
/** Adapter legs executing this hop (can be split across DEXes) */
|
|
184
|
+
legs: HopAdapterAllocationPlan[];
|
|
185
|
+
}
|
|
186
|
+
/** A single adapter leg within a hop */
|
|
187
|
+
export interface HopAdapterAllocationPlan {
|
|
188
|
+
/** Human-readable DEX adapter name */
|
|
189
|
+
adapter: string;
|
|
190
|
+
/** Input amount routed through this adapter (wei) */
|
|
191
|
+
amountIn: string;
|
|
192
|
+
/** Expected output from this adapter (wei) */
|
|
193
|
+
amountOut: string;
|
|
194
|
+
/** V3 fee tier or concentrated-liquidity tick-spacing hint. Zero for adapters that do not use it. */
|
|
195
|
+
fee?: number;
|
|
196
|
+
}
|
|
197
|
+
/** Error response returned when the API encounters a problem */
|
|
198
|
+
export interface ErrorResponse {
|
|
199
|
+
error: string;
|
|
200
|
+
}
|
|
201
|
+
/** The quote endpoint returns either a successful response or an error */
|
|
202
|
+
export type QuoteResponse = BestPathResponse | ErrorResponse;
|
|
203
|
+
/** Type guard to check if a response is an error */
|
|
204
|
+
export declare function isErrorResponse(response: QuoteResponse): response is ErrorResponse;
|
|
205
|
+
/** A single adapter (DEX) returned by `GET /swap/adapters` */
|
|
206
|
+
export interface AdapterInfo {
|
|
207
|
+
/** On-chain adapter index (used in the `adapters` query param for `/swap/quote`) */
|
|
208
|
+
index: number;
|
|
209
|
+
/** Human-readable DEX name (e.g. `"PulseXV2"`, `"UniswapV3"`, `"9inchV2"`) */
|
|
210
|
+
name: string;
|
|
211
|
+
/** On-chain adapter contract address */
|
|
212
|
+
address: string;
|
|
213
|
+
}
|
|
214
|
+
/** Response from `GET /swap/adapters` */
|
|
215
|
+
export interface AdaptersResponse {
|
|
216
|
+
adapters: AdapterInfo[];
|
|
217
|
+
}
|
|
218
|
+
/** Response from `GET /swap/checkTax` */
|
|
219
|
+
export interface CheckTaxResponse {
|
|
220
|
+
/** Lowercased token address that was checked */
|
|
221
|
+
token: string;
|
|
222
|
+
/** Whether this token has a non-zero transfer tax */
|
|
223
|
+
isTaxToken: boolean;
|
|
224
|
+
/** Buy tax in basis points (applied when the token is the output) */
|
|
225
|
+
buyTaxBps: number;
|
|
226
|
+
/** Sell tax in basis points (applied when the token is the input) */
|
|
227
|
+
sellTaxBps: number;
|
|
228
|
+
}
|
|
229
|
+
/** Type guard for CheckTaxResponse errors */
|
|
230
|
+
export type CheckTaxResult = CheckTaxResponse | ErrorResponse;
|
|
231
|
+
/**
|
|
232
|
+
* Parameters for a limit order — the fields the maker signs via EIP-712.
|
|
233
|
+
*
|
|
234
|
+
* All amounts are raw integer strings in wei (same convention as swap quotes).
|
|
235
|
+
* The `nonce` MUST be unique per maker — once used (filled or cancelled) it
|
|
236
|
+
* cannot be reused.
|
|
237
|
+
*/
|
|
238
|
+
export interface LimitOrderParams {
|
|
239
|
+
/** Maker (signer) address */
|
|
240
|
+
maker: string;
|
|
241
|
+
/** Input token address (token the maker is selling) */
|
|
242
|
+
tokenIn: string;
|
|
243
|
+
/** Output token address (token the maker wants to receive) */
|
|
244
|
+
tokenOut: string;
|
|
245
|
+
/** Amount of tokenIn the maker is offering (wei) */
|
|
246
|
+
amountIn: string;
|
|
247
|
+
/** Minimum amount of tokenOut the maker will accept (wei) */
|
|
248
|
+
minAmountOut: string;
|
|
249
|
+
/**
|
|
250
|
+
* Unix timestamp after which the order expires. Set to `0` for no expiry.
|
|
251
|
+
* The contract will revert with `OrderExpired()` if `block.timestamp > deadline` (when > 0).
|
|
252
|
+
*/
|
|
253
|
+
deadline: number;
|
|
254
|
+
/**
|
|
255
|
+
* Unique nonce per maker. Once a nonce is used (filled or cancelled on-chain),
|
|
256
|
+
* it cannot be reused. Use `Date.now()` or a random integer for uniqueness.
|
|
257
|
+
*/
|
|
258
|
+
nonce: number;
|
|
259
|
+
/**
|
|
260
|
+
* If `true`, the contract fee is deducted from the output (tokenOut) instead
|
|
261
|
+
* of the input (tokenIn). Same concept as swap fee modes.
|
|
262
|
+
*/
|
|
263
|
+
feeOnOutput: boolean;
|
|
264
|
+
/**
|
|
265
|
+
* Address that receives the output tokens. Defaults to the maker if set
|
|
266
|
+
* to the zero address (`0x0000…`).
|
|
267
|
+
*/
|
|
268
|
+
recipient: string;
|
|
269
|
+
/**
|
|
270
|
+
* If `true` and tokenOut is WPLS, the contract unwraps to native PLS
|
|
271
|
+
* before sending to the recipient.
|
|
272
|
+
*/
|
|
273
|
+
unwrapOutput: boolean;
|
|
274
|
+
/**
|
|
275
|
+
* Partner address to receive 50% of protocol fees.
|
|
276
|
+
* Use the zero address (`0x0000…`) for no partner.
|
|
277
|
+
*/
|
|
278
|
+
partnerAddress: string;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* A signed limit order — the order parameters plus the EIP-712 signature.
|
|
282
|
+
* This is what gets submitted to the Switch backend via `POST /limit-orders`.
|
|
283
|
+
*/
|
|
284
|
+
export interface SignedLimitOrder extends LimitOrderParams {
|
|
285
|
+
/** EIP-712 signature of the order struct, produced by the maker's wallet */
|
|
286
|
+
signature: string;
|
|
287
|
+
/**
|
|
288
|
+
* SwitchLimitOrder deployment used in the EIP-712 domain.
|
|
289
|
+
* Include this when submitting Robinhood orders or orders signed against an
|
|
290
|
+
* older supported deployment so the backend verifies the correct domain.
|
|
291
|
+
*/
|
|
292
|
+
limitOrderContract?: string;
|
|
293
|
+
}
|
|
294
|
+
/** Body for `POST /limit-orders` */
|
|
295
|
+
export type CreateLimitOrderRequest = SignedLimitOrder;
|
|
296
|
+
/** @deprecated Current hosted API cancellation is on-chain only. */
|
|
297
|
+
export interface CancelLimitOrderRequest {
|
|
298
|
+
/** Maker address */
|
|
299
|
+
maker: string;
|
|
300
|
+
/** Nonce of the order to cancel */
|
|
301
|
+
nonce: number;
|
|
302
|
+
}
|
|
303
|
+
/** Order status in the backend database */
|
|
304
|
+
export type LimitOrderStatus = "ACTIVE" | "FILLED" | "CANCELLED" | "EXPIRED";
|
|
305
|
+
/**
|
|
306
|
+
* A limit order record as returned by the Switch backend API.
|
|
307
|
+
*
|
|
308
|
+
* Includes all order parameters plus metadata fields added by the backend.
|
|
309
|
+
*/
|
|
310
|
+
export interface LimitOrderRecord {
|
|
311
|
+
/** Unique database ID (cuid) */
|
|
312
|
+
id: string;
|
|
313
|
+
/** Maker address (lowercased) */
|
|
314
|
+
maker: string;
|
|
315
|
+
/** Recipient address (lowercased) */
|
|
316
|
+
recipient: string;
|
|
317
|
+
/** Input token address (lowercased) */
|
|
318
|
+
tokenIn: string;
|
|
319
|
+
/** Output token address (lowercased) */
|
|
320
|
+
tokenOut: string;
|
|
321
|
+
/** Input amount in wei */
|
|
322
|
+
amountIn: string;
|
|
323
|
+
/** Minimum output amount in wei */
|
|
324
|
+
minAmountOut: string;
|
|
325
|
+
/** Expiry timestamp (0 = no expiry) */
|
|
326
|
+
deadline: number;
|
|
327
|
+
/** Unique nonce for this maker */
|
|
328
|
+
nonce: number;
|
|
329
|
+
/** Fee mode flag */
|
|
330
|
+
feeOnOutput: boolean;
|
|
331
|
+
/** Unwrap wrapped-native output to PLS/ETH on the selected network */
|
|
332
|
+
unwrapOutput: boolean;
|
|
333
|
+
/** Partner address for fee sharing */
|
|
334
|
+
partnerAddress: string;
|
|
335
|
+
/** LO contract address this order targets for cancellation/fill */
|
|
336
|
+
limitOrderContract?: string;
|
|
337
|
+
/** EIP-712 signature */
|
|
338
|
+
signature: string;
|
|
339
|
+
/** Pair key in format `tokenIn:tokenOut` (lowercased) */
|
|
340
|
+
pairKey: string;
|
|
341
|
+
/** Sell tax on input token in basis points, when known */
|
|
342
|
+
tokenInSellTaxBps?: number;
|
|
343
|
+
/** Buy tax on input token in basis points, when known */
|
|
344
|
+
tokenInBuyTaxBps?: number;
|
|
345
|
+
/** Sell tax on output token in basis points, when known */
|
|
346
|
+
tokenOutSellTaxBps?: number;
|
|
347
|
+
/** Buy tax on output token in basis points, when known */
|
|
348
|
+
tokenOutBuyTaxBps?: number;
|
|
349
|
+
/** Current order status */
|
|
350
|
+
status: LimitOrderStatus;
|
|
351
|
+
/** ISO 8601 creation timestamp */
|
|
352
|
+
createdAt: string;
|
|
353
|
+
/** ISO 8601 last update timestamp */
|
|
354
|
+
updatedAt: string;
|
|
355
|
+
/** Filler address for filled orders */
|
|
356
|
+
filler?: string | null;
|
|
357
|
+
/** Filler profit in tokenIn units for filled orders */
|
|
358
|
+
fillerProfit?: string | null;
|
|
359
|
+
/** Block number where the order was filled (if status is FILLED) */
|
|
360
|
+
filledBlock?: number | null;
|
|
361
|
+
/** Transaction hash of the fill (if status is FILLED) */
|
|
362
|
+
filledTxHash?: string | null;
|
|
363
|
+
}
|
|
364
|
+
/** Response from `POST /limit-orders` (success) */
|
|
365
|
+
export interface CreateLimitOrderResponse {
|
|
366
|
+
success: true;
|
|
367
|
+
order: LimitOrderRecord;
|
|
368
|
+
}
|
|
369
|
+
/** @deprecated Current hosted API cancellation is on-chain only. */
|
|
370
|
+
export interface CancelLimitOrderResponse {
|
|
371
|
+
success: true;
|
|
372
|
+
}
|
|
373
|
+
/** Response from `GET /limit-orders` */
|
|
374
|
+
export interface ListLimitOrdersResponse {
|
|
375
|
+
/** Total matching orders */
|
|
376
|
+
total: number;
|
|
377
|
+
/** Page size used */
|
|
378
|
+
limit: number;
|
|
379
|
+
/** Page offset used */
|
|
380
|
+
offset: number;
|
|
381
|
+
/** Array of order records */
|
|
382
|
+
orders: LimitOrderRecord[];
|
|
383
|
+
}
|
|
384
|
+
/** Response from `GET /limit-orders/pairs` */
|
|
385
|
+
export interface LimitOrderPair {
|
|
386
|
+
/** Pair key in format `tokenIn:tokenOut` */
|
|
387
|
+
pairKey: string;
|
|
388
|
+
/** Input token address */
|
|
389
|
+
tokenIn: string;
|
|
390
|
+
/** Output token address */
|
|
391
|
+
tokenOut: string;
|
|
392
|
+
/** Number of active orders for this pair */
|
|
393
|
+
activeOrders: number;
|
|
394
|
+
}
|
|
395
|
+
/** Response from `GET /limit-orders/stats` */
|
|
396
|
+
export interface LimitOrderStats {
|
|
397
|
+
active: number;
|
|
398
|
+
filled: number;
|
|
399
|
+
cancelled: number;
|
|
400
|
+
expired: number;
|
|
401
|
+
total: number;
|
|
402
|
+
}
|
|
403
|
+
/** Response from `GET /limit-orders/config`. */
|
|
404
|
+
export interface LimitOrderConfigResponse {
|
|
405
|
+
limitOrderContract: string;
|
|
406
|
+
limitOrderRouter?: string | null;
|
|
407
|
+
gaslessContract?: string | null;
|
|
408
|
+
gaslessSettlementVersion?: number;
|
|
409
|
+
gaslessTaxTokensSupported?: boolean;
|
|
410
|
+
gaslessApprovalSpender?: string | null;
|
|
411
|
+
gaslessProtocolFeeBps?: number;
|
|
412
|
+
gaslessMaxOperatorPayoutBps?: number;
|
|
413
|
+
gaslessEip712Domain?: {
|
|
414
|
+
name: string;
|
|
415
|
+
version: string;
|
|
416
|
+
chainId: number;
|
|
417
|
+
verifyingContract: string;
|
|
418
|
+
} | null;
|
|
419
|
+
orderSubmissionLimitOrderContracts?: string[];
|
|
420
|
+
otcRoutingLimitOrderContracts?: string[];
|
|
421
|
+
deprecatedLimitOrderContracts?: string[];
|
|
422
|
+
allLimitOrderContracts: string[];
|
|
423
|
+
plsFlowContract: string | null;
|
|
424
|
+
allPlsFlowContracts: string[];
|
|
425
|
+
limitOrderContractVersions: Record<string, string>;
|
|
426
|
+
plsFlowContractVersions: Record<string, string>;
|
|
427
|
+
eip712Domain: {
|
|
428
|
+
name: "SwitchLimitOrder";
|
|
429
|
+
version: "2";
|
|
430
|
+
chainId: number;
|
|
431
|
+
verifyingContract: string;
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/** Union type for limit order mutation responses */
|
|
435
|
+
export type LimitOrderMutationResponse = CreateLimitOrderResponse | CancelLimitOrderResponse | ErrorResponse;
|
|
436
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Switch DEX Aggregator -- API Response Types
|
|
3
|
+
*
|
|
4
|
+
* TypeScript interfaces for all objects returned by the /swap/quote endpoint.
|
|
5
|
+
* Import these to get full type safety when integrating with the Switch API.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import type { BestPathResponse } from "@switch-win/sdk/types";
|
|
10
|
+
*
|
|
11
|
+
* const res = await fetch("https://quote.switch.win/swap/quote?...");
|
|
12
|
+
* const quote: BestPathResponse = await res.json();
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
/** Type guard to check if a response is an error */
|
|
16
|
+
export function isErrorResponse(response) {
|
|
17
|
+
return "error" in response;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAmNH,oDAAoD;AACpD,MAAM,UAAU,eAAe,CAC7B,QAAuB;IAEvB,OAAO,OAAO,IAAI,QAAQ,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** app/api/switch/[...path]/route.ts. Use your normal origin/session checks and rate limits. */
|
|
2
|
+
const READ_PATHS = new Set([
|
|
3
|
+
"swap/quote",
|
|
4
|
+
"swap/checkTax",
|
|
5
|
+
"limit-orders",
|
|
6
|
+
"limit-orders/config",
|
|
7
|
+
]);
|
|
8
|
+
const STATUS_PATH = /^limit-orders\/0x[\da-f]{40}\/\d{1,16}$/i;
|
|
9
|
+
|
|
10
|
+
export async function proxySwitch(
|
|
11
|
+
request: Request,
|
|
12
|
+
path: string[],
|
|
13
|
+
apiKey: string,
|
|
14
|
+
): Promise<Response> {
|
|
15
|
+
if (!apiKey)
|
|
16
|
+
return Response.json(
|
|
17
|
+
{ error: "Switch proxy is not configured" },
|
|
18
|
+
{ status: 500 },
|
|
19
|
+
);
|
|
20
|
+
const route = path.join("/");
|
|
21
|
+
const read =
|
|
22
|
+
request.method === "GET" &&
|
|
23
|
+
(READ_PATHS.has(route) || STATUS_PATH.test(route));
|
|
24
|
+
const submit = request.method === "POST" && route === "limit-orders";
|
|
25
|
+
if (!read && !submit)
|
|
26
|
+
return Response.json({ error: "Unsupported route" }, { status: 404 });
|
|
27
|
+
const incoming = new URL(request.url);
|
|
28
|
+
if (
|
|
29
|
+
!["pulsechain", "robinhood"].includes(
|
|
30
|
+
incoming.searchParams.get("network") ?? "",
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
return Response.json(
|
|
34
|
+
{ error: "Select a supported network" },
|
|
35
|
+
{ status: 400 },
|
|
36
|
+
);
|
|
37
|
+
const upstream = new URL(`https://quote.switch.win/${route}`);
|
|
38
|
+
incoming.searchParams.forEach((value, key) =>
|
|
39
|
+
upstream.searchParams.set(key, value),
|
|
40
|
+
);
|
|
41
|
+
let body: string | undefined;
|
|
42
|
+
if (submit) {
|
|
43
|
+
// Enforce a body limit at your hosting/server layer as well.
|
|
44
|
+
body = await request.text();
|
|
45
|
+
if (body.length > 16384)
|
|
46
|
+
return Response.json({ error: "Order body too large" }, { status: 413 });
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(upstream, {
|
|
50
|
+
method: request.method,
|
|
51
|
+
body,
|
|
52
|
+
cache: "no-store",
|
|
53
|
+
headers: {
|
|
54
|
+
"x-api-key": apiKey,
|
|
55
|
+
...(submit ? { "Content-Type": "application/json" } : {}),
|
|
56
|
+
},
|
|
57
|
+
signal: AbortSignal.any([
|
|
58
|
+
request.signal,
|
|
59
|
+
AbortSignal.timeout(submit ? 20000 : 60000),
|
|
60
|
+
]),
|
|
61
|
+
});
|
|
62
|
+
return new Response(await response.text(), {
|
|
63
|
+
status: response.status,
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
"Cache-Control": "no-store, max-age=0",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
} catch {
|
|
70
|
+
// A 502 on POST is uncertain: it does not mean the upstream rejected the order.
|
|
71
|
+
return Response.json(
|
|
72
|
+
{
|
|
73
|
+
error: submit
|
|
74
|
+
? "Submission status uncertain; check the saved order"
|
|
75
|
+
: "Switch API unavailable",
|
|
76
|
+
},
|
|
77
|
+
{ status: 502 },
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// App Router wrappers (adapt params to your installed Next.js version):
|
|
83
|
+
// export async function GET(request, context) {
|
|
84
|
+
// return proxySwitch(request, (await context.params).path, process.env.SWITCH_API_KEY!);
|
|
85
|
+
// }
|
|
86
|
+
// export const POST = GET;
|
|
87
|
+
// Keep SWITCH_API_KEY server-side. A status timeout must retain the maker's pending lock.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Contract, type Provider } from "ethers";
|
|
2
|
+
import {
|
|
3
|
+
GASLESS_SWITCH_ABI,
|
|
4
|
+
assertGaslessOperator,
|
|
5
|
+
assessGaslessOperatorProfit,
|
|
6
|
+
encodeGaslessFill,
|
|
7
|
+
gaslessGasBudget,
|
|
8
|
+
type GaslessDeployment,
|
|
9
|
+
type GaslessOrder,
|
|
10
|
+
type GaslessRoute,
|
|
11
|
+
} from "../src/gasless-index.js";
|
|
12
|
+
|
|
13
|
+
/** Validate an execution proposal from your routing engine. Does not broadcast. */
|
|
14
|
+
export async function checkGaslessExecution(input: {
|
|
15
|
+
provider: Provider;
|
|
16
|
+
deployment: GaslessDeployment;
|
|
17
|
+
operator: string;
|
|
18
|
+
order: GaslessOrder;
|
|
19
|
+
signature: string;
|
|
20
|
+
routes: GaslessRoute[];
|
|
21
|
+
receivedInput?: string;
|
|
22
|
+
quoteGasPriceWei: bigint;
|
|
23
|
+
/** Obtained by tracing the exact fill: actual after-tax payout converted to native. */
|
|
24
|
+
valueActualPayoutNative(call: {
|
|
25
|
+
to: string;
|
|
26
|
+
data: string;
|
|
27
|
+
value: string;
|
|
28
|
+
from: string;
|
|
29
|
+
gasPrice: bigint;
|
|
30
|
+
}): Promise<bigint>;
|
|
31
|
+
}) {
|
|
32
|
+
await assertGaslessOperator(input.provider, input.deployment, input.operator);
|
|
33
|
+
const block = await input.provider.getBlock("latest");
|
|
34
|
+
if (!block) throw Error("Current chain block unavailable");
|
|
35
|
+
const call = encodeGaslessFill({ ...input, chainTimestamp: block.timestamp });
|
|
36
|
+
const settlement = new Contract(
|
|
37
|
+
input.deployment.contract,
|
|
38
|
+
GASLESS_SWITCH_ABI,
|
|
39
|
+
input.provider,
|
|
40
|
+
);
|
|
41
|
+
// Necessary signature/balance/nonce checks, not a profitability assertion.
|
|
42
|
+
if (!(await settlement.canFillOrder(input.order, input.signature)))
|
|
43
|
+
return {
|
|
44
|
+
eligible: false as const,
|
|
45
|
+
reason: "Order is not currently fillable",
|
|
46
|
+
};
|
|
47
|
+
const rawPrice = (await input.provider.getFeeData()).gasPrice;
|
|
48
|
+
if (!rawPrice) throw Error("Current gas price unavailable");
|
|
49
|
+
const initial = gaslessGasBudget(1n, rawPrice, input.quoteGasPriceWei);
|
|
50
|
+
const transaction = {
|
|
51
|
+
...call,
|
|
52
|
+
from: input.operator,
|
|
53
|
+
gasPrice: initial.gasPrice,
|
|
54
|
+
};
|
|
55
|
+
const estimate = await input.provider.estimateGas(transaction);
|
|
56
|
+
const budget = gaslessGasBudget(estimate, rawPrice, input.quoteGasPriceWei);
|
|
57
|
+
const measuredPayout = await input.valueActualPayoutNative(transaction);
|
|
58
|
+
const profit = assessGaslessOperatorProfit(
|
|
59
|
+
measuredPayout,
|
|
60
|
+
budget.gasLimit,
|
|
61
|
+
budget.gasPrice,
|
|
62
|
+
);
|
|
63
|
+
// Operators can reserve further headroom, but never increase the contract cap.
|
|
64
|
+
if (!profit.eligible || measuredPayout <= budget.gasBudgetNative)
|
|
65
|
+
return {
|
|
66
|
+
eligible: false as const,
|
|
67
|
+
reason: "Payout does not cover padded gas",
|
|
68
|
+
};
|
|
69
|
+
await input.provider.call({ ...transaction, gasLimit: budget.gasLimit });
|
|
70
|
+
// Recheck deadline/nonce/role/gas immediately before using this proposal; serialize wallet nonces.
|
|
71
|
+
return {
|
|
72
|
+
eligible: true as const,
|
|
73
|
+
transaction: { ...transaction, gasLimit: budget.gasLimit },
|
|
74
|
+
profit,
|
|
75
|
+
};
|
|
76
|
+
}
|