infra-sdk-typescript 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/LICENSE +21 -0
- package/README.md +249 -0
- package/dist/index.cjs +908 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +912 -0
- package/dist/index.d.ts +912 -0
- package/dist/index.js +882 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
// src/core/version.ts
|
|
6
|
+
var SDK_VERSION = "0.1.0";
|
|
7
|
+
|
|
8
|
+
// src/core/errors.ts
|
|
9
|
+
var InfraError = class extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = new.target.name;
|
|
13
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var InfraConnectionError = class extends InfraError {
|
|
17
|
+
constructor(message, cause) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.cause = cause;
|
|
20
|
+
}
|
|
21
|
+
cause;
|
|
22
|
+
};
|
|
23
|
+
var InfraConnectionTimeoutError = class extends InfraConnectionError {
|
|
24
|
+
constructor(timeoutMs) {
|
|
25
|
+
super(`Request timed out after ${timeoutMs}ms`);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var InfraAPIError = class extends InfraError {
|
|
29
|
+
constructor(status, message, errors, body, headers) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.status = status;
|
|
32
|
+
this.errors = errors;
|
|
33
|
+
this.body = body;
|
|
34
|
+
this.headers = headers;
|
|
35
|
+
}
|
|
36
|
+
status;
|
|
37
|
+
errors;
|
|
38
|
+
body;
|
|
39
|
+
headers;
|
|
40
|
+
};
|
|
41
|
+
var BadRequestError = class extends InfraAPIError {
|
|
42
|
+
};
|
|
43
|
+
var AuthenticationError = class extends InfraAPIError {
|
|
44
|
+
};
|
|
45
|
+
var PaymentRequiredError = class extends InfraAPIError {
|
|
46
|
+
};
|
|
47
|
+
var PermissionDeniedError = class extends InfraAPIError {
|
|
48
|
+
};
|
|
49
|
+
var NotFoundError = class extends InfraAPIError {
|
|
50
|
+
};
|
|
51
|
+
var ConflictError = class extends InfraAPIError {
|
|
52
|
+
};
|
|
53
|
+
var UnprocessableEntityError = class extends InfraAPIError {
|
|
54
|
+
};
|
|
55
|
+
var RateLimitError = class extends InfraAPIError {
|
|
56
|
+
constructor(status, message, errors, body, headers, retryAfterSeconds) {
|
|
57
|
+
super(status, message, errors, body, headers);
|
|
58
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
59
|
+
}
|
|
60
|
+
retryAfterSeconds;
|
|
61
|
+
};
|
|
62
|
+
var InternalServerError = class extends InfraAPIError {
|
|
63
|
+
};
|
|
64
|
+
var ServiceUnavailableError = class extends InfraAPIError {
|
|
65
|
+
};
|
|
66
|
+
var UnknownAPIError = class extends InfraAPIError {
|
|
67
|
+
};
|
|
68
|
+
function errorFromResponse(status, parsedBody, headers) {
|
|
69
|
+
const body = parsedBody ?? {};
|
|
70
|
+
const message = typeof body.message === "string" ? body.message : `Request failed with status ${status}`;
|
|
71
|
+
const errors = Array.isArray(body.errors) ? body.errors.filter((e) => typeof e === "string") : void 0;
|
|
72
|
+
switch (status) {
|
|
73
|
+
case 400:
|
|
74
|
+
return new BadRequestError(status, message, errors, parsedBody, headers);
|
|
75
|
+
case 401:
|
|
76
|
+
return new AuthenticationError(status, message, errors, parsedBody, headers);
|
|
77
|
+
case 402:
|
|
78
|
+
return new PaymentRequiredError(status, message, errors, parsedBody, headers);
|
|
79
|
+
case 403:
|
|
80
|
+
return new PermissionDeniedError(status, message, errors, parsedBody, headers);
|
|
81
|
+
case 404:
|
|
82
|
+
return new NotFoundError(status, message, errors, parsedBody, headers);
|
|
83
|
+
case 409:
|
|
84
|
+
return new ConflictError(status, message, errors, parsedBody, headers);
|
|
85
|
+
case 422:
|
|
86
|
+
return new UnprocessableEntityError(status, message, errors, parsedBody, headers);
|
|
87
|
+
case 429: {
|
|
88
|
+
const bodyRetryAfter = typeof body.retryAfter === "number" ? body.retryAfter : void 0;
|
|
89
|
+
const headerRetryAfter = headers["retry-after"] !== void 0 ? Number(headers["retry-after"]) : void 0;
|
|
90
|
+
const retryAfterSeconds = bodyRetryAfter ?? (headerRetryAfter !== void 0 && Number.isFinite(headerRetryAfter) ? headerRetryAfter : void 0);
|
|
91
|
+
return new RateLimitError(status, message, errors, parsedBody, headers, retryAfterSeconds);
|
|
92
|
+
}
|
|
93
|
+
case 503:
|
|
94
|
+
return new ServiceUnavailableError(status, message, errors, parsedBody, headers);
|
|
95
|
+
default:
|
|
96
|
+
if (status >= 500) return new InternalServerError(status, message, errors, parsedBody, headers);
|
|
97
|
+
return new UnknownAPIError(status, message, errors, parsedBody, headers);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
var InfraAuthTokenError = class extends InfraError {
|
|
101
|
+
constructor(status, error, errorDescription) {
|
|
102
|
+
super(errorDescription ? `${error}: ${errorDescription}` : error);
|
|
103
|
+
this.status = status;
|
|
104
|
+
this.error = error;
|
|
105
|
+
this.errorDescription = errorDescription;
|
|
106
|
+
}
|
|
107
|
+
status;
|
|
108
|
+
error;
|
|
109
|
+
errorDescription;
|
|
110
|
+
};
|
|
111
|
+
var WebhookSignatureVerificationError = class extends InfraError {
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// src/core/http-client.ts
|
|
115
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
116
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
117
|
+
var HttpClient = class {
|
|
118
|
+
baseURL;
|
|
119
|
+
getAuthHeaders;
|
|
120
|
+
fetchImpl;
|
|
121
|
+
timeoutMs;
|
|
122
|
+
maxRetries;
|
|
123
|
+
defaultHeaders;
|
|
124
|
+
constructor(options) {
|
|
125
|
+
this.baseURL = options.baseURL;
|
|
126
|
+
this.getAuthHeaders = options.getAuthHeaders;
|
|
127
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
128
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
129
|
+
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
130
|
+
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
131
|
+
}
|
|
132
|
+
/** Issues a request against the Infra API and returns the unwrapped `data` field of the response envelope. */
|
|
133
|
+
async request(options) {
|
|
134
|
+
const url = buildUrl(this.baseURL, options.path, options.query);
|
|
135
|
+
const timeoutMs = options.timeoutMs ?? this.timeoutMs;
|
|
136
|
+
const retryableOnConnectionError = options.method === "GET" || Boolean(options.idempotencyKey);
|
|
137
|
+
let attempt = 0;
|
|
138
|
+
let lastError;
|
|
139
|
+
while (attempt <= this.maxRetries) {
|
|
140
|
+
attempt++;
|
|
141
|
+
const authHeaders = await this.getAuthHeaders();
|
|
142
|
+
const requestHeaders = {
|
|
143
|
+
Accept: "application/json",
|
|
144
|
+
"User-Agent": `infra-sdk-typescript/${SDK_VERSION}`,
|
|
145
|
+
...this.defaultHeaders,
|
|
146
|
+
...authHeaders,
|
|
147
|
+
...options.headers
|
|
148
|
+
};
|
|
149
|
+
if (options.body !== void 0) {
|
|
150
|
+
requestHeaders["Content-Type"] = "application/json";
|
|
151
|
+
}
|
|
152
|
+
if (options.idempotencyKey) {
|
|
153
|
+
requestHeaders["Idempotency-Key"] = options.idempotencyKey;
|
|
154
|
+
}
|
|
155
|
+
const controller = new AbortController();
|
|
156
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
157
|
+
const onCallerAbort = () => controller.abort();
|
|
158
|
+
if (options.signal) {
|
|
159
|
+
if (options.signal.aborted) controller.abort();
|
|
160
|
+
else options.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
161
|
+
}
|
|
162
|
+
let response;
|
|
163
|
+
try {
|
|
164
|
+
response = await this.fetchImpl(url, {
|
|
165
|
+
method: options.method,
|
|
166
|
+
headers: requestHeaders,
|
|
167
|
+
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
168
|
+
signal: controller.signal
|
|
169
|
+
});
|
|
170
|
+
} catch (err) {
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
options.signal?.removeEventListener("abort", onCallerAbort);
|
|
173
|
+
if (options.signal?.aborted) {
|
|
174
|
+
throw new InfraConnectionError("Request aborted by caller", err);
|
|
175
|
+
}
|
|
176
|
+
const connectionError = isAbortError(err) ? new InfraConnectionTimeoutError(timeoutMs) : new InfraConnectionError("Could not reach the Infra API", err);
|
|
177
|
+
if (!retryableOnConnectionError || attempt > this.maxRetries) {
|
|
178
|
+
throw connectionError;
|
|
179
|
+
}
|
|
180
|
+
lastError = connectionError;
|
|
181
|
+
await sleep(backoffDelayMs(attempt));
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
clearTimeout(timer);
|
|
185
|
+
options.signal?.removeEventListener("abort", onCallerAbort);
|
|
186
|
+
const responseHeaders = headersToObject(response.headers);
|
|
187
|
+
const parsedBody = await parseJsonSafely(response);
|
|
188
|
+
if (response.ok) {
|
|
189
|
+
const envelope = parsedBody;
|
|
190
|
+
return envelope?.data;
|
|
191
|
+
}
|
|
192
|
+
const apiError = errorFromResponse(response.status, parsedBody, responseHeaders);
|
|
193
|
+
const isRetryableStatus = apiError instanceof RateLimitError || apiError instanceof ServiceUnavailableError || apiError instanceof InternalServerError;
|
|
194
|
+
if (!isRetryableStatus || attempt > this.maxRetries) {
|
|
195
|
+
throw apiError;
|
|
196
|
+
}
|
|
197
|
+
lastError = apiError;
|
|
198
|
+
const delayMs = apiError instanceof RateLimitError && apiError.retryAfterSeconds !== void 0 ? apiError.retryAfterSeconds * 1e3 : backoffDelayMs(attempt);
|
|
199
|
+
await sleep(delayMs);
|
|
200
|
+
}
|
|
201
|
+
throw lastError;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
function buildUrl(baseURL, path, query) {
|
|
205
|
+
const base = baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
|
|
206
|
+
const url = new URL(path.replace(/^\//, ""), base);
|
|
207
|
+
if (query) {
|
|
208
|
+
for (const [key, value] of Object.entries(query)) {
|
|
209
|
+
if (value === void 0 || value === null) continue;
|
|
210
|
+
url.searchParams.set(key, String(value));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return url.toString();
|
|
214
|
+
}
|
|
215
|
+
function headersToObject(headers) {
|
|
216
|
+
const obj = {};
|
|
217
|
+
headers.forEach((value, key) => {
|
|
218
|
+
obj[key.toLowerCase()] = value;
|
|
219
|
+
});
|
|
220
|
+
return obj;
|
|
221
|
+
}
|
|
222
|
+
async function parseJsonSafely(response) {
|
|
223
|
+
const text = await response.text();
|
|
224
|
+
if (!text) return void 0;
|
|
225
|
+
try {
|
|
226
|
+
return JSON.parse(text);
|
|
227
|
+
} catch {
|
|
228
|
+
return void 0;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function isAbortError(err) {
|
|
232
|
+
return err instanceof Error && err.name === "AbortError";
|
|
233
|
+
}
|
|
234
|
+
function backoffDelayMs(attempt) {
|
|
235
|
+
const base = Math.min(500 * 2 ** (attempt - 1), 8e3);
|
|
236
|
+
const jitter = Math.random() * 250;
|
|
237
|
+
return base + jitter;
|
|
238
|
+
}
|
|
239
|
+
function sleep(ms) {
|
|
240
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/core/auth.ts
|
|
244
|
+
var TokenManager = class {
|
|
245
|
+
apiKey;
|
|
246
|
+
baseURL;
|
|
247
|
+
scope;
|
|
248
|
+
fetchImpl;
|
|
249
|
+
clockSkewSeconds;
|
|
250
|
+
maxRetries;
|
|
251
|
+
cached = null;
|
|
252
|
+
pendingRefresh = null;
|
|
253
|
+
constructor(options) {
|
|
254
|
+
this.apiKey = options.apiKey;
|
|
255
|
+
this.baseURL = options.baseURL;
|
|
256
|
+
this.scope = options.scope;
|
|
257
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
258
|
+
this.clockSkewSeconds = options.clockSkewSeconds ?? 30;
|
|
259
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
260
|
+
}
|
|
261
|
+
/** Returns a valid bearer token, minting or refreshing one only when necessary. */
|
|
262
|
+
async getAccessToken(opts = {}) {
|
|
263
|
+
if (!opts.forceRefresh && this.cached && Date.now() < this.cached.expiresAtMs) {
|
|
264
|
+
return this.cached.accessToken;
|
|
265
|
+
}
|
|
266
|
+
if (this.pendingRefresh) {
|
|
267
|
+
return this.pendingRefresh;
|
|
268
|
+
}
|
|
269
|
+
this.pendingRefresh = this.fetchToken().finally(() => {
|
|
270
|
+
this.pendingRefresh = null;
|
|
271
|
+
});
|
|
272
|
+
return this.pendingRefresh;
|
|
273
|
+
}
|
|
274
|
+
async fetchToken() {
|
|
275
|
+
let attempt = 0;
|
|
276
|
+
for (; ; ) {
|
|
277
|
+
attempt++;
|
|
278
|
+
let response;
|
|
279
|
+
try {
|
|
280
|
+
response = await this.fetchImpl(new URL("/v1/oauth/token", this.baseURL), {
|
|
281
|
+
method: "POST",
|
|
282
|
+
headers: {
|
|
283
|
+
"Content-Type": "application/json",
|
|
284
|
+
Accept: "application/json",
|
|
285
|
+
"User-Agent": `infra-sdk-typescript/${SDK_VERSION}`
|
|
286
|
+
},
|
|
287
|
+
body: JSON.stringify({
|
|
288
|
+
grant_type: "client_credentials",
|
|
289
|
+
client_secret: this.apiKey,
|
|
290
|
+
...this.scope ? { scope: this.scope } : {}
|
|
291
|
+
})
|
|
292
|
+
});
|
|
293
|
+
} catch (err) {
|
|
294
|
+
throw new InfraConnectionError("Could not reach the Infra API to obtain an access token", err);
|
|
295
|
+
}
|
|
296
|
+
const text = await response.text();
|
|
297
|
+
const parsed = text ? JSON.parse(text) : void 0;
|
|
298
|
+
if (response.ok && parsed && "access_token" in parsed) {
|
|
299
|
+
const expiresAtMs = Date.now() + Math.max(0, parsed.expires_in - this.clockSkewSeconds) * 1e3;
|
|
300
|
+
this.cached = { accessToken: parsed.access_token, expiresAtMs };
|
|
301
|
+
return parsed.access_token;
|
|
302
|
+
}
|
|
303
|
+
const errorBody = parsed ?? { error: "server_error" };
|
|
304
|
+
const retryable = response.status === 429 || response.status === 503;
|
|
305
|
+
if (retryable && attempt <= this.maxRetries) {
|
|
306
|
+
const delayMs = typeof errorBody.retryAfter === "number" ? errorBody.retryAfter * 1e3 : Math.min(500 * 2 ** (attempt - 1), 4e3);
|
|
307
|
+
await sleep2(delayMs);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const fallbackDescription = parsed?.message;
|
|
311
|
+
throw new InfraAuthTokenError(
|
|
312
|
+
response.status,
|
|
313
|
+
errorBody.error ?? (response.status === 429 ? "rate_limited" : "server_error"),
|
|
314
|
+
errorBody.error_description ?? fallbackDescription
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/** Drops the cached token, forcing the next call to mint a fresh one. */
|
|
319
|
+
clearCache() {
|
|
320
|
+
this.cached = null;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
function sleep2(ms) {
|
|
324
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/resources/resource.ts
|
|
328
|
+
var APIResource = class {
|
|
329
|
+
constructor(client) {
|
|
330
|
+
this.client = client;
|
|
331
|
+
}
|
|
332
|
+
client;
|
|
333
|
+
};
|
|
334
|
+
function generateIdempotencyKey() {
|
|
335
|
+
return crypto.randomUUID();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/core/pagination.ts
|
|
339
|
+
var Page = class {
|
|
340
|
+
data;
|
|
341
|
+
nextParams;
|
|
342
|
+
fetchNext;
|
|
343
|
+
constructor(opts) {
|
|
344
|
+
this.data = opts.data;
|
|
345
|
+
this.nextParams = opts.nextParams;
|
|
346
|
+
this.fetchNext = opts.fetchNext;
|
|
347
|
+
}
|
|
348
|
+
hasNextPage() {
|
|
349
|
+
return this.nextParams !== null && this.fetchNext !== null;
|
|
350
|
+
}
|
|
351
|
+
async getNextPage() {
|
|
352
|
+
if (!this.fetchNext || !this.nextParams) {
|
|
353
|
+
throw new Error("This is the last page \u2014 check hasNextPage() before calling getNextPage().");
|
|
354
|
+
}
|
|
355
|
+
return this.fetchNext(this.nextParams);
|
|
356
|
+
}
|
|
357
|
+
/** Walks every page starting with this one, yielding each `Page<T>` in turn. */
|
|
358
|
+
async *iterPages() {
|
|
359
|
+
let page = this;
|
|
360
|
+
yield page;
|
|
361
|
+
while (page.hasNextPage()) {
|
|
362
|
+
page = await page.getNextPage();
|
|
363
|
+
yield page;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/** Yields every item across all pages, fetching subsequent pages lazily as iteration proceeds. */
|
|
367
|
+
async *[Symbol.asyncIterator]() {
|
|
368
|
+
for await (const page of this.iterPages()) {
|
|
369
|
+
yield* page.data;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/** Eagerly fetches every remaining page and returns all items as a single array. Use with caution on large collections. */
|
|
373
|
+
async toArray() {
|
|
374
|
+
const items = [];
|
|
375
|
+
for await (const item of this) {
|
|
376
|
+
items.push(item);
|
|
377
|
+
}
|
|
378
|
+
return items;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
function pageFromOffsetResponse(raw, fetchNext) {
|
|
382
|
+
const hasNext = raw.pagination.hasMore && raw.pagination.nextOffset !== null;
|
|
383
|
+
return new Page({
|
|
384
|
+
data: raw.items,
|
|
385
|
+
nextParams: hasNext ? { offset: raw.pagination.nextOffset, limit: raw.pagination.limit } : null,
|
|
386
|
+
fetchNext: hasNext ? fetchNext : null
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
function pageFromFlatResponse(raw, fetchNext) {
|
|
390
|
+
const itemsSeenSoFar = (raw.page - 1) * raw.pageSize + raw.items.length;
|
|
391
|
+
const hasNext = itemsSeenSoFar < raw.total;
|
|
392
|
+
return new Page({
|
|
393
|
+
data: raw.items,
|
|
394
|
+
nextParams: hasNext ? { page: raw.page + 1, pageSize: raw.pageSize } : null,
|
|
395
|
+
fetchNext: hasNext ? fetchNext : null
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
function pageFromMetaResponse(raw, fetchNext) {
|
|
399
|
+
const hasNext = raw.meta.page < raw.meta.totalPages;
|
|
400
|
+
return new Page({
|
|
401
|
+
data: raw.data,
|
|
402
|
+
nextParams: hasNext ? { page: raw.meta.page + 1, limit: raw.meta.limit } : null,
|
|
403
|
+
fetchNext: hasNext ? fetchNext : null
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// src/resources/wallet/index.ts
|
|
408
|
+
var WalletResource = class extends APIResource {
|
|
409
|
+
/** Derives one or more new addresses on the business HD tree. Requires `wallet.hd.generate`. */
|
|
410
|
+
async generateWallets(params, overrides = {}) {
|
|
411
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
412
|
+
return this.client.request({
|
|
413
|
+
method: "POST",
|
|
414
|
+
path: "/v1/wallet/hd/generate",
|
|
415
|
+
body: params,
|
|
416
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
417
|
+
...rest
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Lists derived wallets for the business. Requires `wallet.hd.read`.
|
|
422
|
+
*
|
|
423
|
+
* Note: unlike {@link getTransactionHistory}, the API does not return pagination
|
|
424
|
+
* metadata for this endpoint — it always answers with a plain array sized by `limit`.
|
|
425
|
+
*/
|
|
426
|
+
async listWallets(params = {}, overrides = {}) {
|
|
427
|
+
return this.client.request({
|
|
428
|
+
method: "GET",
|
|
429
|
+
path: "/v1/wallet/hd",
|
|
430
|
+
query: params,
|
|
431
|
+
...overrides
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/** Returns the number of derived wallets, optionally filtered by chain. Requires `wallet.hd.read`. */
|
|
435
|
+
async getWalletCount(params = {}, overrides = {}) {
|
|
436
|
+
return this.client.request({
|
|
437
|
+
method: "GET",
|
|
438
|
+
path: "/v1/wallet/hd/count",
|
|
439
|
+
query: params,
|
|
440
|
+
...overrides
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
/** Reports whether HD wallets are enabled for the business. Requires `wallet.hd.read`. */
|
|
444
|
+
async getHdEnabled(overrides = {}) {
|
|
445
|
+
return this.client.request({
|
|
446
|
+
method: "GET",
|
|
447
|
+
path: "/v1/wallet/hd/enabled",
|
|
448
|
+
...overrides
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
/** Fetches a single wallet by its on-chain address. Requires `wallet.hd.read`. */
|
|
452
|
+
async getWalletByAddress(address, params = {}, overrides = {}) {
|
|
453
|
+
return this.client.request({
|
|
454
|
+
method: "GET",
|
|
455
|
+
path: `/v1/wallet/hd/address/${encodeURIComponent(address)}`,
|
|
456
|
+
query: params,
|
|
457
|
+
...overrides
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
/** Fetches a wallet by its HD `addressIndex`. Requires `wallet.hd.read`. */
|
|
461
|
+
async getWalletByAddressIndex(addressIndex, params = {}, overrides = {}) {
|
|
462
|
+
return this.client.request({
|
|
463
|
+
method: "GET",
|
|
464
|
+
path: `/v1/wallet/hd/address-index/${encodeURIComponent(String(addressIndex))}`,
|
|
465
|
+
query: params,
|
|
466
|
+
...overrides
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Signs (and, where applicable, sponsors and submits) a transaction from a derived
|
|
471
|
+
* wallet. Requires `wallet.hd.sign`.
|
|
472
|
+
*
|
|
473
|
+
* Returns a {@link SignedTransactionResult} for local-only signing (a non-EVM chain
|
|
474
|
+
* with `addressIndex` — nothing broadcast), or a {@link WalletTransaction} for a
|
|
475
|
+
* sponsored sign (async `PENDING` by default, or the settled result with
|
|
476
|
+
* `waitForCompletion: true`).
|
|
477
|
+
*/
|
|
478
|
+
async signTransaction(params, overrides = {}) {
|
|
479
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
480
|
+
return this.client.request({
|
|
481
|
+
method: "POST",
|
|
482
|
+
path: "/v1/wallet/hd/sign",
|
|
483
|
+
body: params,
|
|
484
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
485
|
+
...rest
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
/** Deactivates a wallet so it is no longer used for new operations. Requires `wallet.hd.deactivate`. */
|
|
489
|
+
async deactivateWallet(walletId, params = {}, overrides = {}) {
|
|
490
|
+
return this.client.request({
|
|
491
|
+
method: "POST",
|
|
492
|
+
path: `/v1/wallet/hd/deactivate/${encodeURIComponent(walletId)}`,
|
|
493
|
+
query: params,
|
|
494
|
+
...overrides
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
/** Lists the chains the platform can sign and broadcast on. */
|
|
498
|
+
async getSupportedChains(overrides = {}) {
|
|
499
|
+
return this.client.request({
|
|
500
|
+
method: "GET",
|
|
501
|
+
path: "/v1/wallet/rpc/chains",
|
|
502
|
+
...overrides
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Submits a token (or native) transfer from a derived wallet. Requires
|
|
507
|
+
* `wallet.transfer.create`.
|
|
508
|
+
*
|
|
509
|
+
* On a `422` (transfer reverted after broadcast, `waitForCompletion: true` only), the
|
|
510
|
+
* thrown {@link UnprocessableEntityError} still carries the failed transaction — read it
|
|
511
|
+
* from `error.body` (shaped like `{ data: WalletTransaction }`) if you need the details
|
|
512
|
+
* rather than just the message.
|
|
513
|
+
*/
|
|
514
|
+
async initiateTransfer(params, overrides = {}) {
|
|
515
|
+
const { idempotencyKey, ...rest } = overrides;
|
|
516
|
+
return this.client.request({
|
|
517
|
+
method: "POST",
|
|
518
|
+
path: "/v1/wallet/transaction",
|
|
519
|
+
body: params,
|
|
520
|
+
idempotencyKey: idempotencyKey ?? generateIdempotencyKey(),
|
|
521
|
+
...rest
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Returns a paginated history of the business's transactions. Requires
|
|
526
|
+
* `wallet.transfer.read`.
|
|
527
|
+
*
|
|
528
|
+
* ```ts
|
|
529
|
+
* const page = await client.wallet.getTransactionHistory({ status: "COMPLETED" });
|
|
530
|
+
* for await (const tx of page) {
|
|
531
|
+
* console.log(tx.id, tx.status);
|
|
532
|
+
* }
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
async getTransactionHistory(params = {}, overrides = {}) {
|
|
536
|
+
const fetchPage = (query) => this.client.request({
|
|
537
|
+
method: "GET",
|
|
538
|
+
path: "/v1/wallet/transaction/history",
|
|
539
|
+
query: { ...params, ...query },
|
|
540
|
+
...overrides
|
|
541
|
+
}).then((raw) => pageFromOffsetResponse(raw, fetchPage));
|
|
542
|
+
return fetchPage({ limit: params.limit, offset: params.offset });
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Fetches a single transaction record — the authoritative view of a transfer's outcome.
|
|
546
|
+
* Poll this after an async `202` until `status` is `COMPLETED` or `FAILED`. Requires
|
|
547
|
+
* `wallet.transfer.read`.
|
|
548
|
+
*/
|
|
549
|
+
async getTransaction(id, overrides = {}) {
|
|
550
|
+
return this.client.request({
|
|
551
|
+
method: "GET",
|
|
552
|
+
path: `/v1/wallet/transaction/${encodeURIComponent(id)}`,
|
|
553
|
+
...overrides
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
/** Returns the business's spendable USD balance, used to pay gas on mainnet transfers. Requires `wallet.balance.read`. */
|
|
557
|
+
async getBalance(overrides = {}) {
|
|
558
|
+
return this.client.request({
|
|
559
|
+
method: "GET",
|
|
560
|
+
path: "/v1/wallet/balance",
|
|
561
|
+
...overrides
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// src/resources/tokenization/index.ts
|
|
567
|
+
var TokenizationResource = class extends APIResource {
|
|
568
|
+
/**
|
|
569
|
+
* Issues a new tokenized asset and deploys it on the target chain. Returns immediately
|
|
570
|
+
* with a `PENDING` token plus a deployment `operationRef` and `txHash` — poll
|
|
571
|
+
* {@link getToken} until `status` is `ACTIVE`. Supply `yieldParams` for a
|
|
572
|
+
* `YIELD_BEARING` token.
|
|
573
|
+
*/
|
|
574
|
+
async createToken(params, overrides = {}) {
|
|
575
|
+
return this.client.request({
|
|
576
|
+
method: "POST",
|
|
577
|
+
path: "/v1/tokens",
|
|
578
|
+
body: params,
|
|
579
|
+
...overrides
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Returns a paginated list of tokens for the calling project.
|
|
584
|
+
*
|
|
585
|
+
* ```ts
|
|
586
|
+
* const page = await client.tokenization.listTokens();
|
|
587
|
+
* for await (const token of page) console.log(token.ticker, token.status);
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
async listTokens(params = {}, overrides = {}) {
|
|
591
|
+
const fetchPage = (query) => this.client.request({
|
|
592
|
+
method: "GET",
|
|
593
|
+
path: "/v1/tokens",
|
|
594
|
+
query: { ...params, ...query },
|
|
595
|
+
...overrides
|
|
596
|
+
}).then((raw) => pageFromFlatResponse(raw, fetchPage));
|
|
597
|
+
return fetchPage({ page: params.page, pageSize: params.pageSize });
|
|
598
|
+
}
|
|
599
|
+
/** Fetches a single token by id, including its per-chain deployment details. */
|
|
600
|
+
async getToken(id, overrides = {}) {
|
|
601
|
+
return this.client.request({
|
|
602
|
+
method: "GET",
|
|
603
|
+
path: `/v1/tokens/${encodeURIComponent(id)}`,
|
|
604
|
+
...overrides
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
/** Mints new units of a token to a recipient address. */
|
|
608
|
+
async mintToken(params, overrides = {}) {
|
|
609
|
+
return this.client.request({
|
|
610
|
+
method: "POST",
|
|
611
|
+
path: "/v1/tokens/mint",
|
|
612
|
+
body: params,
|
|
613
|
+
...overrides
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
/** Burns units of a token from a holder address. */
|
|
617
|
+
async burnToken(params, overrides = {}) {
|
|
618
|
+
return this.client.request({
|
|
619
|
+
method: "POST",
|
|
620
|
+
path: "/v1/tokens/burn",
|
|
621
|
+
body: params,
|
|
622
|
+
...overrides
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
/** Transfers token units between holder addresses. */
|
|
626
|
+
async transferToken(params, overrides = {}) {
|
|
627
|
+
return this.client.request({
|
|
628
|
+
method: "POST",
|
|
629
|
+
path: "/v1/tokens/transfer",
|
|
630
|
+
body: params,
|
|
631
|
+
...overrides
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
/** Registers (whitelists) a wallet address so it can hold a given token. */
|
|
635
|
+
async registerWallet(params, overrides = {}) {
|
|
636
|
+
return this.client.request({
|
|
637
|
+
method: "POST",
|
|
638
|
+
path: "/v1/tokens/register-wallet",
|
|
639
|
+
body: params,
|
|
640
|
+
...overrides
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
/** Returns the paginated list of holder wallet addresses and balances for a token on a chain. */
|
|
644
|
+
async getTokenHolders(params, overrides = {}) {
|
|
645
|
+
const fetchPage = (query) => this.client.request({
|
|
646
|
+
method: "GET",
|
|
647
|
+
path: "/v1/tokens/holders",
|
|
648
|
+
query: { ...params, ...query },
|
|
649
|
+
...overrides
|
|
650
|
+
}).then((raw) => pageFromFlatResponse(raw, fetchPage));
|
|
651
|
+
return fetchPage({ page: params.page, pageSize: params.pageSize });
|
|
652
|
+
}
|
|
653
|
+
/** Fetches a single token transaction by its reference. */
|
|
654
|
+
async getTokenTransaction(ref, overrides = {}) {
|
|
655
|
+
return this.client.request({
|
|
656
|
+
method: "GET",
|
|
657
|
+
path: `/v1/tokens/transactions/${encodeURIComponent(ref)}`,
|
|
658
|
+
...overrides
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
/** Returns token transactions, optionally filtered by token. */
|
|
662
|
+
async listTokenTransactions(params = {}, overrides = {}) {
|
|
663
|
+
const fetchPage = (query) => this.client.request({
|
|
664
|
+
method: "GET",
|
|
665
|
+
path: "/v1/tokens/transactions",
|
|
666
|
+
query: { ...params, ...query },
|
|
667
|
+
...overrides
|
|
668
|
+
}).then((raw) => pageFromFlatResponse(raw, fetchPage));
|
|
669
|
+
return fetchPage({ page: params.page, pageSize: params.pageSize });
|
|
670
|
+
}
|
|
671
|
+
/** Updates the annual yield rate for a yield-bearing token. */
|
|
672
|
+
async updateYield(params, overrides = {}) {
|
|
673
|
+
return this.client.request({
|
|
674
|
+
method: "PATCH",
|
|
675
|
+
path: "/v1/tokens/yield",
|
|
676
|
+
body: params,
|
|
677
|
+
...overrides
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
/** Funds and distributes a yield payout to holders. Unclaimed funds may be reclaimed after `reclaimAfter`. */
|
|
681
|
+
async distributeYield(params, overrides = {}) {
|
|
682
|
+
return this.client.request({
|
|
683
|
+
method: "POST",
|
|
684
|
+
path: "/v1/tokens/yield/distribute",
|
|
685
|
+
body: params,
|
|
686
|
+
...overrides
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
/** Pays the scheduled coupon for a yield-bearing token. Set `pushYield` to push funds rather than let investors claim. */
|
|
690
|
+
async payCoupon(params, overrides = {}) {
|
|
691
|
+
return this.client.request({
|
|
692
|
+
method: "POST",
|
|
693
|
+
path: "/v1/tokens/yield/pay-coupon",
|
|
694
|
+
body: params,
|
|
695
|
+
...overrides
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
/** Claims an investor's yield for a given distribution snapshot. */
|
|
699
|
+
async claimYield(params, overrides = {}) {
|
|
700
|
+
return this.client.request({
|
|
701
|
+
method: "POST",
|
|
702
|
+
path: "/v1/tokens/yield/claim",
|
|
703
|
+
body: params,
|
|
704
|
+
...overrides
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
/** Redeems principal to holders at maturity. Set `pushYield` to push funds rather than let investors claim. */
|
|
708
|
+
async redeemPrincipal(params, overrides = {}) {
|
|
709
|
+
return this.client.request({
|
|
710
|
+
method: "POST",
|
|
711
|
+
path: "/v1/tokens/yield/redeem",
|
|
712
|
+
body: params,
|
|
713
|
+
...overrides
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
// src/resources/indexer/index.ts
|
|
719
|
+
var IndexerResource = class extends APIResource {
|
|
720
|
+
/**
|
|
721
|
+
* Creates a subscription that streams decoded events for a contract to your webhook.
|
|
722
|
+
* Requires the network's subscribe capability (e.g. `indexer.subscribe.evm`).
|
|
723
|
+
*
|
|
724
|
+
* The response includes `signingSecret` only on creation — store it securely, it is
|
|
725
|
+
* never shown again (see {@link rotateSigningSecret} if you lose it).
|
|
726
|
+
*/
|
|
727
|
+
async createSubscription(params, overrides = {}) {
|
|
728
|
+
return this.client.request({
|
|
729
|
+
method: "POST",
|
|
730
|
+
path: "/v1/indexer/subscriptions",
|
|
731
|
+
body: params,
|
|
732
|
+
...overrides
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
/** Lists the project's subscriptions. Requires `indexer.subscription.list`. */
|
|
736
|
+
async listSubscriptions(params = {}, overrides = {}) {
|
|
737
|
+
const fetchPage = (query) => this.client.request({
|
|
738
|
+
method: "GET",
|
|
739
|
+
path: "/v1/indexer/subscriptions",
|
|
740
|
+
query: { ...params, ...query },
|
|
741
|
+
...overrides
|
|
742
|
+
}).then((raw) => pageFromMetaResponse(raw, fetchPage));
|
|
743
|
+
return fetchPage({ page: params.page, limit: params.limit });
|
|
744
|
+
}
|
|
745
|
+
/** Fetches one subscription. Requires `indexer.subscription.read`. */
|
|
746
|
+
async getSubscription(id, overrides = {}) {
|
|
747
|
+
return this.client.request({
|
|
748
|
+
method: "GET",
|
|
749
|
+
path: `/v1/indexer/subscriptions/${encodeURIComponent(id)}`,
|
|
750
|
+
...overrides
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
/** Updates a subscription's webhook, events, or status. At least one field is required. Requires `indexer.subscription.update`. */
|
|
754
|
+
async updateSubscription(id, params, overrides = {}) {
|
|
755
|
+
return this.client.request({
|
|
756
|
+
method: "PATCH",
|
|
757
|
+
path: `/v1/indexer/subscriptions/${encodeURIComponent(id)}`,
|
|
758
|
+
body: params,
|
|
759
|
+
...overrides
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
/** Soft-deletes a subscription (`status` becomes `"deleted"`; deliveries stop). Requires `indexer.subscription.delete`. */
|
|
763
|
+
async deleteSubscription(id, overrides = {}) {
|
|
764
|
+
return this.client.request({
|
|
765
|
+
method: "DELETE",
|
|
766
|
+
path: `/v1/indexer/subscriptions/${encodeURIComponent(id)}`,
|
|
767
|
+
...overrides
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Generates a new signing secret and invalidates the old one. Requires
|
|
772
|
+
* `indexer.subscription.rotate_secret`. The response includes the new `signingSecret`
|
|
773
|
+
* once — store it immediately.
|
|
774
|
+
*/
|
|
775
|
+
async rotateSigningSecret(id, overrides = {}) {
|
|
776
|
+
return this.client.request({
|
|
777
|
+
method: "POST",
|
|
778
|
+
path: `/v1/indexer/subscriptions/${encodeURIComponent(id)}/rotate-secret`,
|
|
779
|
+
...overrides
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
/** Returns the catalog of subscribable event keys. Requires `indexer.subscription.list`. */
|
|
783
|
+
async listEventCatalog(params = {}, overrides = {}) {
|
|
784
|
+
return this.client.request({
|
|
785
|
+
method: "GET",
|
|
786
|
+
path: "/v1/indexer/events",
|
|
787
|
+
query: params,
|
|
788
|
+
...overrides
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
// src/client.ts
|
|
794
|
+
var DEFAULT_BASE_URL = "https://api.withconvexity.com";
|
|
795
|
+
var InfraClient = class {
|
|
796
|
+
wallet;
|
|
797
|
+
tokenization;
|
|
798
|
+
indexer;
|
|
799
|
+
http;
|
|
800
|
+
auth;
|
|
801
|
+
constructor(options) {
|
|
802
|
+
if (!options.apiKey) {
|
|
803
|
+
throw new Error(
|
|
804
|
+
"InfraClient requires an `apiKey`. Request test/live keys from infra@withconvexity.com."
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
const baseURL = options.baseURL ?? DEFAULT_BASE_URL;
|
|
808
|
+
this.auth = new TokenManager({
|
|
809
|
+
apiKey: options.apiKey,
|
|
810
|
+
baseURL,
|
|
811
|
+
scope: options.scope,
|
|
812
|
+
fetch: options.fetch
|
|
813
|
+
});
|
|
814
|
+
this.http = new HttpClient({
|
|
815
|
+
baseURL,
|
|
816
|
+
fetch: options.fetch,
|
|
817
|
+
timeoutMs: options.timeoutMs,
|
|
818
|
+
maxRetries: options.maxRetries,
|
|
819
|
+
getAuthHeaders: async () => ({ Authorization: `Bearer ${await this.auth.getAccessToken()}` })
|
|
820
|
+
});
|
|
821
|
+
this.wallet = new WalletResource(this);
|
|
822
|
+
this.tokenization = new TokenizationResource(this);
|
|
823
|
+
this.indexer = new IndexerResource(this);
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Low-level escape hatch that issues a raw request against the Infra API using this
|
|
827
|
+
* client's auth, retry, and timeout configuration. The typed resource methods
|
|
828
|
+
* (`client.wallet.*`, `client.tokenization.*`, `client.indexer.*`) are built on top of
|
|
829
|
+
* this and should be preferred — reach for this only to call an endpoint this SDK
|
|
830
|
+
* version doesn't wrap yet.
|
|
831
|
+
*/
|
|
832
|
+
async request(options) {
|
|
833
|
+
try {
|
|
834
|
+
return await this.http.request(options);
|
|
835
|
+
} catch (err) {
|
|
836
|
+
if (err instanceof AuthenticationError) {
|
|
837
|
+
await this.auth.getAccessToken({ forceRefresh: true });
|
|
838
|
+
return await this.http.request(options);
|
|
839
|
+
}
|
|
840
|
+
throw err;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
var DEFAULT_TOLERANCE_SECONDS = 300;
|
|
845
|
+
function verifyWalletWebhookSignature(options) {
|
|
846
|
+
const { payload, timestampHeader, signatureHeader, secret, toleranceSeconds = DEFAULT_TOLERANCE_SECONDS } = options;
|
|
847
|
+
const timestamp = Number(timestampHeader);
|
|
848
|
+
if (!Number.isFinite(timestamp)) {
|
|
849
|
+
throw new WebhookSignatureVerificationError("Missing or invalid x-wallet-timestamp header");
|
|
850
|
+
}
|
|
851
|
+
const ageSeconds = Math.abs(Date.now() / 1e3 - timestamp);
|
|
852
|
+
if (ageSeconds > toleranceSeconds) {
|
|
853
|
+
throw new WebhookSignatureVerificationError(
|
|
854
|
+
`Webhook timestamp is ${Math.round(ageSeconds)}s old, outside the allowed tolerance of ${toleranceSeconds}s`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
const rawBody = typeof payload === "string" ? payload : payload.toString("utf8");
|
|
858
|
+
const expected = crypto.createHmac("sha256", secret).update(`${timestampHeader}.${rawBody}`).digest("hex");
|
|
859
|
+
if (!constantTimeEqual(expected, signatureHeader)) {
|
|
860
|
+
throw new WebhookSignatureVerificationError(
|
|
861
|
+
"Signature mismatch \u2014 the payload may have been tampered with, or the secret is incorrect"
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
function verifyIndexerWebhookSignature(options) {
|
|
866
|
+
const { payload, signatureHeader, secret } = options;
|
|
867
|
+
const rawBody = typeof payload === "string" ? payload : payload.toString("utf8");
|
|
868
|
+
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
|
|
869
|
+
if (!constantTimeEqual(expected, signatureHeader)) {
|
|
870
|
+
throw new WebhookSignatureVerificationError(
|
|
871
|
+
"Signature mismatch \u2014 the payload may have been tampered with, or the secret is incorrect"
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function constantTimeEqual(expectedHex, actualHex) {
|
|
876
|
+
const expected = Buffer.from(expectedHex, "utf8");
|
|
877
|
+
const actual = Buffer.from(actualHex, "utf8");
|
|
878
|
+
if (expected.length !== actual.length) return false;
|
|
879
|
+
return crypto.timingSafeEqual(expected, actual);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
exports.AuthenticationError = AuthenticationError;
|
|
883
|
+
exports.BadRequestError = BadRequestError;
|
|
884
|
+
exports.ConflictError = ConflictError;
|
|
885
|
+
exports.IndexerResource = IndexerResource;
|
|
886
|
+
exports.InfraAPIError = InfraAPIError;
|
|
887
|
+
exports.InfraAuthTokenError = InfraAuthTokenError;
|
|
888
|
+
exports.InfraClient = InfraClient;
|
|
889
|
+
exports.InfraConnectionError = InfraConnectionError;
|
|
890
|
+
exports.InfraConnectionTimeoutError = InfraConnectionTimeoutError;
|
|
891
|
+
exports.InfraError = InfraError;
|
|
892
|
+
exports.InternalServerError = InternalServerError;
|
|
893
|
+
exports.NotFoundError = NotFoundError;
|
|
894
|
+
exports.Page = Page;
|
|
895
|
+
exports.PaymentRequiredError = PaymentRequiredError;
|
|
896
|
+
exports.PermissionDeniedError = PermissionDeniedError;
|
|
897
|
+
exports.RateLimitError = RateLimitError;
|
|
898
|
+
exports.ServiceUnavailableError = ServiceUnavailableError;
|
|
899
|
+
exports.TokenizationResource = TokenizationResource;
|
|
900
|
+
exports.UnknownAPIError = UnknownAPIError;
|
|
901
|
+
exports.UnprocessableEntityError = UnprocessableEntityError;
|
|
902
|
+
exports.WalletResource = WalletResource;
|
|
903
|
+
exports.WebhookSignatureVerificationError = WebhookSignatureVerificationError;
|
|
904
|
+
exports.generateIdempotencyKey = generateIdempotencyKey;
|
|
905
|
+
exports.verifyIndexerWebhookSignature = verifyIndexerWebhookSignature;
|
|
906
|
+
exports.verifyWalletWebhookSignature = verifyWalletWebhookSignature;
|
|
907
|
+
//# sourceMappingURL=index.cjs.map
|
|
908
|
+
//# sourceMappingURL=index.cjs.map
|