@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.
package/dist/index.mjs ADDED
@@ -0,0 +1,140 @@
1
+ import {
2
+ OrkidClient,
3
+ SANDBOX_BASE_URL
4
+ } from "./chunk-PMDWMIGW.mjs";
5
+ import {
6
+ OrkidViemPermitSigner,
7
+ PERMIT2_ABI,
8
+ PERMIT_TRANSFER_FROM_TYPES,
9
+ buildNonce,
10
+ buildPermit2Domain,
11
+ buildPermitMessage,
12
+ buildSwapPermit,
13
+ defaultDeadline,
14
+ formatAmount,
15
+ parseAmount,
16
+ randomStartWord,
17
+ toOrkidPermit
18
+ } from "./chunk-3UAOMCO3.mjs";
19
+ import {
20
+ ORKID_CHAINS,
21
+ ORKID_CHAIN_CONFIG,
22
+ PERMIT2,
23
+ chainConfigFromName,
24
+ chainIdFromName,
25
+ chainNameFromId,
26
+ getPermit2Address,
27
+ normalizeChainName
28
+ } from "./chunk-7T23KZR4.mjs";
29
+
30
+ // src/ethers.ts
31
+ import { ethers } from "ethers";
32
+ var OrkidEthersPermitSigner = class {
33
+ signer;
34
+ provider;
35
+ constructor(signer, provider) {
36
+ this.signer = signer;
37
+ this.provider = provider;
38
+ }
39
+ async findUnusedNonce(owner, maxWords = 1e3) {
40
+ const startWord = randomStartWord2();
41
+ const max = BigInt(maxWords);
42
+ const permit2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
43
+ const iface = new ethers.Interface([
44
+ "function nonceBitmap(address owner, uint256 wordPos) view returns (uint256)"
45
+ ]);
46
+ for (let offset = BigInt(0); offset < max; offset++) {
47
+ const wordPos = (startWord + offset) % BigInt(1e6);
48
+ const data = iface.encodeFunctionData("nonceBitmap", [owner, wordPos]);
49
+ const result = await this.provider.call({ to: permit2, data });
50
+ const bitmap = BigInt(result);
51
+ const allBits = (BigInt(1) << BigInt(256)) - BigInt(1);
52
+ if (bitmap === allBits) continue;
53
+ for (let bitPos = 0; bitPos < 256; bitPos++) {
54
+ const bit = BigInt(1) << BigInt(bitPos);
55
+ if ((bitmap & bit) === BigInt(0)) {
56
+ return buildNonce(wordPos, bitPos);
57
+ }
58
+ }
59
+ }
60
+ throw new Error("No unused Permit2 nonce found after scanning 1000 words");
61
+ }
62
+ async signSwap(params) {
63
+ const chainConfig = chainConfigFromName(params.chain);
64
+ const amountRaw = parseAmount(params.amount, params.fromDecimals);
65
+ const nonce = params.nonce || await this.findUnusedNonce(params.user);
66
+ const deadline = params.deadline || defaultDeadline();
67
+ const message = buildPermitMessage({
68
+ token: params.fromToken,
69
+ amount: amountRaw,
70
+ nonce,
71
+ deadline: String(deadline),
72
+ spender: chainConfig.tvmExecutor
73
+ });
74
+ const domain = buildPermit2Domain(chainConfig.id);
75
+ const types = {
76
+ PermitTransferFrom: [
77
+ { name: "permitted", type: "TokenPermissions" },
78
+ { name: "spender", type: "address" },
79
+ { name: "nonce", type: "uint256" },
80
+ { name: "deadline", type: "uint256" }
81
+ ],
82
+ TokenPermissions: [
83
+ { name: "token", type: "address" },
84
+ { name: "amount", type: "uint256" }
85
+ ]
86
+ };
87
+ const signature = await this.signer.signTypedData(
88
+ domain,
89
+ types,
90
+ message
91
+ );
92
+ return {
93
+ permit: toOrkidPermit(message),
94
+ signature,
95
+ chainConfig
96
+ };
97
+ }
98
+ async getPermit2Allowance(token, owner) {
99
+ const permit2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
100
+ const iface = new ethers.Interface([
101
+ "function allowance(address owner, address spender) view returns (uint256)"
102
+ ]);
103
+ const data = iface.encodeFunctionData("allowance", [owner, permit2]);
104
+ const result = await this.provider.call({ to: token, data });
105
+ return BigInt(result);
106
+ }
107
+ };
108
+ function randomStartWord2() {
109
+ if (typeof crypto !== "undefined" && "getRandomValues" in crypto) {
110
+ const arr = new Uint32Array(1);
111
+ crypto.getRandomValues(arr);
112
+ return BigInt(arr[0] >> 8);
113
+ }
114
+ return BigInt(Math.floor(Math.random() * 16777215));
115
+ }
116
+ export {
117
+ ORKID_CHAINS,
118
+ ORKID_CHAIN_CONFIG,
119
+ OrkidClient,
120
+ OrkidEthersPermitSigner,
121
+ OrkidViemPermitSigner,
122
+ PERMIT2,
123
+ PERMIT2_ABI,
124
+ PERMIT_TRANSFER_FROM_TYPES,
125
+ SANDBOX_BASE_URL,
126
+ buildNonce,
127
+ buildPermit2Domain,
128
+ buildPermitMessage,
129
+ buildSwapPermit,
130
+ chainConfigFromName,
131
+ chainIdFromName,
132
+ chainNameFromId,
133
+ defaultDeadline,
134
+ formatAmount,
135
+ getPermit2Address,
136
+ normalizeChainName,
137
+ parseAmount,
138
+ randomStartWord,
139
+ toOrkidPermit
140
+ };
@@ -0,0 +1,23 @@
1
+ import {
2
+ OrkidViemPermitSigner,
3
+ buildNonce,
4
+ buildPermit2Domain,
5
+ buildPermitMessage,
6
+ buildSwapPermit,
7
+ defaultDeadline,
8
+ formatAmount,
9
+ parseAmount,
10
+ toOrkidPermit
11
+ } from "./chunk-3UAOMCO3.mjs";
12
+ import "./chunk-7T23KZR4.mjs";
13
+ export {
14
+ OrkidViemPermitSigner,
15
+ buildNonce,
16
+ buildPermit2Domain,
17
+ buildPermitMessage,
18
+ buildSwapPermit,
19
+ defaultDeadline,
20
+ formatAmount,
21
+ parseAmount,
22
+ toOrkidPermit
23
+ };
package/openapi.yaml ADDED
@@ -0,0 +1,522 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: Orkid Swap API
4
+ description: Gasless, same-chain token swaps with competitive Orkid fee (contact us for pricing).
5
+ version: '1.0.0'
6
+ contact:
7
+ email: api@orkidlabs.com
8
+ servers:
9
+ - url: https://orkidlabs.xyz
10
+ description: Production
11
+ - url: https://orkid-67sli2v23-orkidlabs.vercel.app
12
+ description: Staging
13
+ paths:
14
+ /api/v1/route:
15
+ post:
16
+ summary: Get an executable swap quote
17
+ operationId: getQuote
18
+ security:
19
+ - ApiKeyAuth: []
20
+ requestBody:
21
+ required: true
22
+ content:
23
+ application/json:
24
+ schema:
25
+ $ref: '#/components/schemas/RouteRequest'
26
+ responses:
27
+ '200':
28
+ description: Quote or error envelope
29
+ content:
30
+ application/json:
31
+ schema:
32
+ $ref: '#/components/schemas/OrkidResponse'
33
+ /api/v1/solve:
34
+ post:
35
+ summary: Execute a gasless swap (or dry-run)
36
+ operationId: solveSwap
37
+ security:
38
+ - ApiKeyAuth: []
39
+ requestBody:
40
+ required: true
41
+ content:
42
+ application/json:
43
+ schema:
44
+ $ref: '#/components/schemas/SolveRequest'
45
+ responses:
46
+ '200':
47
+ description: Solved transaction, dry-run calldata, or error envelope
48
+ content:
49
+ application/json:
50
+ schema:
51
+ $ref: '#/components/schemas/OrkidResponse'
52
+ /api/v1/tokens:
53
+ get:
54
+ summary: Search tokens for a chain
55
+ operationId: listTokens
56
+ security:
57
+ - ApiKeyAuth: []
58
+ parameters:
59
+ - name: chain
60
+ in: query
61
+ required: true
62
+ schema:
63
+ type: string
64
+ example: base
65
+ - name: search
66
+ in: query
67
+ schema:
68
+ type: string
69
+ - name: limit
70
+ in: query
71
+ schema:
72
+ type: integer
73
+ example: 50
74
+ responses:
75
+ '200':
76
+ description: Token list
77
+ content:
78
+ application/json:
79
+ schema:
80
+ type: object
81
+ properties:
82
+ ok:
83
+ type: boolean
84
+ tokens:
85
+ type: array
86
+ items:
87
+ $ref: '#/components/schemas/Token'
88
+ error:
89
+ type: string
90
+ /api/v1/account:
91
+ get:
92
+ summary: Get the partner account for this API key
93
+ operationId: getAccount
94
+ security:
95
+ - ApiKeyAuth: []
96
+ responses:
97
+ '200':
98
+ description: Partner account record
99
+ content:
100
+ application/json:
101
+ schema:
102
+ type: object
103
+ properties:
104
+ ok:
105
+ type: boolean
106
+ account:
107
+ $ref: '#/components/schemas/Account'
108
+ error:
109
+ type: string
110
+ '401':
111
+ description: Invalid or revoked API key
112
+ '403':
113
+ description: API key not associated with an account
114
+ /api/v1/usage:
115
+ get:
116
+ summary: List usage events for this API key's account
117
+ operationId: getUsage
118
+ security:
119
+ - ApiKeyAuth: []
120
+ parameters:
121
+ - name: period
122
+ in: query
123
+ description: Month filter in YYYY-MM format (UTC)
124
+ schema:
125
+ type: string
126
+ example: '2026-09'
127
+ - name: event_type
128
+ in: query
129
+ schema:
130
+ type: string
131
+ enum: [route, solve]
132
+ - name: limit
133
+ in: query
134
+ schema:
135
+ type: integer
136
+ default: 100
137
+ responses:
138
+ '200':
139
+ description: Usage events with totals
140
+ content:
141
+ application/json:
142
+ schema:
143
+ type: object
144
+ properties:
145
+ ok:
146
+ type: boolean
147
+ events:
148
+ type: array
149
+ items:
150
+ $ref: '#/components/schemas/UsageEvent'
151
+ total_volume_usd:
152
+ type: number
153
+ total_savings_usd:
154
+ type: number
155
+ error:
156
+ type: string
157
+ '401':
158
+ description: Invalid or revoked API key
159
+ '403':
160
+ description: API key not associated with an account
161
+ /api/v1/rebates:
162
+ get:
163
+ summary: List rebate ledger entries for this API key's account
164
+ operationId: getRebates
165
+ security:
166
+ - ApiKeyAuth: []
167
+ parameters:
168
+ - name: period
169
+ in: query
170
+ description: Month filter in YYYY-MM format (UTC)
171
+ schema:
172
+ type: string
173
+ example: '2026-09'
174
+ responses:
175
+ '200':
176
+ description: Rebate ledger rows, most recent first
177
+ content:
178
+ application/json:
179
+ schema:
180
+ type: object
181
+ properties:
182
+ ok:
183
+ type: boolean
184
+ rebates:
185
+ type: array
186
+ items:
187
+ $ref: '#/components/schemas/Rebate'
188
+ error:
189
+ type: string
190
+ '401':
191
+ description: Invalid or revoked API key
192
+ '403':
193
+ description: API key not associated with an account
194
+ /api/v1/confirm:
195
+ post:
196
+ summary: Confirm a user-submitted swap transaction
197
+ description: >-
198
+ For user-pays-gas swaps (e.g. below the gasless notional floor,
199
+ `gaslessEligible: false`), call this after the user's transaction is
200
+ mined. The API verifies the tx on-chain — receipt success, correct
201
+ TVMExecutor, and keccak256(tx.input) matching the encoded calldata —
202
+ then marks the usage event as a real solve so the volume counts
203
+ toward rebates.
204
+ operationId: confirmTransaction
205
+ security:
206
+ - ApiKeyAuth: []
207
+ requestBody:
208
+ required: true
209
+ content:
210
+ application/json:
211
+ schema:
212
+ type: object
213
+ required: [txHash]
214
+ properties:
215
+ txHash:
216
+ type: string
217
+ description: 32-byte transaction hash
218
+ chain:
219
+ type: string
220
+ description: Optional chain hint (default searches all supported chains)
221
+ responses:
222
+ '200':
223
+ description: Swap confirmed and counted
224
+ '202':
225
+ description: Transaction found but receipt not yet available — retry
226
+ '400':
227
+ description: Malformed txHash
228
+ '404':
229
+ description: Transaction not found on any supported chain
230
+ '422':
231
+ description: Tx reverted, wrong executor, or no matching encoded swap
232
+ /api/v1/handshake:
233
+ get:
234
+ summary: Anti-scrape token handshake
235
+ description: >-
236
+ Issues a fresh anti-scrape cookie (orkid_quote_token) bound to the
237
+ caller's IP. Used by anonymous browser clients to recover from a 403
238
+ 'anti-scraping token' response — call this endpoint, then retry the
239
+ original request once. The SDK performs this recovery automatically.
240
+ Not needed for API-key requests.
241
+ tags: [meta]
242
+ responses:
243
+ '200':
244
+ description: Token issued via Set-Cookie
245
+ '403':
246
+ description: AI crawler blocked
247
+ '429':
248
+ description: Handshake rate limit (10/min per IP)
249
+ '503':
250
+ description: Token service unavailable (secret unset)
251
+ components:
252
+ securitySchemes:
253
+ ApiKeyAuth:
254
+ type: apiKey
255
+ in: header
256
+ name: X-ORKID-API-Key
257
+ schemas:
258
+ RouteRequest:
259
+ type: object
260
+ required: [from, to, amount]
261
+ properties:
262
+ from:
263
+ type: string
264
+ description: Token symbol or address to sell
265
+ example: USDC
266
+ to:
267
+ type: string
268
+ description: Token symbol or address to buy
269
+ example: WETH
270
+ amount:
271
+ type: string
272
+ description: Human-readable amount to sell
273
+ example: '25.0'
274
+ chain:
275
+ type: string
276
+ default: base
277
+ example: base
278
+ fromAddress:
279
+ type: string
280
+ example: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
281
+ fromDecimals:
282
+ type: integer
283
+ example: 6
284
+ toAddress:
285
+ type: string
286
+ example: '0x4200000000000000000000000000000000000006'
287
+ toDecimals:
288
+ type: integer
289
+ example: 18
290
+ SolveRequest:
291
+ allOf:
292
+ - $ref: '#/components/schemas/RouteRequest'
293
+ type: object
294
+ required: [from, to, amount, user, fromAddress, fromDecimals, toAddress, toDecimals, permit, signature]
295
+ properties:
296
+ user:
297
+ type: string
298
+ example: '0xA7362d61c467328cb85c428c2a5D921DBB4e7f5b'
299
+ dryRun:
300
+ type: boolean
301
+ default: false
302
+ slippageBps:
303
+ type: number
304
+ default: 50
305
+ permit:
306
+ $ref: '#/components/schemas/Permit'
307
+ signature:
308
+ type: string
309
+ description: 65-byte hex signature of the EIP-712 PermitTransferFrom message
310
+ example: '0x...'
311
+ Permit:
312
+ type: object
313
+ required: [permitted, nonce, deadline]
314
+ properties:
315
+ permitted:
316
+ type: object
317
+ required: [token, amount]
318
+ properties:
319
+ token:
320
+ type: string
321
+ amount:
322
+ type: string
323
+ nonce:
324
+ type: string
325
+ deadline:
326
+ type: string
327
+ OrkidResponse:
328
+ type: object
329
+ required: [ok, computeMs]
330
+ properties:
331
+ ok:
332
+ type: boolean
333
+ quote:
334
+ $ref: '#/components/schemas/Quote'
335
+ transaction:
336
+ $ref: '#/components/schemas/Transaction'
337
+ savings:
338
+ $ref: '#/components/schemas/Savings'
339
+ error:
340
+ type: string
341
+ computeMs:
342
+ type: integer
343
+ Quote:
344
+ type: object
345
+ required: [amountIn, amountOut, amountOutRaw, volumeUsd, rate, protocol]
346
+ properties:
347
+ amountIn:
348
+ type: string
349
+ amountOut:
350
+ type: string
351
+ amountOutRaw:
352
+ type: string
353
+ volumeUsd:
354
+ type: number
355
+ rate:
356
+ type: string
357
+ protocol:
358
+ type: string
359
+ poolAddress:
360
+ type: string
361
+ priceImpactBps:
362
+ type: number
363
+ Transaction:
364
+ type: object
365
+ required: [to, data, value, chain]
366
+ properties:
367
+ txHash:
368
+ type: string
369
+ to:
370
+ type: string
371
+ data:
372
+ type: string
373
+ value:
374
+ type: string
375
+ chain:
376
+ type: string
377
+ Savings:
378
+ type: object
379
+ required: [orkidBps, metamaskBps, savingsBps, savingsUsd, volumeUsd, savingsMultiplier]
380
+ properties:
381
+ orkidBps:
382
+ type: number
383
+ metamaskBps:
384
+ type: number
385
+ savingsBps:
386
+ type: number
387
+ savingsUsd:
388
+ type: number
389
+ volumeUsd:
390
+ type: number
391
+ savingsMultiplier:
392
+ type: string
393
+ Token:
394
+ type: object
395
+ required: [address, symbol, decimals, chain]
396
+ properties:
397
+ address:
398
+ type: string
399
+ symbol:
400
+ type: string
401
+ decimals:
402
+ type: integer
403
+ chain:
404
+ type: string
405
+ Account:
406
+ type: object
407
+ properties:
408
+ id:
409
+ type: string
410
+ format: uuid
411
+ slug:
412
+ type: string
413
+ name:
414
+ type: string
415
+ contact_email:
416
+ type: string
417
+ rebate_bps:
418
+ type: number
419
+ description: Rebate rate in basis points (e.g. 3 = 0.03%)
420
+ rebate_active:
421
+ type: boolean
422
+ notes:
423
+ type: string
424
+ created_at:
425
+ type: string
426
+ format: date-time
427
+ updated_at:
428
+ type: string
429
+ format: date-time
430
+ UsageEvent:
431
+ type: object
432
+ properties:
433
+ id:
434
+ type: integer
435
+ account_id:
436
+ type: string
437
+ format: uuid
438
+ api_key_id:
439
+ type: string
440
+ format: uuid
441
+ event_type:
442
+ type: string
443
+ enum: [route, solve]
444
+ chain:
445
+ type: string
446
+ token_in:
447
+ type: string
448
+ token_out:
449
+ type: string
450
+ amount_in_raw:
451
+ type: string
452
+ amount_out_raw:
453
+ type: string
454
+ volume_usd:
455
+ type: number
456
+ savings_usd:
457
+ type: number
458
+ fee_bps:
459
+ type: number
460
+ tx_hash:
461
+ type: string
462
+ request_ip:
463
+ type: string
464
+ is_dry_run:
465
+ type: boolean
466
+ description: Dry-run solves are excluded from rebate volume
467
+ is_test:
468
+ type: boolean
469
+ description: >-
470
+ Operator-marked test traffic — excluded from rebateable volume.
471
+ Set server-side by Orkid; cannot be requested by API clients.
472
+ metadata:
473
+ type: object
474
+ created_at:
475
+ type: string
476
+ format: date-time
477
+ Rebate:
478
+ type: object
479
+ properties:
480
+ id:
481
+ type: integer
482
+ account_id:
483
+ type: string
484
+ format: uuid
485
+ period:
486
+ type: string
487
+ enum: [tx, daily, weekly, monthly, quarterly, annual]
488
+ description: Rebate frequency (the month is in period_start)
489
+ period_start:
490
+ type: string
491
+ format: date-time
492
+ description: UTC start of the rebate period
493
+ volume_usd:
494
+ type: number
495
+ rebate_bps:
496
+ type: number
497
+ rebate_usd:
498
+ type: number
499
+ kind:
500
+ type: string
501
+ enum: [volume, referral]
502
+ default: volume
503
+ description: >-
504
+ 'volume' = rebate on the account's own swap volume;
505
+ 'referral' = income paid to this account for a referred partner's
506
+ volume (see source_account_id).
507
+ source_account_id:
508
+ type: string
509
+ format: uuid
510
+ nullable: true
511
+ description: For kind='referral', the referred account whose volume generated this row
512
+ status:
513
+ type: string
514
+ enum: [accrued, paid, voided]
515
+ settled_at:
516
+ type: string
517
+ format: date-time
518
+ settlement_tx_hash:
519
+ type: string
520
+ created_at:
521
+ type: string
522
+ format: date-time