@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/README.md +689 -0
- package/dist/chains-QHQI25WO.mjs +20 -0
- package/dist/chunk-3UAOMCO3.mjs +227 -0
- package/dist/chunk-7T23KZR4.mjs +122 -0
- package/dist/chunk-PMDWMIGW.mjs +164 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +856 -0
- package/dist/cli.mjs +324 -0
- package/dist/index.d.mts +542 -0
- package/dist/index.d.ts +542 -0
- package/dist/index.js +640 -0
- package/dist/index.mjs +140 -0
- package/dist/viem-CZMIEVIX.mjs +23 -0
- package/openapi.yaml +522 -0
- package/package.json +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ORKID_CHAINS: () => ORKID_CHAINS,
|
|
24
|
+
ORKID_CHAIN_CONFIG: () => ORKID_CHAIN_CONFIG,
|
|
25
|
+
OrkidClient: () => OrkidClient,
|
|
26
|
+
OrkidEthersPermitSigner: () => OrkidEthersPermitSigner,
|
|
27
|
+
OrkidViemPermitSigner: () => OrkidViemPermitSigner,
|
|
28
|
+
PERMIT2: () => PERMIT2,
|
|
29
|
+
PERMIT2_ABI: () => PERMIT2_ABI,
|
|
30
|
+
PERMIT_TRANSFER_FROM_TYPES: () => PERMIT_TRANSFER_FROM_TYPES,
|
|
31
|
+
SANDBOX_BASE_URL: () => SANDBOX_BASE_URL,
|
|
32
|
+
buildNonce: () => buildNonce,
|
|
33
|
+
buildPermit2Domain: () => buildPermit2Domain,
|
|
34
|
+
buildPermitMessage: () => buildPermitMessage,
|
|
35
|
+
buildSwapPermit: () => buildSwapPermit,
|
|
36
|
+
chainConfigFromName: () => chainConfigFromName,
|
|
37
|
+
chainIdFromName: () => chainIdFromName,
|
|
38
|
+
chainNameFromId: () => chainNameFromId,
|
|
39
|
+
defaultDeadline: () => defaultDeadline,
|
|
40
|
+
formatAmount: () => formatAmount,
|
|
41
|
+
getPermit2Address: () => getPermit2Address,
|
|
42
|
+
normalizeChainName: () => normalizeChainName,
|
|
43
|
+
parseAmount: () => parseAmount,
|
|
44
|
+
randomStartWord: () => randomStartWord,
|
|
45
|
+
toOrkidPermit: () => toOrkidPermit
|
|
46
|
+
});
|
|
47
|
+
module.exports = __toCommonJS(index_exports);
|
|
48
|
+
|
|
49
|
+
// src/client.ts
|
|
50
|
+
var DEFAULT_BASE_URL = "https://orkidlabs.xyz";
|
|
51
|
+
var SANDBOX_BASE_URL = "https://sandbox.orkidlabs.com";
|
|
52
|
+
var OrkidClient = class {
|
|
53
|
+
apiKey;
|
|
54
|
+
operatorSecret;
|
|
55
|
+
baseUrl;
|
|
56
|
+
fetch;
|
|
57
|
+
constructor(options) {
|
|
58
|
+
this.apiKey = options.apiKey;
|
|
59
|
+
this.operatorSecret = options.operatorSecret;
|
|
60
|
+
this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
61
|
+
this.fetch = options.fetch || globalThis.fetch.bind(globalThis);
|
|
62
|
+
if (this.apiKey && !/^[a-f0-9]{40}$/i.test(this.apiKey)) {
|
|
63
|
+
throw new Error("OrkidClient: apiKey must be a 40-character hex string");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
baseHeaders(json) {
|
|
67
|
+
const headers = {};
|
|
68
|
+
if (json) headers["Content-Type"] = "application/json";
|
|
69
|
+
if (this.apiKey) headers["X-ORKID-API-Key"] = this.apiKey;
|
|
70
|
+
if (this.operatorSecret) headers["X-ORKID-Operator"] = this.operatorSecret;
|
|
71
|
+
return headers;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Anonymous (keyless) browser calls can hit 403 when the anti-scrape token
|
|
75
|
+
* is missing or expired. Recover by hitting /api/v1/handshake (issues a
|
|
76
|
+
* fresh bound cookie) and retrying once.
|
|
77
|
+
*/
|
|
78
|
+
async fetchWithHandshake(doFetch) {
|
|
79
|
+
let res = await doFetch();
|
|
80
|
+
if (res.status === 403 && !this.apiKey) {
|
|
81
|
+
const body = await res.clone().json().catch(() => null);
|
|
82
|
+
if (body && typeof body.error === "string" && /anti-scraping token/i.test(body.error)) {
|
|
83
|
+
try {
|
|
84
|
+
await this.fetch(`${this.baseUrl}/api/v1/handshake`, { credentials: "include" });
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
res = await doFetch();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return res;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get an executable quote for a swap.
|
|
94
|
+
*/
|
|
95
|
+
async getQuote(request) {
|
|
96
|
+
return this.post("/api/v1/route", request);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Solve a swap gaslessly.
|
|
100
|
+
*/
|
|
101
|
+
async solve(request) {
|
|
102
|
+
return this.post("/api/v1/solve", request);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Convenience wrapper: get a quote then dry-run solve the same swap with a
|
|
106
|
+
* pre-signed permit. Useful for testing signatures before live execution.
|
|
107
|
+
*/
|
|
108
|
+
async dryRun(request) {
|
|
109
|
+
return this.solve({ ...request, dryRun: true });
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Fetch a list of tokens for a chain from the Orkid token search endpoint.
|
|
113
|
+
* This is a convenience; the API key is optional for this read-only endpoint.
|
|
114
|
+
*/
|
|
115
|
+
async listTokens(params = { chain: "base" }) {
|
|
116
|
+
const url = new URL(`${this.baseUrl}/api/v1/tokens`);
|
|
117
|
+
url.searchParams.set("chain", params.chain);
|
|
118
|
+
if (params.search) url.searchParams.set("search", params.search);
|
|
119
|
+
if (params.limit) url.searchParams.set("limit", String(params.limit));
|
|
120
|
+
const headers = this.baseHeaders(false);
|
|
121
|
+
const res = await this.fetchWithHandshake(() => this.fetch(url.toString(), { headers }));
|
|
122
|
+
if (!res.ok) {
|
|
123
|
+
const text = await res.text().catch(() => "");
|
|
124
|
+
return { ok: false, error: `HTTP ${res.status}: ${text}` };
|
|
125
|
+
}
|
|
126
|
+
return res.json();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Fetch the partner account associated with this API key.
|
|
130
|
+
*/
|
|
131
|
+
async getAccount() {
|
|
132
|
+
return this.get("/api/v1/account");
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Fetch usage events for the partner account. Optionally filter by month
|
|
136
|
+
* (`period: 'YYYY-MM'`) and event type (`'route'` | `'solve'`).
|
|
137
|
+
*/
|
|
138
|
+
async getUsage(params = {}) {
|
|
139
|
+
const query = {};
|
|
140
|
+
if (params.period) query.period = params.period;
|
|
141
|
+
if (params.eventType) query.event_type = params.eventType;
|
|
142
|
+
if (params.limit) query.limit = String(params.limit);
|
|
143
|
+
return this.get("/api/v1/usage", query);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Fetch the rebate ledger for the partner account. Optionally filter by
|
|
147
|
+
* month (`period: 'YYYY-MM'`).
|
|
148
|
+
*/
|
|
149
|
+
async getRebates(params = {}) {
|
|
150
|
+
const query = {};
|
|
151
|
+
if (params.period) query.period = params.period;
|
|
152
|
+
return this.get("/api/v1/rebates", query);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Health check on the API. The API key is optional here.
|
|
156
|
+
*/
|
|
157
|
+
async getStatus() {
|
|
158
|
+
return this.get("/api/v1/status");
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Confirm a user-submitted swap (user-pays-gas mode, e.g. below the gasless
|
|
162
|
+
* floor). The API verifies the tx on-chain and counts it as a real solve —
|
|
163
|
+
* this is what makes user-paid volume rebate-eligible.
|
|
164
|
+
*/
|
|
165
|
+
async confirmTransaction(txHash, chain) {
|
|
166
|
+
const headers = this.baseHeaders(true);
|
|
167
|
+
const res = await this.fetchWithHandshake(
|
|
168
|
+
() => this.fetch(`${this.baseUrl}/api/v1/confirm`, {
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers,
|
|
171
|
+
body: JSON.stringify({ txHash, chain })
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
const data = await res.json().catch(() => null);
|
|
175
|
+
return data ?? { ok: false, error: `HTTP ${res.status}: empty response` };
|
|
176
|
+
}
|
|
177
|
+
async get(path, query = {}) {
|
|
178
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
179
|
+
for (const [k, v] of Object.entries(query)) url.searchParams.set(k, v);
|
|
180
|
+
const headers = this.baseHeaders(false);
|
|
181
|
+
const res = await this.fetchWithHandshake(() => this.fetch(url.toString(), { headers }));
|
|
182
|
+
const data = await res.json().catch(() => null);
|
|
183
|
+
if (!data) {
|
|
184
|
+
return { ok: false, error: `HTTP ${res.status}: empty response` };
|
|
185
|
+
}
|
|
186
|
+
return data;
|
|
187
|
+
}
|
|
188
|
+
async post(path, body) {
|
|
189
|
+
const headers = this.baseHeaders(true);
|
|
190
|
+
const res = await this.fetchWithHandshake(
|
|
191
|
+
() => this.fetch(`${this.baseUrl}${path}`, {
|
|
192
|
+
method: "POST",
|
|
193
|
+
headers,
|
|
194
|
+
body: JSON.stringify(body)
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
const data = await res.json().catch(() => null);
|
|
198
|
+
if (!data) {
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
error: `HTTP ${res.status}: empty response`,
|
|
202
|
+
computeMs: 0
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return data;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// src/chains.ts
|
|
210
|
+
var PERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
211
|
+
function getPermit2Address() {
|
|
212
|
+
return PERMIT2;
|
|
213
|
+
}
|
|
214
|
+
var ORKID_CHAIN_CONFIG = {
|
|
215
|
+
8453: {
|
|
216
|
+
id: 8453,
|
|
217
|
+
name: "base",
|
|
218
|
+
shortName: "BASE",
|
|
219
|
+
color: "#0052FF",
|
|
220
|
+
permit2: PERMIT2,
|
|
221
|
+
tvmExecutor: "0x60AB9E090abF9B6BcFbA16017eE18AEf5f2c2289",
|
|
222
|
+
sandboxTvmExecutor: "0xD5B87788AF6F9E04E7FA298Bd973cEEA8c0Df0aF",
|
|
223
|
+
tychoRouter: "0x2D3524b9b5dAE34B646614eebb1E038D403E4Cac",
|
|
224
|
+
explorer: "https://basescan.org",
|
|
225
|
+
rpcUrl: "https://rpc.orkidlabs.com/rpc/base",
|
|
226
|
+
nativeSymbol: "ETH",
|
|
227
|
+
nativeDecimals: 18,
|
|
228
|
+
minNotionalUsd: 20,
|
|
229
|
+
isLive: true
|
|
230
|
+
},
|
|
231
|
+
1: {
|
|
232
|
+
id: 1,
|
|
233
|
+
name: "ethereum",
|
|
234
|
+
shortName: "ETH",
|
|
235
|
+
color: "#627EEA",
|
|
236
|
+
permit2: PERMIT2,
|
|
237
|
+
tvmExecutor: "0xCfc33b521190FcD414c8f81f479749c4dCE8f69b",
|
|
238
|
+
sandboxTvmExecutor: "0xd1b5c6e142fd95be389cc4ca117cb0fb7429534f",
|
|
239
|
+
tychoRouter: "0xfd0b31d2e955fa55e3fa641fe90e08b677188d35",
|
|
240
|
+
explorer: "https://etherscan.io",
|
|
241
|
+
rpcUrl: "https://rpc.orkidlabs.com/rpc/ethereum",
|
|
242
|
+
nativeSymbol: "ETH",
|
|
243
|
+
nativeDecimals: 18,
|
|
244
|
+
minNotionalUsd: 200,
|
|
245
|
+
isLive: true
|
|
246
|
+
},
|
|
247
|
+
130: {
|
|
248
|
+
id: 130,
|
|
249
|
+
name: "unichain",
|
|
250
|
+
shortName: "UNI",
|
|
251
|
+
color: "#FF007A",
|
|
252
|
+
permit2: PERMIT2,
|
|
253
|
+
tvmExecutor: "0x3fC25b3Ae57514e839f901cB1f628F8557150C2b",
|
|
254
|
+
sandboxTvmExecutor: "0x0000000000000000000000000000000000000000",
|
|
255
|
+
tychoRouter: "0xFfA5ec2e444e4285108e4a17b82dA495c178427B",
|
|
256
|
+
explorer: "https://unichain.blockscout.com",
|
|
257
|
+
rpcUrl: "https://unichain-rpc.publicnode.com",
|
|
258
|
+
nativeSymbol: "ETH",
|
|
259
|
+
nativeDecimals: 18,
|
|
260
|
+
minNotionalUsd: 200,
|
|
261
|
+
isLive: false
|
|
262
|
+
},
|
|
263
|
+
42161: {
|
|
264
|
+
id: 42161,
|
|
265
|
+
name: "arbitrum",
|
|
266
|
+
shortName: "ARB",
|
|
267
|
+
color: "#28A0F0",
|
|
268
|
+
permit2: PERMIT2,
|
|
269
|
+
tvmExecutor: "0xf57235609bf99fb0b017e95b3bc33a606a541374",
|
|
270
|
+
sandboxTvmExecutor: "0x9a4cd4ce4ca1dd554d337270b3353da9abb52c83",
|
|
271
|
+
tychoRouter: "0x924F147c50eA59f5180a26031A8b65B2aA1e81Cd",
|
|
272
|
+
explorer: "https://arbiscan.io",
|
|
273
|
+
rpcUrl: "https://rpc.orkidlabs.com/rpc/arbitrum",
|
|
274
|
+
nativeSymbol: "ETH",
|
|
275
|
+
nativeDecimals: 18,
|
|
276
|
+
minNotionalUsd: 50,
|
|
277
|
+
isLive: true
|
|
278
|
+
},
|
|
279
|
+
137: {
|
|
280
|
+
id: 137,
|
|
281
|
+
name: "polygon",
|
|
282
|
+
shortName: "POL",
|
|
283
|
+
color: "#8247E5",
|
|
284
|
+
permit2: PERMIT2,
|
|
285
|
+
tvmExecutor: "0xd633Ea84E6E2Db002C14C6fe3A064eE2cc4E258c",
|
|
286
|
+
sandboxTvmExecutor: "0x9a4cd4ce4ca1dd554d337270b3353da9abb52c83",
|
|
287
|
+
tychoRouter: "0xbd4e6011F03355C2A377Fd9Af939322A7d0A1bC1",
|
|
288
|
+
explorer: "https://polygonscan.com",
|
|
289
|
+
rpcUrl: "https://rpc.orkidlabs.com/rpc/polygon",
|
|
290
|
+
nativeSymbol: "MATIC",
|
|
291
|
+
nativeDecimals: 18,
|
|
292
|
+
minNotionalUsd: 50,
|
|
293
|
+
isLive: true
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var ORKID_CHAINS = ["base", "ethereum", "arbitrum", "polygon"];
|
|
297
|
+
function chainIdFromName(name) {
|
|
298
|
+
const normalized = normalizeChainName(name);
|
|
299
|
+
for (const config of Object.values(ORKID_CHAIN_CONFIG)) {
|
|
300
|
+
if (config.name === normalized) return config.id;
|
|
301
|
+
}
|
|
302
|
+
throw new Error(`Unsupported chain: ${name}`);
|
|
303
|
+
}
|
|
304
|
+
function chainNameFromId(chainId) {
|
|
305
|
+
const config = ORKID_CHAIN_CONFIG[chainId];
|
|
306
|
+
if (!config) throw new Error(`Unsupported chain id: ${chainId}`);
|
|
307
|
+
return config.name;
|
|
308
|
+
}
|
|
309
|
+
function chainConfigFromName(name) {
|
|
310
|
+
const normalized = normalizeChainName(name);
|
|
311
|
+
const config = Object.values(ORKID_CHAIN_CONFIG).find((c) => c.name === normalized);
|
|
312
|
+
if (!config) throw new Error(`Unsupported chain: ${name}`);
|
|
313
|
+
return config;
|
|
314
|
+
}
|
|
315
|
+
function normalizeChainName(name) {
|
|
316
|
+
const n = name.toLowerCase();
|
|
317
|
+
if (n === "mainnet") return "ethereum";
|
|
318
|
+
return n;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/permit.ts
|
|
322
|
+
var PERMIT_TRANSFER_FROM_TYPES = {
|
|
323
|
+
PermitTransferFrom: [
|
|
324
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
325
|
+
{ name: "spender", type: "address" },
|
|
326
|
+
{ name: "nonce", type: "uint256" },
|
|
327
|
+
{ name: "deadline", type: "uint256" }
|
|
328
|
+
],
|
|
329
|
+
TokenPermissions: [
|
|
330
|
+
{ name: "token", type: "address" },
|
|
331
|
+
{ name: "amount", type: "uint256" }
|
|
332
|
+
]
|
|
333
|
+
};
|
|
334
|
+
function buildPermit2Domain(chainId) {
|
|
335
|
+
return {
|
|
336
|
+
name: "Permit2",
|
|
337
|
+
chainId,
|
|
338
|
+
verifyingContract: getPermit2Address()
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function buildPermitMessage(params) {
|
|
342
|
+
return {
|
|
343
|
+
permitted: {
|
|
344
|
+
token: params.token,
|
|
345
|
+
amount: params.amount
|
|
346
|
+
},
|
|
347
|
+
spender: params.spender,
|
|
348
|
+
nonce: params.nonce,
|
|
349
|
+
deadline: params.deadline
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function toOrkidPermit(message) {
|
|
353
|
+
return {
|
|
354
|
+
permitted: message.permitted,
|
|
355
|
+
nonce: message.nonce,
|
|
356
|
+
deadline: message.deadline
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function parseAmount(value, decimals) {
|
|
360
|
+
const normalized = value.trim();
|
|
361
|
+
if (!/^\d+(\.\d+)?$/.test(normalized)) {
|
|
362
|
+
throw new Error(`Invalid amount: ${value}`);
|
|
363
|
+
}
|
|
364
|
+
const [whole, fraction = ""] = normalized.split(".");
|
|
365
|
+
if (fraction.length > decimals) {
|
|
366
|
+
throw new Error(`Amount ${value} exceeds ${decimals} decimals`);
|
|
367
|
+
}
|
|
368
|
+
const scale = BigInt("1" + "0".repeat(decimals));
|
|
369
|
+
const wholeBig = BigInt(whole) * scale;
|
|
370
|
+
const fractionBig = BigInt(fraction.padEnd(decimals, "0") || "0");
|
|
371
|
+
return (wholeBig + fractionBig).toString();
|
|
372
|
+
}
|
|
373
|
+
function formatAmount(raw, decimals) {
|
|
374
|
+
const rawBig = BigInt(raw);
|
|
375
|
+
const scale = BigInt("1" + "0".repeat(decimals));
|
|
376
|
+
const whole = (rawBig / scale).toString();
|
|
377
|
+
const fraction = (rawBig % scale).toString().padStart(decimals, "0");
|
|
378
|
+
const trimmed = fraction.replace(/0+$/, "");
|
|
379
|
+
return trimmed ? `${whole}.${trimmed}` : whole;
|
|
380
|
+
}
|
|
381
|
+
function buildSwapPermit(chainName, token, amount, nonce, deadlineSeconds) {
|
|
382
|
+
const chainConfig = chainConfigFromName(chainName);
|
|
383
|
+
const message = buildPermitMessage({
|
|
384
|
+
token,
|
|
385
|
+
amount,
|
|
386
|
+
nonce,
|
|
387
|
+
deadline: String(deadlineSeconds),
|
|
388
|
+
spender: chainConfig.tvmExecutor
|
|
389
|
+
});
|
|
390
|
+
const permit = toOrkidPermit(message);
|
|
391
|
+
return { message, permit, chainConfig };
|
|
392
|
+
}
|
|
393
|
+
function defaultDeadline() {
|
|
394
|
+
return Math.floor(Date.now() / 1e3) + 3600;
|
|
395
|
+
}
|
|
396
|
+
function buildNonce(wordPos, bitPos) {
|
|
397
|
+
return (wordPos << BigInt(8) | BigInt(bitPos)).toString();
|
|
398
|
+
}
|
|
399
|
+
function randomStartWord() {
|
|
400
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
401
|
+
const arr = new Uint32Array(1);
|
|
402
|
+
crypto.getRandomValues(arr);
|
|
403
|
+
return BigInt(arr[0] >>> 8);
|
|
404
|
+
}
|
|
405
|
+
return BigInt(Math.floor(Math.random() * 16777215));
|
|
406
|
+
}
|
|
407
|
+
var PERMIT2_ABI = [
|
|
408
|
+
{
|
|
409
|
+
type: "function",
|
|
410
|
+
name: "nonceBitmap",
|
|
411
|
+
inputs: [
|
|
412
|
+
{ name: "owner", type: "address" },
|
|
413
|
+
{ name: "wordPos", type: "uint256" }
|
|
414
|
+
],
|
|
415
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
416
|
+
stateMutability: "view"
|
|
417
|
+
}
|
|
418
|
+
];
|
|
419
|
+
|
|
420
|
+
// src/viem.ts
|
|
421
|
+
var OrkidViemPermitSigner = class {
|
|
422
|
+
walletClient;
|
|
423
|
+
publicClient;
|
|
424
|
+
constructor(walletClient, publicClient) {
|
|
425
|
+
this.walletClient = walletClient;
|
|
426
|
+
this.publicClient = publicClient;
|
|
427
|
+
}
|
|
428
|
+
async findUnusedNonce(owner, maxWords = 1e3) {
|
|
429
|
+
const startWord = randomStartWord2();
|
|
430
|
+
const max = BigInt(maxWords);
|
|
431
|
+
for (let offset = BigInt(0); offset < max; offset++) {
|
|
432
|
+
const wordPos = (startWord + offset) % BigInt(1e6);
|
|
433
|
+
const bitmap = await this.publicClient.readContract({
|
|
434
|
+
address: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
435
|
+
abi: PERMIT2_ABI,
|
|
436
|
+
functionName: "nonceBitmap",
|
|
437
|
+
args: [owner, wordPos]
|
|
438
|
+
});
|
|
439
|
+
const bitmapBig = BigInt(String(bitmap));
|
|
440
|
+
if (bitmapBig === (BigInt(1) << BigInt(256)) - BigInt(1)) continue;
|
|
441
|
+
for (let bitPos = 0; bitPos < 256; bitPos++) {
|
|
442
|
+
const bit = BigInt(1) << BigInt(bitPos);
|
|
443
|
+
if ((bitmapBig & bit) === BigInt(0)) {
|
|
444
|
+
return buildNonce(wordPos, bitPos);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
throw new Error("No unused Permit2 nonce found after scanning 1000 words");
|
|
449
|
+
}
|
|
450
|
+
async signSwap(params) {
|
|
451
|
+
const chainConfig = chainConfigFromName(params.chain);
|
|
452
|
+
const amountRaw = parseAmount(params.amount, params.fromDecimals);
|
|
453
|
+
const nonce = params.nonce || await this.findUnusedNonce(params.user);
|
|
454
|
+
const deadline = params.deadline || defaultDeadline();
|
|
455
|
+
const message = buildPermitMessage({
|
|
456
|
+
token: params.fromToken,
|
|
457
|
+
amount: amountRaw,
|
|
458
|
+
nonce,
|
|
459
|
+
deadline: String(deadline),
|
|
460
|
+
spender: chainConfig.tvmExecutor
|
|
461
|
+
});
|
|
462
|
+
const domain = buildPermit2Domain(chainConfig.id);
|
|
463
|
+
const account = this.walletClient.account ?? params.user;
|
|
464
|
+
const signature = await this.walletClient.signTypedData({
|
|
465
|
+
account,
|
|
466
|
+
domain,
|
|
467
|
+
types: {
|
|
468
|
+
PermitTransferFrom: [
|
|
469
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
470
|
+
{ name: "spender", type: "address" },
|
|
471
|
+
{ name: "nonce", type: "uint256" },
|
|
472
|
+
{ name: "deadline", type: "uint256" }
|
|
473
|
+
],
|
|
474
|
+
TokenPermissions: [
|
|
475
|
+
{ name: "token", type: "address" },
|
|
476
|
+
{ name: "amount", type: "uint256" }
|
|
477
|
+
]
|
|
478
|
+
},
|
|
479
|
+
primaryType: "PermitTransferFrom",
|
|
480
|
+
message: {
|
|
481
|
+
permitted: {
|
|
482
|
+
token: message.permitted.token,
|
|
483
|
+
amount: BigInt(message.permitted.amount)
|
|
484
|
+
},
|
|
485
|
+
spender: message.spender,
|
|
486
|
+
nonce: BigInt(message.nonce),
|
|
487
|
+
deadline: BigInt(message.deadline)
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
return {
|
|
491
|
+
permit: toOrkidPermit(message),
|
|
492
|
+
signature,
|
|
493
|
+
chainConfig
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Read the ERC20 allowance of the user for the Permit2 contract.
|
|
498
|
+
*/
|
|
499
|
+
async getPermit2Allowance(token, owner) {
|
|
500
|
+
const allowance = await this.publicClient.readContract({
|
|
501
|
+
address: token,
|
|
502
|
+
abi: [
|
|
503
|
+
{
|
|
504
|
+
type: "function",
|
|
505
|
+
name: "allowance",
|
|
506
|
+
inputs: [
|
|
507
|
+
{ name: "owner", type: "address" },
|
|
508
|
+
{ name: "spender", type: "address" }
|
|
509
|
+
],
|
|
510
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
511
|
+
stateMutability: "view"
|
|
512
|
+
}
|
|
513
|
+
],
|
|
514
|
+
functionName: "allowance",
|
|
515
|
+
args: [owner, "0x000000000022D473030F116dDEE9F6B43aC78BA3"]
|
|
516
|
+
});
|
|
517
|
+
return BigInt(String(allowance));
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
function randomStartWord2() {
|
|
521
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
522
|
+
const arr = new Uint32Array(1);
|
|
523
|
+
crypto.getRandomValues(arr);
|
|
524
|
+
return BigInt(arr[0] >>> 8);
|
|
525
|
+
}
|
|
526
|
+
return BigInt(Math.floor(Math.random() * 16777215));
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/ethers.ts
|
|
530
|
+
var import_ethers = require("ethers");
|
|
531
|
+
var OrkidEthersPermitSigner = class {
|
|
532
|
+
signer;
|
|
533
|
+
provider;
|
|
534
|
+
constructor(signer, provider) {
|
|
535
|
+
this.signer = signer;
|
|
536
|
+
this.provider = provider;
|
|
537
|
+
}
|
|
538
|
+
async findUnusedNonce(owner, maxWords = 1e3) {
|
|
539
|
+
const startWord = randomStartWord3();
|
|
540
|
+
const max = BigInt(maxWords);
|
|
541
|
+
const permit2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
542
|
+
const iface = new import_ethers.ethers.Interface([
|
|
543
|
+
"function nonceBitmap(address owner, uint256 wordPos) view returns (uint256)"
|
|
544
|
+
]);
|
|
545
|
+
for (let offset = BigInt(0); offset < max; offset++) {
|
|
546
|
+
const wordPos = (startWord + offset) % BigInt(1e6);
|
|
547
|
+
const data = iface.encodeFunctionData("nonceBitmap", [owner, wordPos]);
|
|
548
|
+
const result = await this.provider.call({ to: permit2, data });
|
|
549
|
+
const bitmap = BigInt(result);
|
|
550
|
+
const allBits = (BigInt(1) << BigInt(256)) - BigInt(1);
|
|
551
|
+
if (bitmap === allBits) continue;
|
|
552
|
+
for (let bitPos = 0; bitPos < 256; bitPos++) {
|
|
553
|
+
const bit = BigInt(1) << BigInt(bitPos);
|
|
554
|
+
if ((bitmap & bit) === BigInt(0)) {
|
|
555
|
+
return buildNonce(wordPos, bitPos);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
throw new Error("No unused Permit2 nonce found after scanning 1000 words");
|
|
560
|
+
}
|
|
561
|
+
async signSwap(params) {
|
|
562
|
+
const chainConfig = chainConfigFromName(params.chain);
|
|
563
|
+
const amountRaw = parseAmount(params.amount, params.fromDecimals);
|
|
564
|
+
const nonce = params.nonce || await this.findUnusedNonce(params.user);
|
|
565
|
+
const deadline = params.deadline || defaultDeadline();
|
|
566
|
+
const message = buildPermitMessage({
|
|
567
|
+
token: params.fromToken,
|
|
568
|
+
amount: amountRaw,
|
|
569
|
+
nonce,
|
|
570
|
+
deadline: String(deadline),
|
|
571
|
+
spender: chainConfig.tvmExecutor
|
|
572
|
+
});
|
|
573
|
+
const domain = buildPermit2Domain(chainConfig.id);
|
|
574
|
+
const types = {
|
|
575
|
+
PermitTransferFrom: [
|
|
576
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
577
|
+
{ name: "spender", type: "address" },
|
|
578
|
+
{ name: "nonce", type: "uint256" },
|
|
579
|
+
{ name: "deadline", type: "uint256" }
|
|
580
|
+
],
|
|
581
|
+
TokenPermissions: [
|
|
582
|
+
{ name: "token", type: "address" },
|
|
583
|
+
{ name: "amount", type: "uint256" }
|
|
584
|
+
]
|
|
585
|
+
};
|
|
586
|
+
const signature = await this.signer.signTypedData(
|
|
587
|
+
domain,
|
|
588
|
+
types,
|
|
589
|
+
message
|
|
590
|
+
);
|
|
591
|
+
return {
|
|
592
|
+
permit: toOrkidPermit(message),
|
|
593
|
+
signature,
|
|
594
|
+
chainConfig
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
async getPermit2Allowance(token, owner) {
|
|
598
|
+
const permit2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
599
|
+
const iface = new import_ethers.ethers.Interface([
|
|
600
|
+
"function allowance(address owner, address spender) view returns (uint256)"
|
|
601
|
+
]);
|
|
602
|
+
const data = iface.encodeFunctionData("allowance", [owner, permit2]);
|
|
603
|
+
const result = await this.provider.call({ to: token, data });
|
|
604
|
+
return BigInt(result);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
function randomStartWord3() {
|
|
608
|
+
if (typeof crypto !== "undefined" && "getRandomValues" in crypto) {
|
|
609
|
+
const arr = new Uint32Array(1);
|
|
610
|
+
crypto.getRandomValues(arr);
|
|
611
|
+
return BigInt(arr[0] >> 8);
|
|
612
|
+
}
|
|
613
|
+
return BigInt(Math.floor(Math.random() * 16777215));
|
|
614
|
+
}
|
|
615
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
616
|
+
0 && (module.exports = {
|
|
617
|
+
ORKID_CHAINS,
|
|
618
|
+
ORKID_CHAIN_CONFIG,
|
|
619
|
+
OrkidClient,
|
|
620
|
+
OrkidEthersPermitSigner,
|
|
621
|
+
OrkidViemPermitSigner,
|
|
622
|
+
PERMIT2,
|
|
623
|
+
PERMIT2_ABI,
|
|
624
|
+
PERMIT_TRANSFER_FROM_TYPES,
|
|
625
|
+
SANDBOX_BASE_URL,
|
|
626
|
+
buildNonce,
|
|
627
|
+
buildPermit2Domain,
|
|
628
|
+
buildPermitMessage,
|
|
629
|
+
buildSwapPermit,
|
|
630
|
+
chainConfigFromName,
|
|
631
|
+
chainIdFromName,
|
|
632
|
+
chainNameFromId,
|
|
633
|
+
defaultDeadline,
|
|
634
|
+
formatAmount,
|
|
635
|
+
getPermit2Address,
|
|
636
|
+
normalizeChainName,
|
|
637
|
+
parseAmount,
|
|
638
|
+
randomStartWord,
|
|
639
|
+
toOrkidPermit
|
|
640
|
+
});
|