@secondlayer/sdk 6.19.0 → 6.21.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 +61 -4
- package/dist/index.d.ts +281 -164
- package/dist/index.js +294 -169
- package/dist/index.js.map +10 -10
- package/dist/streams/index.d.ts +46 -1
- package/dist/streams/index.js +48 -1
- package/dist/streams/index.js.map +5 -5
- package/dist/subgraphs/index.d.ts +124 -158
- package/dist/subgraphs/index.js +70 -167
- package/dist/subgraphs/index.js.map +8 -9
- package/dist/x402.d.ts +159 -0
- package/dist/x402.js +230 -0
- package/dist/x402.js.map +10 -0
- package/package.json +8 -4
package/dist/index.js
CHANGED
|
@@ -281,6 +281,19 @@ class Subgraphs extends BaseClient {
|
|
|
281
281
|
const qs = options?.force ? "?force=true" : "";
|
|
282
282
|
return this.request("DELETE", `/api/subgraphs/${name}${qs}`);
|
|
283
283
|
}
|
|
284
|
+
async publish(name) {
|
|
285
|
+
return this.request("POST", `/api/subgraphs/${name}/publish`);
|
|
286
|
+
}
|
|
287
|
+
async unpublish(name) {
|
|
288
|
+
return this.request("POST", `/api/subgraphs/${name}/unpublish`);
|
|
289
|
+
}
|
|
290
|
+
async rows(name, table, params = {}) {
|
|
291
|
+
const { cursor, ...rest } = params;
|
|
292
|
+
const qs = buildSubgraphQueryString(rest);
|
|
293
|
+
const sep = qs ? "&" : "?";
|
|
294
|
+
const cursorQs = cursor ? `${sep}cursor=${encodeURIComponent(cursor)}` : "";
|
|
295
|
+
return this.request("GET", `/v1/subgraphs/${name}/${table}${qs}${cursorQs}`);
|
|
296
|
+
}
|
|
284
297
|
async operations(name) {
|
|
285
298
|
return this.request("GET", `/api/subgraphs/${name}/operations`);
|
|
286
299
|
}
|
|
@@ -429,164 +442,6 @@ class Contracts extends BaseClient {
|
|
|
429
442
|
}
|
|
430
443
|
}
|
|
431
444
|
|
|
432
|
-
// src/datasets/client.ts
|
|
433
|
-
var PARAM_KEYS = {
|
|
434
|
-
fromBlock: "from_block",
|
|
435
|
-
toBlock: "to_block",
|
|
436
|
-
functionName: "function_name",
|
|
437
|
-
delegateTo: "delegate_to",
|
|
438
|
-
signerKey: "signer_key",
|
|
439
|
-
rewardCycle: "reward_cycle",
|
|
440
|
-
eventType: "event_type",
|
|
441
|
-
bnsId: "bns_id"
|
|
442
|
-
};
|
|
443
|
-
var CURSOR_SLUGS = {
|
|
444
|
-
"stx-transfers": { path: "stx-transfers", rowKey: "events" },
|
|
445
|
-
"sbtc-events": { path: "sbtc/events", rowKey: "events" },
|
|
446
|
-
"sbtc-token-events": { path: "sbtc/token-events", rowKey: "events" },
|
|
447
|
-
"pox-4-calls": { path: "pox-4/calls", rowKey: "calls" },
|
|
448
|
-
"burnchain-rewards": { path: "burnchain/rewards", rowKey: "rewards" },
|
|
449
|
-
"burnchain-reward-slots": {
|
|
450
|
-
path: "burnchain/reward-slots",
|
|
451
|
-
rowKey: "slots"
|
|
452
|
-
},
|
|
453
|
-
"bns-events": { path: "bns/events", rowKey: "events" },
|
|
454
|
-
"bns-namespace-events": { path: "bns/namespace-events", rowKey: "events" },
|
|
455
|
-
"bns-marketplace-events": {
|
|
456
|
-
path: "bns/marketplace-events",
|
|
457
|
-
rowKey: "events"
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
function catalogPathTail(path) {
|
|
461
|
-
return path.replace(/^\/?v1\/datasets\//, "").replace(/^\/+/, "");
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
class Datasets extends BaseClient {
|
|
465
|
-
catalogPromise;
|
|
466
|
-
constructor(options = {}) {
|
|
467
|
-
super(options);
|
|
468
|
-
}
|
|
469
|
-
listDatasets() {
|
|
470
|
-
return this.request("GET", "/v1/datasets");
|
|
471
|
-
}
|
|
472
|
-
async get(slug, params = {}) {
|
|
473
|
-
const { path, rowKey } = await this.resolveDataset(slug);
|
|
474
|
-
const env = await this.requestPath(path, this.paramsToQuery(params));
|
|
475
|
-
const value = env[rowKey];
|
|
476
|
-
const rows = Array.isArray(value) ? value : value == null ? [] : [value];
|
|
477
|
-
return {
|
|
478
|
-
rows,
|
|
479
|
-
next_cursor: env.next_cursor ?? null,
|
|
480
|
-
tip: env.tip
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
async query(slug, params = {}) {
|
|
484
|
-
const d = CURSOR_SLUGS[slug];
|
|
485
|
-
if (!d) {
|
|
486
|
-
throw new Error(`unknown cursor dataset "${slug}" (use one of: ${Object.keys(CURSOR_SLUGS).join(", ")})`);
|
|
487
|
-
}
|
|
488
|
-
const env = await this.requestPath(d.path, this.paramsToQuery(params));
|
|
489
|
-
return {
|
|
490
|
-
rows: env[d.rowKey] ?? [],
|
|
491
|
-
next_cursor: env.next_cursor ?? null,
|
|
492
|
-
tip: env.tip
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
stxTransfers = this.cursorDataset("stx-transfers", "events");
|
|
496
|
-
sbtcEvents = this.cursorDataset("sbtc/events", "events");
|
|
497
|
-
sbtcTokenEvents = this.cursorDataset("sbtc/token-events", "events");
|
|
498
|
-
pox4Calls = this.cursorDataset("pox-4/calls", "calls");
|
|
499
|
-
burnchainRewards = this.cursorDataset("burnchain/rewards", "rewards");
|
|
500
|
-
burnchainRewardSlots = this.cursorDataset("burnchain/reward-slots", "slots");
|
|
501
|
-
bnsEvents = this.cursorDataset("bns/events", "events");
|
|
502
|
-
bnsNamespaceEvents = this.cursorDataset("bns/namespace-events", "events");
|
|
503
|
-
bnsMarketplaceEvents = this.cursorDataset("bns/marketplace-events", "events");
|
|
504
|
-
bnsNames(params = {}) {
|
|
505
|
-
return this.requestPath("bns/names", buildQuery({
|
|
506
|
-
namespace: params.namespace,
|
|
507
|
-
owner: params.owner,
|
|
508
|
-
limit: params.limit,
|
|
509
|
-
offset: params.offset
|
|
510
|
-
}));
|
|
511
|
-
}
|
|
512
|
-
bnsNamespaces() {
|
|
513
|
-
return this.requestPath("bns/namespaces", "");
|
|
514
|
-
}
|
|
515
|
-
bnsResolve(fqn) {
|
|
516
|
-
return this.requestPath("bns/resolve", buildQuery({ fqn }));
|
|
517
|
-
}
|
|
518
|
-
networkHealth() {
|
|
519
|
-
return this.requestPath("network-health/summary", "");
|
|
520
|
-
}
|
|
521
|
-
requestPath(path, query) {
|
|
522
|
-
return this.request("GET", `/v1/datasets/${path}${query}`);
|
|
523
|
-
}
|
|
524
|
-
async resolveDataset(slug) {
|
|
525
|
-
const cursor = CURSOR_SLUGS[slug];
|
|
526
|
-
if (cursor)
|
|
527
|
-
return { path: cursor.path, rowKey: cursor.rowKey };
|
|
528
|
-
const families = await this.loadCatalog();
|
|
529
|
-
const tail = catalogPathTail(slug);
|
|
530
|
-
const match = families.find((f) => f.family === slug || catalogPathTail(f.path) === tail);
|
|
531
|
-
if (!match) {
|
|
532
|
-
throw new Error(`unknown dataset "${slug}" (available: ${families.map((f) => f.family).join(", ")})`);
|
|
533
|
-
}
|
|
534
|
-
return { path: catalogPathTail(match.path), rowKey: match.row_key };
|
|
535
|
-
}
|
|
536
|
-
loadCatalog() {
|
|
537
|
-
this.catalogPromise ??= (async () => {
|
|
538
|
-
const raw = await this.listDatasets();
|
|
539
|
-
const families = raw.families;
|
|
540
|
-
if (!Array.isArray(families)) {
|
|
541
|
-
throw new Error("dataset catalog response missing families[]");
|
|
542
|
-
}
|
|
543
|
-
return families;
|
|
544
|
-
})();
|
|
545
|
-
return this.catalogPromise;
|
|
546
|
-
}
|
|
547
|
-
paramsToQuery(params) {
|
|
548
|
-
const mapped = {};
|
|
549
|
-
for (const [k, v] of Object.entries(params)) {
|
|
550
|
-
if (v === undefined || v === null || k === "batchSize" || k === "signal")
|
|
551
|
-
continue;
|
|
552
|
-
mapped[PARAM_KEYS[k] ?? k] = v;
|
|
553
|
-
}
|
|
554
|
-
return buildQuery(mapped);
|
|
555
|
-
}
|
|
556
|
-
cursorDataset(path, rowKey) {
|
|
557
|
-
const list = async (params = {}) => {
|
|
558
|
-
const envelope = await this.requestPath(path, this.paramsToQuery(params));
|
|
559
|
-
return {
|
|
560
|
-
rows: envelope[rowKey] ?? [],
|
|
561
|
-
next_cursor: envelope.next_cursor ?? null,
|
|
562
|
-
tip: envelope.tip
|
|
563
|
-
};
|
|
564
|
-
};
|
|
565
|
-
const walk = async function* (params = {}) {
|
|
566
|
-
const batchSize = params.batchSize ?? 200;
|
|
567
|
-
let cursor = params.cursor ?? null;
|
|
568
|
-
let first = true;
|
|
569
|
-
while (!params.signal?.aborted) {
|
|
570
|
-
const env = await list({
|
|
571
|
-
...params,
|
|
572
|
-
limit: batchSize,
|
|
573
|
-
cursor: first ? params.cursor : cursor ?? undefined
|
|
574
|
-
});
|
|
575
|
-
for (const row of env.rows) {
|
|
576
|
-
if (params.signal?.aborted)
|
|
577
|
-
return;
|
|
578
|
-
yield row;
|
|
579
|
-
}
|
|
580
|
-
if (!env.next_cursor || env.next_cursor === cursor || env.rows.length < batchSize)
|
|
581
|
-
return;
|
|
582
|
-
cursor = env.next_cursor;
|
|
583
|
-
first = false;
|
|
584
|
-
}
|
|
585
|
-
}.bind(this);
|
|
586
|
-
return { list, walk };
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
445
|
// src/index-api/client.ts
|
|
591
446
|
function firstWalkFromHeight(params) {
|
|
592
447
|
if (params.fromHeight !== undefined)
|
|
@@ -606,18 +461,18 @@ class Index extends BaseClient {
|
|
|
606
461
|
discover() {
|
|
607
462
|
return this.request("GET", "/v1/index");
|
|
608
463
|
}
|
|
609
|
-
ftTransfers = {
|
|
464
|
+
ftTransfers = Object.assign((params = {}) => this.listFtTransfers(params), {
|
|
610
465
|
list: (params = {}) => this.listFtTransfers(params),
|
|
611
466
|
walk: (params = {}) => this.walkFtTransfers(params)
|
|
612
|
-
};
|
|
613
|
-
nftTransfers = {
|
|
467
|
+
});
|
|
468
|
+
nftTransfers = Object.assign((params = {}) => this.listNftTransfers(params), {
|
|
614
469
|
list: (params = {}) => this.listNftTransfers(params),
|
|
615
470
|
walk: (params = {}) => this.walkNftTransfers(params)
|
|
616
|
-
};
|
|
617
|
-
events = {
|
|
471
|
+
});
|
|
472
|
+
events = Object.assign((params) => this.listEvents(params), {
|
|
618
473
|
list: (params) => this.listEvents(params),
|
|
619
474
|
walk: (params) => this.walkEvents(params)
|
|
620
|
-
};
|
|
475
|
+
});
|
|
621
476
|
contractCalls = {
|
|
622
477
|
list: (params = {}) => this.listContractCalls(params),
|
|
623
478
|
walk: (params = {}) => this.walkContractCalls(params)
|
|
@@ -1182,6 +1037,38 @@ async function consumeStreamsEvents(opts) {
|
|
|
1182
1037
|
}
|
|
1183
1038
|
return { cursor, pages, emptyPolls };
|
|
1184
1039
|
}
|
|
1040
|
+
async function* iterateStreamsBatches(opts) {
|
|
1041
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
1042
|
+
let cursor = opts.fromCursor ?? null;
|
|
1043
|
+
while (!opts.signal?.aborted) {
|
|
1044
|
+
const envelope = await opts.fetchEvents({
|
|
1045
|
+
cursor,
|
|
1046
|
+
limit: opts.batchSize,
|
|
1047
|
+
types: opts.types,
|
|
1048
|
+
notTypes: opts.notTypes,
|
|
1049
|
+
contractId: opts.contractId,
|
|
1050
|
+
sender: opts.sender,
|
|
1051
|
+
recipient: opts.recipient,
|
|
1052
|
+
assetIdentifier: opts.assetIdentifier
|
|
1053
|
+
});
|
|
1054
|
+
const checkpoint = envelope.next_cursor ?? cursor;
|
|
1055
|
+
if (envelope.events.length > 0 || envelope.reorgs.length > 0) {
|
|
1056
|
+
yield {
|
|
1057
|
+
events: envelope.events,
|
|
1058
|
+
cursor: checkpoint,
|
|
1059
|
+
tip: envelope.tip,
|
|
1060
|
+
reorgs: envelope.reorgs
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
const advanced = checkpoint !== null && checkpoint !== cursor;
|
|
1064
|
+
cursor = checkpoint;
|
|
1065
|
+
if (!advanced && envelope.events.length === 0) {
|
|
1066
|
+
if (opts.signal?.aborted)
|
|
1067
|
+
return;
|
|
1068
|
+
await sleep(opts.intervalMs, opts.signal);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1185
1072
|
async function* streamStreamsEvents(opts) {
|
|
1186
1073
|
const sleep = opts.sleep ?? defaultSleep;
|
|
1187
1074
|
const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
|
|
@@ -1559,6 +1446,21 @@ function createStreamsClient(options) {
|
|
|
1559
1446
|
})}`);
|
|
1560
1447
|
}
|
|
1561
1448
|
return {
|
|
1449
|
+
consume(params = {}) {
|
|
1450
|
+
return iterateStreamsBatches({
|
|
1451
|
+
fromCursor: params.cursor,
|
|
1452
|
+
batchSize: params.batchSize ?? 100,
|
|
1453
|
+
intervalMs: params.intervalMs ?? 2000,
|
|
1454
|
+
types: params.types,
|
|
1455
|
+
notTypes: params.notTypes,
|
|
1456
|
+
contractId: params.contractId,
|
|
1457
|
+
sender: params.sender,
|
|
1458
|
+
recipient: params.recipient,
|
|
1459
|
+
assetIdentifier: params.assetIdentifier,
|
|
1460
|
+
signal: params.signal,
|
|
1461
|
+
fetchEvents
|
|
1462
|
+
});
|
|
1463
|
+
},
|
|
1562
1464
|
events: {
|
|
1563
1465
|
list: listEvents,
|
|
1564
1466
|
byTxId(txId) {
|
|
@@ -1711,7 +1613,6 @@ class Subscriptions extends BaseClient {
|
|
|
1711
1613
|
class SecondLayer extends BaseClient {
|
|
1712
1614
|
streams;
|
|
1713
1615
|
index;
|
|
1714
|
-
datasets;
|
|
1715
1616
|
contracts;
|
|
1716
1617
|
subgraphs;
|
|
1717
1618
|
subscriptions;
|
|
@@ -1726,13 +1627,15 @@ class SecondLayer extends BaseClient {
|
|
|
1726
1627
|
dumpsBaseUrl: options.dumpsBaseUrl
|
|
1727
1628
|
});
|
|
1728
1629
|
this.index = new Index(options);
|
|
1729
|
-
this.datasets = new Datasets(options);
|
|
1730
1630
|
this.contracts = new Contracts(options);
|
|
1731
1631
|
this.subgraphs = new Subgraphs(options);
|
|
1732
1632
|
this.subscriptions = new Subscriptions(options);
|
|
1733
1633
|
this.apiKeys = new ApiKeys(options);
|
|
1734
1634
|
this.projects = new Projects(options);
|
|
1735
1635
|
}
|
|
1636
|
+
async batch(requests) {
|
|
1637
|
+
return this.request("POST", "/v1/batch", { requests });
|
|
1638
|
+
}
|
|
1736
1639
|
async context() {
|
|
1737
1640
|
const safe = (p) => p.then((v) => v).catch(() => null);
|
|
1738
1641
|
const [
|
|
@@ -2093,6 +1996,220 @@ function decodePrint(event) {
|
|
|
2093
1996
|
import {
|
|
2094
1997
|
STREAMS_EVENT_TYPES
|
|
2095
1998
|
} from "@secondlayer/shared";
|
|
1999
|
+
// src/x402.ts
|
|
2000
|
+
import {
|
|
2001
|
+
X402_TOKENS,
|
|
2002
|
+
findX402TokenByAsset
|
|
2003
|
+
} from "@secondlayer/shared/x402";
|
|
2004
|
+
import {
|
|
2005
|
+
serializeTransactionHex,
|
|
2006
|
+
signTransactionWithAccount
|
|
2007
|
+
} from "@secondlayer/stacks/transactions";
|
|
2008
|
+
import { buildExactTransfer } from "@secondlayer/stacks/x402";
|
|
2009
|
+
var DEFAULT_PREFER_ASSETS = [
|
|
2010
|
+
"sBTC",
|
|
2011
|
+
"USDCx",
|
|
2012
|
+
"STX"
|
|
2013
|
+
];
|
|
2014
|
+
var DEFAULT_NONCE_NODE_URL = "https://api.hiro.so";
|
|
2015
|
+
|
|
2016
|
+
class X402SpendGuardError extends Error {
|
|
2017
|
+
constructor(message) {
|
|
2018
|
+
super(message);
|
|
2019
|
+
this.name = "X402SpendGuardError";
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
function b64encode(value) {
|
|
2023
|
+
return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
|
|
2024
|
+
}
|
|
2025
|
+
function b64decodeJson(value) {
|
|
2026
|
+
try {
|
|
2027
|
+
return JSON.parse(Buffer.from(value, "base64").toString("utf8"));
|
|
2028
|
+
} catch {
|
|
2029
|
+
return null;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
async function readX402Challenge(res) {
|
|
2033
|
+
const header = res.headers.get("PAYMENT-REQUIRED");
|
|
2034
|
+
if (header) {
|
|
2035
|
+
const decoded = b64decodeJson(header);
|
|
2036
|
+
if (decoded)
|
|
2037
|
+
return decoded;
|
|
2038
|
+
}
|
|
2039
|
+
try {
|
|
2040
|
+
return await res.clone().json();
|
|
2041
|
+
} catch {
|
|
2042
|
+
return null;
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
function readX402Receipt(res) {
|
|
2046
|
+
const header = res.headers.get("PAYMENT-RESPONSE");
|
|
2047
|
+
return header ? b64decodeJson(header) : null;
|
|
2048
|
+
}
|
|
2049
|
+
function selectOffer(challenge, opts = {}) {
|
|
2050
|
+
const prefer = opts.preferAssets ?? DEFAULT_PREFER_ASSETS;
|
|
2051
|
+
for (const symbol of prefer) {
|
|
2052
|
+
const token = X402_TOKENS[symbol];
|
|
2053
|
+
const accept = challenge.accepts.find((a) => a.asset === token.asset);
|
|
2054
|
+
if (!accept)
|
|
2055
|
+
continue;
|
|
2056
|
+
const cap = opts.maxAmountPerCall?.[symbol];
|
|
2057
|
+
if (cap !== undefined && BigInt(accept.amount) > cap)
|
|
2058
|
+
continue;
|
|
2059
|
+
return { accept, symbol };
|
|
2060
|
+
}
|
|
2061
|
+
throw new X402SpendGuardError("no x402 offer matched preferAssets within maxAmountPerCall");
|
|
2062
|
+
}
|
|
2063
|
+
async function resolveAccountNonce(address, nodeUrl = DEFAULT_NONCE_NODE_URL) {
|
|
2064
|
+
const res = await fetch(`${nodeUrl.replace(/\/$/, "")}/v2/accounts/${address}?proof=0`);
|
|
2065
|
+
if (!res.ok)
|
|
2066
|
+
throw new Error(`x402 nonce lookup failed: ${res.status}`);
|
|
2067
|
+
const json = await res.json();
|
|
2068
|
+
return json.nonce;
|
|
2069
|
+
}
|
|
2070
|
+
async function buildSignedX402Payment(opts) {
|
|
2071
|
+
const accept = opts.asset ? opts.challenge.accepts.find((a) => a.asset === opts.asset) : opts.challenge.accepts[0];
|
|
2072
|
+
if (!accept) {
|
|
2073
|
+
throw new Error(`No x402 offer${opts.asset ? ` for asset ${opts.asset}` : ""}`);
|
|
2074
|
+
}
|
|
2075
|
+
const token = findX402TokenByAsset(accept.asset);
|
|
2076
|
+
if (!token)
|
|
2077
|
+
throw new Error(`Unknown x402 asset: ${accept.asset}`);
|
|
2078
|
+
const asset = token.contractId && token.assetName ? {
|
|
2079
|
+
kind: "sip010",
|
|
2080
|
+
contractId: token.contractId,
|
|
2081
|
+
assetName: token.assetName
|
|
2082
|
+
} : { kind: "stx" };
|
|
2083
|
+
const tx = buildExactTransfer({
|
|
2084
|
+
asset,
|
|
2085
|
+
amount: BigInt(accept.amount),
|
|
2086
|
+
payTo: accept.payTo,
|
|
2087
|
+
payer: opts.account.address,
|
|
2088
|
+
payerPublicKey: opts.account.publicKey,
|
|
2089
|
+
accountNonce: opts.accountNonce,
|
|
2090
|
+
nonce: accept.extra.nonce,
|
|
2091
|
+
chain: opts.chain
|
|
2092
|
+
});
|
|
2093
|
+
const signed = await signTransactionWithAccount(tx, opts.account);
|
|
2094
|
+
const header = b64encode({
|
|
2095
|
+
x402Version: opts.challenge.x402Version ?? 2,
|
|
2096
|
+
scheme: "exact",
|
|
2097
|
+
network: accept.network,
|
|
2098
|
+
asset: accept.asset,
|
|
2099
|
+
payload: { transaction: serializeTransactionHex(signed) },
|
|
2100
|
+
extra: { nonce: accept.extra.nonce }
|
|
2101
|
+
});
|
|
2102
|
+
return { header, accept };
|
|
2103
|
+
}
|
|
2104
|
+
function withX402(baseFetch, opts) {
|
|
2105
|
+
const sessions = new Map;
|
|
2106
|
+
let balanceToken = opts.balanceToken ?? null;
|
|
2107
|
+
let toppingUp = false;
|
|
2108
|
+
const originOf = (input) => {
|
|
2109
|
+
try {
|
|
2110
|
+
return new URL(String(input)).origin;
|
|
2111
|
+
} catch {
|
|
2112
|
+
return "";
|
|
2113
|
+
}
|
|
2114
|
+
};
|
|
2115
|
+
const wrapped = async (input, init) => {
|
|
2116
|
+
const origin = originOf(input);
|
|
2117
|
+
const run = (extra, signal2) => baseFetch(input, {
|
|
2118
|
+
...init,
|
|
2119
|
+
headers: { ...init?.headers, ...extra },
|
|
2120
|
+
...signal2 ? { signal: signal2 } : {}
|
|
2121
|
+
});
|
|
2122
|
+
const maybeTopUp = (res) => {
|
|
2123
|
+
const policy = opts.topUp;
|
|
2124
|
+
if (!policy || toppingUp || !origin)
|
|
2125
|
+
return;
|
|
2126
|
+
const remaining = res.headers.get("X-BALANCE-REMAINING-USD");
|
|
2127
|
+
if (remaining === null || Number(remaining) >= policy.whenBelow)
|
|
2128
|
+
return;
|
|
2129
|
+
toppingUp = true;
|
|
2130
|
+
(async () => {
|
|
2131
|
+
try {
|
|
2132
|
+
const dep = await wrapped(`${origin}/v1/x402/deposit?usd=${policy.usd}`, { method: "POST" });
|
|
2133
|
+
if (dep.ok) {
|
|
2134
|
+
const body = await dep.json();
|
|
2135
|
+
if (body.balance_token)
|
|
2136
|
+
balanceToken = body.balance_token;
|
|
2137
|
+
}
|
|
2138
|
+
} catch {} finally {
|
|
2139
|
+
toppingUp = false;
|
|
2140
|
+
}
|
|
2141
|
+
})();
|
|
2142
|
+
};
|
|
2143
|
+
const remember = (res) => {
|
|
2144
|
+
const voucher = res.headers.get("PAYMENT-SESSION");
|
|
2145
|
+
if (voucher && origin)
|
|
2146
|
+
sessions.set(origin, voucher);
|
|
2147
|
+
maybeTopUp(res);
|
|
2148
|
+
return res;
|
|
2149
|
+
};
|
|
2150
|
+
const cached = origin ? sessions.get(origin) : undefined;
|
|
2151
|
+
const first = await run({
|
|
2152
|
+
...cached ? { "PAYMENT-SESSION": cached } : {},
|
|
2153
|
+
...balanceToken ? { "PAYMENT-BALANCE": balanceToken } : {}
|
|
2154
|
+
});
|
|
2155
|
+
if (first.status !== 402)
|
|
2156
|
+
return remember(first);
|
|
2157
|
+
if (cached && origin)
|
|
2158
|
+
sessions.delete(origin);
|
|
2159
|
+
const challenge = await readX402Challenge(first);
|
|
2160
|
+
if (!challenge)
|
|
2161
|
+
return first;
|
|
2162
|
+
const { accept } = selectOffer(challenge, opts);
|
|
2163
|
+
const accountNonce = opts.accountNonce ?? await resolveAccountNonce(opts.account.address, opts.nodeUrl);
|
|
2164
|
+
const { header } = await buildSignedX402Payment({
|
|
2165
|
+
challenge,
|
|
2166
|
+
account: opts.account,
|
|
2167
|
+
accountNonce,
|
|
2168
|
+
asset: accept.asset,
|
|
2169
|
+
chain: opts.chain
|
|
2170
|
+
});
|
|
2171
|
+
opts.onSettling?.({ asset: accept.asset, amount: accept.amount });
|
|
2172
|
+
const signal = opts.timeoutMs ? AbortSignal.timeout(opts.timeoutMs) : undefined;
|
|
2173
|
+
return remember(await run({ "PAYMENT-SIGNATURE": header }, signal));
|
|
2174
|
+
};
|
|
2175
|
+
return wrapped;
|
|
2176
|
+
}
|
|
2177
|
+
function createX402Client(opts) {
|
|
2178
|
+
const f = withX402(opts.fetch ?? fetch, opts);
|
|
2179
|
+
const base = opts.baseUrl.replace(/\/$/, "");
|
|
2180
|
+
async function request(method, path, o = {}) {
|
|
2181
|
+
const qs = o.query ? `?${new URLSearchParams(o.query).toString()}` : "";
|
|
2182
|
+
const init = { method };
|
|
2183
|
+
if (o.body !== undefined) {
|
|
2184
|
+
init.body = JSON.stringify(o.body);
|
|
2185
|
+
init.headers = { "content-type": "application/json" };
|
|
2186
|
+
}
|
|
2187
|
+
const response = await f(`${base}${path}${qs}`, init);
|
|
2188
|
+
const data = await response.json().catch(() => null);
|
|
2189
|
+
return { data, payment: readX402Receipt(response), response };
|
|
2190
|
+
}
|
|
2191
|
+
return {
|
|
2192
|
+
get: (path, o) => request("GET", path, o),
|
|
2193
|
+
post: (path, o) => request("POST", path, o)
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
async function payAndRetry(doFetch, opts) {
|
|
2197
|
+
const first = await doFetch({});
|
|
2198
|
+
if (first.status !== 402)
|
|
2199
|
+
return first;
|
|
2200
|
+
const challenge = await readX402Challenge(first);
|
|
2201
|
+
if (!challenge)
|
|
2202
|
+
return first;
|
|
2203
|
+
const { accept } = selectOffer(challenge, opts);
|
|
2204
|
+
const { header } = await buildSignedX402Payment({
|
|
2205
|
+
challenge,
|
|
2206
|
+
account: opts.account,
|
|
2207
|
+
accountNonce: opts.accountNonce,
|
|
2208
|
+
asset: accept.asset,
|
|
2209
|
+
chain: opts.chain
|
|
2210
|
+
});
|
|
2211
|
+
return doFetch({ "PAYMENT-SIGNATURE": header });
|
|
2212
|
+
}
|
|
2096
2213
|
// src/webhooks.ts
|
|
2097
2214
|
import { verifySecondlayerSignatureValues } from "@secondlayer/shared/crypto/secondlayer-webhook";
|
|
2098
2215
|
import {
|
|
@@ -2218,11 +2335,17 @@ function verifyTransactionProof(proof, opts) {
|
|
|
2218
2335
|
};
|
|
2219
2336
|
}
|
|
2220
2337
|
export {
|
|
2338
|
+
withX402,
|
|
2221
2339
|
verifyWebhookSignature,
|
|
2222
2340
|
verifyTransactionProof,
|
|
2223
2341
|
verifySecondlayerSignature,
|
|
2224
2342
|
trigger,
|
|
2225
2343
|
toJsonSafe,
|
|
2344
|
+
selectOffer,
|
|
2345
|
+
resolveAccountNonce,
|
|
2346
|
+
readX402Receipt,
|
|
2347
|
+
readX402Challenge,
|
|
2348
|
+
payAndRetry,
|
|
2226
2349
|
isStxTransfer,
|
|
2227
2350
|
isStxMint,
|
|
2228
2351
|
isStxLock,
|
|
@@ -2248,7 +2371,10 @@ export {
|
|
|
2248
2371
|
decodeFtMint,
|
|
2249
2372
|
decodeFtBurn,
|
|
2250
2373
|
decodeClarityValue,
|
|
2374
|
+
createX402Client,
|
|
2251
2375
|
createStreamsClient,
|
|
2376
|
+
buildSignedX402Payment,
|
|
2377
|
+
X402SpendGuardError,
|
|
2252
2378
|
VersionConflictError,
|
|
2253
2379
|
ValidationError,
|
|
2254
2380
|
Subscriptions,
|
|
@@ -2259,15 +2385,14 @@ export {
|
|
|
2259
2385
|
RateLimitError,
|
|
2260
2386
|
Projects,
|
|
2261
2387
|
Index,
|
|
2262
|
-
|
|
2388
|
+
DEFAULT_PREFER_ASSETS,
|
|
2263
2389
|
Cursor,
|
|
2264
2390
|
Contracts,
|
|
2265
|
-
CURSOR_SLUGS,
|
|
2266
2391
|
ByoBreakingChangeError,
|
|
2267
2392
|
AuthError,
|
|
2268
2393
|
ApiKeys,
|
|
2269
2394
|
ApiError
|
|
2270
2395
|
};
|
|
2271
2396
|
|
|
2272
|
-
//# debugId=
|
|
2397
|
+
//# debugId=EAC231197163FBBB64756E2164756E21
|
|
2273
2398
|
//# sourceMappingURL=index.js.map
|