@orkid-labs/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,542 @@
1
+ import { WalletClient, PublicClient, Hex } from 'viem';
2
+ import { ethers } from 'ethers';
3
+
4
+ /**
5
+ * Core types for the Orkid Swap API.
6
+ *
7
+ * The /route and /solve endpoints share the same response envelope. A quote is
8
+ * returned in the `quote` field; a solved transaction is returned in the
9
+ * `transaction` field when applicable.
10
+ */
11
+ type OrkidChain = 'base';
12
+ /**
13
+ * Token data returned by /api/v1/tokens.
14
+ */
15
+ interface OrkidToken {
16
+ address: string;
17
+ symbol: string;
18
+ decimals: number;
19
+ chain: string;
20
+ }
21
+ interface OrkidQuote {
22
+ /** Human-readable amount in (e.g. "25.000000") */
23
+ amountIn: string;
24
+ /** Human-readable amount out after Orkid fee */
25
+ amountOut: string;
26
+ /** Raw atomic amount out as a string */
27
+ amountOutRaw: string;
28
+ /** USD notional of the swap input */
29
+ volumeUsd: number;
30
+ /** Human-readable rate, e.g. "0.000407 WETH/USDC" */
31
+ rate: string;
32
+ /** Best routing protocol, e.g. "aerodrome_slipstreams" */
33
+ protocol: string;
34
+ /** Pool address used for the primary hop */
35
+ poolAddress?: string;
36
+ /** Estimated price impact in basis points */
37
+ priceImpactBps?: number;
38
+ }
39
+ interface OrkidSavings {
40
+ /** Orkid fee in basis points (competitive — contact Orkid for pricing) */
41
+ orkidBps: number;
42
+ /** Estimated MetaMask-like fee for comparison */
43
+ metamaskBps: number;
44
+ /** Savings vs MetaMask in basis points */
45
+ savingsBps: number;
46
+ /** Estimated USD savings */
47
+ savingsUsd: number;
48
+ /** USD notional of the swap */
49
+ volumeUsd: number;
50
+ /** Human-readable savings multiplier */
51
+ savingsMultiplier: string;
52
+ }
53
+ interface OrkidTransaction {
54
+ /** Transaction hash when the solver submitted gaslessly */
55
+ txHash?: string;
56
+ /** Contract the transaction is sent to (TVMExecutor) */
57
+ to: string;
58
+ /** Calldata for the transaction */
59
+ data: string;
60
+ /** ETH value to send (usually 0) */
61
+ value: string;
62
+ /** Chain the tx is for */
63
+ chain: string;
64
+ }
65
+ /**
66
+ * Success/error envelope returned by both /route and /solve.
67
+ */
68
+ interface OrkidResponse {
69
+ ok: boolean;
70
+ quote?: OrkidQuote;
71
+ transaction?: OrkidTransaction;
72
+ savings?: OrkidSavings;
73
+ error?: string;
74
+ /**
75
+ * False when the swap is below the solver's gasless notional floor —
76
+ * the client should submit the returned calldata itself (user pays gas).
77
+ */
78
+ gaslessEligible?: boolean;
79
+ computeMs: number;
80
+ }
81
+ interface OrkidRouteRequest {
82
+ /** Token symbol or address to sell */
83
+ from: string;
84
+ /** Token symbol or address to buy */
85
+ to: string;
86
+ /** Human-readable amount to sell, e.g. "25.0" */
87
+ amount: string;
88
+ /** Chain name, e.g. "base" */
89
+ chain?: string;
90
+ /** Explicit token-in address (recommended) */
91
+ fromAddress?: string;
92
+ /** Explicit token-in decimals (required if fromAddress is provided) */
93
+ fromDecimals?: number;
94
+ /** Explicit token-out address (recommended) */
95
+ toAddress?: string;
96
+ /** Explicit token-out decimals (required if toAddress is provided) */
97
+ toDecimals?: number;
98
+ }
99
+ interface OrkidPermit {
100
+ permitted: {
101
+ /** Token address */
102
+ token: string;
103
+ /** Raw atomic amount */
104
+ amount: string;
105
+ };
106
+ /** Permit2 nonce as a uint256 string */
107
+ nonce: string;
108
+ /** Unix timestamp deadline */
109
+ deadline: string;
110
+ }
111
+ interface OrkidSolveRequest extends OrkidRouteRequest {
112
+ /** User wallet address */
113
+ user: string;
114
+ /** Permit2 permit object (without spender) */
115
+ permit: OrkidPermit;
116
+ /** 65-byte hex signature */
117
+ signature: string;
118
+ /** If true, only return calldata and do not submit */
119
+ dryRun?: boolean;
120
+ /** Slippage in basis points (e.g. 5 = 0.05%) */
121
+ slippageBps?: number;
122
+ }
123
+ /**
124
+ * Client options.
125
+ */
126
+ interface OrkidClientOptions {
127
+ /** 40-character hex Orkid API key. Optional when using a partner proxy. */
128
+ apiKey?: string;
129
+ /** Base URL for the Orkid API */
130
+ baseUrl?: string;
131
+ /** Optional fetch override */
132
+ fetch?: typeof fetch;
133
+ /**
134
+ * Orkid operator secret — marks usage events as test traffic (excluded from
135
+ * rebateable volume). Server-side only; never set this in browser code.
136
+ */
137
+ operatorSecret?: string;
138
+ }
139
+ /**
140
+ * Partner account record from GET /api/v1/account.
141
+ */
142
+ interface OrkidAccount {
143
+ id: string;
144
+ slug: string;
145
+ name: string;
146
+ contact_email?: string;
147
+ /** Rebate rate in basis points (e.g. 3 = 0.03%) */
148
+ rebate_bps: number;
149
+ rebate_active: boolean;
150
+ notes?: string;
151
+ created_at?: string;
152
+ updated_at?: string;
153
+ }
154
+ interface OrkidAccountResponse {
155
+ ok: boolean;
156
+ account?: OrkidAccount;
157
+ error?: string;
158
+ }
159
+ /**
160
+ * A single usage event from GET /api/v1/usage.
161
+ */
162
+ interface OrkidUsageEvent {
163
+ id: number;
164
+ account_id: string;
165
+ api_key_id: string;
166
+ event_type: 'route' | 'solve' | string;
167
+ chain?: string;
168
+ token_in?: string;
169
+ token_out?: string;
170
+ amount_in_raw?: string | number | null;
171
+ amount_out_raw?: string | number | null;
172
+ volume_usd?: number | null;
173
+ savings_usd?: number | null;
174
+ fee_bps?: number | null;
175
+ tx_hash?: string | null;
176
+ request_ip?: string | null;
177
+ is_dry_run: boolean;
178
+ metadata?: Record<string, unknown>;
179
+ created_at: string;
180
+ }
181
+ interface OrkidUsageResponse {
182
+ ok: boolean;
183
+ events?: OrkidUsageEvent[];
184
+ total_volume_usd?: number;
185
+ total_savings_usd?: number;
186
+ error?: string;
187
+ }
188
+ /**
189
+ * A monthly rebate ledger row from GET /api/v1/rebates.
190
+ */
191
+ interface OrkidRebate {
192
+ id: number;
193
+ account_id: string;
194
+ /** Rebate frequency, e.g. "monthly" */
195
+ period: string;
196
+ /** ISO timestamp of the period start (month) */
197
+ period_start: string;
198
+ volume_usd: number;
199
+ rebate_bps: number;
200
+ rebate_usd: number;
201
+ status: 'accrued' | 'paid' | 'voided' | string;
202
+ settled_at?: string | null;
203
+ settlement_tx_hash?: string | null;
204
+ created_at?: string;
205
+ }
206
+ interface OrkidRebatesResponse {
207
+ ok: boolean;
208
+ rebates?: OrkidRebate[];
209
+ error?: string;
210
+ }
211
+
212
+ /** Sandbox base URL — dry-run mode, real quotes but no execution. */
213
+ declare const SANDBOX_BASE_URL = "https://sandbox.orkidlabs.com";
214
+ /**
215
+ * HTTP client for the Orkid /api/v1 endpoints.
216
+ *
217
+ * Example:
218
+ *
219
+ * ```ts
220
+ * const orkid = new OrkidClient({ apiKey: process.env.ORKID_API_KEY })
221
+ * const quote = await orkid.getQuote({ from: 'USDC', to: 'WETH', amount: '25', chain: 'base' })
222
+ * ```
223
+ */
224
+ declare class OrkidClient {
225
+ private apiKey;
226
+ private operatorSecret;
227
+ private baseUrl;
228
+ private fetch;
229
+ constructor(options: OrkidClientOptions);
230
+ private baseHeaders;
231
+ /**
232
+ * Anonymous (keyless) browser calls can hit 403 when the anti-scrape token
233
+ * is missing or expired. Recover by hitting /api/v1/handshake (issues a
234
+ * fresh bound cookie) and retrying once.
235
+ */
236
+ private fetchWithHandshake;
237
+ /**
238
+ * Get an executable quote for a swap.
239
+ */
240
+ getQuote(request: OrkidRouteRequest): Promise<OrkidResponse>;
241
+ /**
242
+ * Solve a swap gaslessly.
243
+ */
244
+ solve(request: OrkidSolveRequest): Promise<OrkidResponse>;
245
+ /**
246
+ * Convenience wrapper: get a quote then dry-run solve the same swap with a
247
+ * pre-signed permit. Useful for testing signatures before live execution.
248
+ */
249
+ dryRun(request: Omit<OrkidSolveRequest, 'dryRun'>): Promise<OrkidResponse>;
250
+ /**
251
+ * Fetch a list of tokens for a chain from the Orkid token search endpoint.
252
+ * This is a convenience; the API key is optional for this read-only endpoint.
253
+ */
254
+ listTokens(params?: {
255
+ chain: string;
256
+ search?: string;
257
+ limit?: number;
258
+ }): Promise<{
259
+ ok: boolean;
260
+ tokens?: {
261
+ address: string;
262
+ symbol: string;
263
+ decimals: number;
264
+ chain: string;
265
+ }[];
266
+ error?: string;
267
+ }>;
268
+ /**
269
+ * Fetch the partner account associated with this API key.
270
+ */
271
+ getAccount(): Promise<OrkidAccountResponse>;
272
+ /**
273
+ * Fetch usage events for the partner account. Optionally filter by month
274
+ * (`period: 'YYYY-MM'`) and event type (`'route'` | `'solve'`).
275
+ */
276
+ getUsage(params?: {
277
+ period?: string;
278
+ eventType?: 'route' | 'solve' | string;
279
+ limit?: number;
280
+ }): Promise<OrkidUsageResponse>;
281
+ /**
282
+ * Fetch the rebate ledger for the partner account. Optionally filter by
283
+ * month (`period: 'YYYY-MM'`).
284
+ */
285
+ getRebates(params?: {
286
+ period?: string;
287
+ }): Promise<OrkidRebatesResponse>;
288
+ /**
289
+ * Health check on the API. The API key is optional here.
290
+ */
291
+ getStatus(): Promise<{
292
+ ok: boolean;
293
+ [key: string]: unknown;
294
+ }>;
295
+ /**
296
+ * Confirm a user-submitted swap (user-pays-gas mode, e.g. below the gasless
297
+ * floor). The API verifies the tx on-chain and counts it as a real solve —
298
+ * this is what makes user-paid volume rebate-eligible.
299
+ */
300
+ confirmTransaction(txHash: string, chain?: string): Promise<{
301
+ ok: boolean;
302
+ confirmed?: boolean;
303
+ eventId?: number;
304
+ txHash?: string;
305
+ chain?: string;
306
+ error?: string;
307
+ }>;
308
+ private get;
309
+ private post;
310
+ }
311
+
312
+ /**
313
+ * Chain metadata for Orkid swaps.
314
+ *
315
+ * Base, Ethereum, Arbitrum, and Polygon are live with production TVMExecutor
316
+ * proxies at 30 bps. Each chain also has a sandbox TVMExecutor at 75 bps
317
+ * for the retail widget on orkidlabs.xyz.
318
+ */
319
+ interface OrkidChainConfig {
320
+ id: number;
321
+ name: string;
322
+ shortName: string;
323
+ color: string;
324
+ permit2: string;
325
+ tvmExecutor: string;
326
+ /** Sandbox TVMExecutor at 75 bps for the retail widget (orkidlabs.xyz). */
327
+ sandboxTvmExecutor: string;
328
+ tychoRouter: string;
329
+ explorer: string;
330
+ rpcUrl: string;
331
+ nativeSymbol: string;
332
+ nativeDecimals: number;
333
+ minNotionalUsd: number;
334
+ isLive: boolean;
335
+ }
336
+ declare const PERMIT2: "0x000000000022D473030F116dDEE9F6B43aC78BA3";
337
+ /** Canonical Permit2 contract address on all EVM chains. */
338
+ declare function getPermit2Address(): string;
339
+ /**
340
+ * Map chain identifiers to the solver/TVMExecutor config.
341
+ *
342
+ * The TVMExecutor is the `spender` that must appear in the signed Permit2
343
+ * PermitTransferFrom message.
344
+ *
345
+ * Production TVMExecutors charge 30 bps. Sandbox TVMExecutors charge 75 bps
346
+ * and are used by the retail widget on orkidlabs.xyz.
347
+ */
348
+ declare const ORKID_CHAIN_CONFIG: Record<number, OrkidChainConfig>;
349
+ /** Supported chain names. */
350
+ declare const ORKID_CHAINS: readonly ["base", "ethereum", "arbitrum", "polygon"];
351
+ declare function chainIdFromName(name: string): number;
352
+ declare function chainNameFromId(chainId: number): string;
353
+ declare function chainConfigFromName(name: string): OrkidChainConfig;
354
+ declare function normalizeChainName(name: string): string;
355
+
356
+ /**
357
+ * EIP-712 typed-data definition for Permit2 PermitTransferFrom.
358
+ *
359
+ * The `spender` field is part of the signed type string but is not included in
360
+ * the JSON `permit` object sent to /api/v1/solve.
361
+ */
362
+ declare const PERMIT_TRANSFER_FROM_TYPES: {
363
+ readonly PermitTransferFrom: readonly [{
364
+ readonly name: "permitted";
365
+ readonly type: "TokenPermissions";
366
+ }, {
367
+ readonly name: "spender";
368
+ readonly type: "address";
369
+ }, {
370
+ readonly name: "nonce";
371
+ readonly type: "uint256";
372
+ }, {
373
+ readonly name: "deadline";
374
+ readonly type: "uint256";
375
+ }];
376
+ readonly TokenPermissions: readonly [{
377
+ readonly name: "token";
378
+ readonly type: "address";
379
+ }, {
380
+ readonly name: "amount";
381
+ readonly type: "uint256";
382
+ }];
383
+ };
384
+ interface OrkidPermitMessage {
385
+ permitted: {
386
+ token: string;
387
+ amount: string;
388
+ };
389
+ spender: string;
390
+ nonce: string;
391
+ deadline: string;
392
+ }
393
+ interface OrkidPermitDomain {
394
+ name: string;
395
+ chainId: number;
396
+ verifyingContract: `0x${string}`;
397
+ }
398
+ /**
399
+ * Build the EIP-712 domain for Permit2 on a given chain.
400
+ */
401
+ declare function buildPermit2Domain(chainId: number): OrkidPermitDomain;
402
+ /**
403
+ * Build the full PermitTransferFrom message (including the `spender`) that must
404
+ * be signed by the user.
405
+ */
406
+ declare function buildPermitMessage(params: {
407
+ token: string;
408
+ amount: string;
409
+ nonce: string;
410
+ deadline: string;
411
+ spender: string;
412
+ }): OrkidPermitMessage;
413
+ /**
414
+ * Strip the `spender` from the signed permit to produce the object sent to
415
+ * /api/v1/solve.
416
+ */
417
+ declare function toOrkidPermit(message: OrkidPermitMessage): OrkidPermit;
418
+ /**
419
+ * Convert a human-readable token amount to raw atomic units based on decimals.
420
+ */
421
+ declare function parseAmount(value: string, decimals: number): string;
422
+ /**
423
+ * Format a raw atomic amount to a human-readable string.
424
+ */
425
+ declare function formatAmount(raw: string, decimals: number): string;
426
+ /**
427
+ * Build the permit for a swap using the Orkid TVMExecutor as the spender.
428
+ */
429
+ declare function buildSwapPermit(chainName: string, token: string, amount: string, nonce: string, deadlineSeconds: number): {
430
+ message: OrkidPermitMessage;
431
+ permit: OrkidPermit;
432
+ chainConfig: OrkidChainConfig;
433
+ };
434
+ /**
435
+ * Compute a default deadline 1 hour from now.
436
+ */
437
+ declare function defaultDeadline(): number;
438
+ /**
439
+ * Permit2 contract nonce layout:
440
+ *
441
+ * wordPos = uint248(nonce >> 8)
442
+ * bitPos = uint8(nonce)
443
+ *
444
+ * This helper constructs a nonce from a word position and bit position.
445
+ */
446
+ declare function buildNonce(wordPos: bigint, bitPos: number): string;
447
+ /**
448
+ * Random starting word position to avoid nonce collisions across users.
449
+ */
450
+ declare function randomStartWord(): bigint;
451
+ /**
452
+ * Minimal Permit2 nonceBitmap ABI and selectors.
453
+ */
454
+ declare const PERMIT2_ABI: readonly [{
455
+ readonly type: "function";
456
+ readonly name: "nonceBitmap";
457
+ readonly inputs: readonly [{
458
+ readonly name: "owner";
459
+ readonly type: "address";
460
+ }, {
461
+ readonly name: "wordPos";
462
+ readonly type: "uint256";
463
+ }];
464
+ readonly outputs: readonly [{
465
+ readonly name: "";
466
+ readonly type: "uint256";
467
+ }];
468
+ readonly stateMutability: "view";
469
+ }];
470
+
471
+ /**
472
+ * viem-specific helpers for Orkid Permit2 signing.
473
+ *
474
+ * This file is optional — the core SDK has no viem dependency. Import this
475
+ * module if you already have viem in your project.
476
+ */
477
+
478
+ interface OrkidSignSwapParams$1 {
479
+ /** User wallet address */
480
+ user: string;
481
+ /** Token-in address */
482
+ fromToken: string;
483
+ /** Token-in decimals */
484
+ fromDecimals: number;
485
+ /** Human-readable amount to sell */
486
+ amount: string;
487
+ /** Chain name, e.g. 'base' */
488
+ chain: string;
489
+ /** Optional explicit nonce. If omitted, one is found on-chain. */
490
+ nonce?: string;
491
+ /** Optional deadline (seconds). Defaults to now + 1 hour. */
492
+ deadline?: number;
493
+ }
494
+ /**
495
+ * Signer for Orkid Permit2 messages using a viem WalletClient.
496
+ */
497
+ declare class OrkidViemPermitSigner {
498
+ private walletClient;
499
+ private publicClient;
500
+ constructor(walletClient: WalletClient, publicClient: PublicClient);
501
+ findUnusedNonce(owner: string, maxWords?: number): Promise<string>;
502
+ signSwap(params: OrkidSignSwapParams$1): Promise<{
503
+ permit: OrkidPermit;
504
+ signature: Hex;
505
+ chainConfig: OrkidChainConfig;
506
+ }>;
507
+ /**
508
+ * Read the ERC20 allowance of the user for the Permit2 contract.
509
+ */
510
+ getPermit2Allowance(token: string, owner: string): Promise<bigint>;
511
+ }
512
+
513
+ /**
514
+ * ethers v6-specific helpers for Orkid Permit2 signing.
515
+ *
516
+ * This file is optional — the core SDK has no ethers dependency. Import this
517
+ * module if you already have ethers in your project.
518
+ */
519
+
520
+ interface OrkidSignSwapParams {
521
+ user: string;
522
+ fromToken: string;
523
+ fromDecimals: number;
524
+ amount: string;
525
+ chain: string;
526
+ nonce?: string;
527
+ deadline?: number;
528
+ }
529
+ declare class OrkidEthersPermitSigner {
530
+ private signer;
531
+ private provider;
532
+ constructor(signer: ethers.Signer, provider: ethers.Provider);
533
+ findUnusedNonce(owner: string, maxWords?: number): Promise<string>;
534
+ signSwap(params: OrkidSignSwapParams): Promise<{
535
+ permit: OrkidPermit;
536
+ signature: string;
537
+ chainConfig: OrkidChainConfig;
538
+ }>;
539
+ getPermit2Allowance(token: string, owner: string): Promise<bigint>;
540
+ }
541
+
542
+ export { ORKID_CHAINS, ORKID_CHAIN_CONFIG, type OrkidAccount, type OrkidAccountResponse, type OrkidChain, OrkidClient, type OrkidClientOptions, OrkidEthersPermitSigner, type OrkidPermit, type OrkidPermitDomain, type OrkidPermitMessage, type OrkidQuote, type OrkidRebate, type OrkidRebatesResponse, type OrkidResponse, type OrkidRouteRequest, type OrkidSavings, type OrkidSignSwapParams$1 as OrkidSignSwapParams, type OrkidSolveRequest, type OrkidToken, type OrkidTransaction, type OrkidUsageEvent, type OrkidUsageResponse, OrkidViemPermitSigner, PERMIT2, PERMIT2_ABI, PERMIT_TRANSFER_FROM_TYPES, SANDBOX_BASE_URL, buildNonce, buildPermit2Domain, buildPermitMessage, buildSwapPermit, chainConfigFromName, chainIdFromName, chainNameFromId, defaultDeadline, formatAmount, getPermit2Address, normalizeChainName, parseAmount, randomStartWord, toOrkidPermit };