@secondlayer/sdk 6.18.0 → 6.20.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 +348 -93
- package/dist/index.js +266 -8
- package/dist/index.js.map +10 -9
- 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 +158 -13
- package/dist/subgraphs/index.js +88 -8
- package/dist/subgraphs/index.js.map +7 -7
- package/dist/x402.d.ts +149 -0
- package/dist/x402.js +184 -0
- package/dist/x402.js.map +10 -0
- package/package.json +7 -3
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
|
}
|
|
@@ -417,6 +430,16 @@ class Contracts extends BaseClient {
|
|
|
417
430
|
cursor: params.cursor
|
|
418
431
|
})}`);
|
|
419
432
|
}
|
|
433
|
+
async get(contractId, opts = {}) {
|
|
434
|
+
try {
|
|
435
|
+
const { contract } = await this.request("GET", `/v1/contracts/${encodeURIComponent(contractId)}${opts.includeAbi ? "?include=abi" : ""}`);
|
|
436
|
+
return contract;
|
|
437
|
+
} catch (err) {
|
|
438
|
+
if (err instanceof ApiError && err.status === 404)
|
|
439
|
+
return null;
|
|
440
|
+
throw err;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
420
443
|
}
|
|
421
444
|
|
|
422
445
|
// src/datasets/client.ts
|
|
@@ -596,18 +619,18 @@ class Index extends BaseClient {
|
|
|
596
619
|
discover() {
|
|
597
620
|
return this.request("GET", "/v1/index");
|
|
598
621
|
}
|
|
599
|
-
ftTransfers = {
|
|
622
|
+
ftTransfers = Object.assign((params = {}) => this.listFtTransfers(params), {
|
|
600
623
|
list: (params = {}) => this.listFtTransfers(params),
|
|
601
624
|
walk: (params = {}) => this.walkFtTransfers(params)
|
|
602
|
-
};
|
|
603
|
-
nftTransfers = {
|
|
625
|
+
});
|
|
626
|
+
nftTransfers = Object.assign((params = {}) => this.listNftTransfers(params), {
|
|
604
627
|
list: (params = {}) => this.listNftTransfers(params),
|
|
605
628
|
walk: (params = {}) => this.walkNftTransfers(params)
|
|
606
|
-
};
|
|
607
|
-
events = {
|
|
629
|
+
});
|
|
630
|
+
events = Object.assign((params) => this.listEvents(params), {
|
|
608
631
|
list: (params) => this.listEvents(params),
|
|
609
632
|
walk: (params) => this.walkEvents(params)
|
|
610
|
-
};
|
|
633
|
+
});
|
|
611
634
|
contractCalls = {
|
|
612
635
|
list: (params = {}) => this.listContractCalls(params),
|
|
613
636
|
walk: (params = {}) => this.walkContractCalls(params)
|
|
@@ -624,7 +647,8 @@ class Index extends BaseClient {
|
|
|
624
647
|
transactions = {
|
|
625
648
|
list: (params = {}) => this.listTransactions(params),
|
|
626
649
|
walk: (params = {}) => this.walkTransactions(params),
|
|
627
|
-
get: (txId) => this.getTransaction(txId)
|
|
650
|
+
get: (txId) => this.getTransaction(txId),
|
|
651
|
+
getProof: (txId) => this.getTransactionProof(txId)
|
|
628
652
|
};
|
|
629
653
|
stacking = {
|
|
630
654
|
list: (params = {}) => this.listStacking(params),
|
|
@@ -886,6 +910,15 @@ class Index extends BaseClient {
|
|
|
886
910
|
throw err;
|
|
887
911
|
}
|
|
888
912
|
}
|
|
913
|
+
async getTransactionProof(txId) {
|
|
914
|
+
try {
|
|
915
|
+
return await this.request("GET", `/v1/index/transactions/${encodeURIComponent(txId)}/proof`);
|
|
916
|
+
} catch (err) {
|
|
917
|
+
if (err instanceof ApiError && err.status === 404)
|
|
918
|
+
return null;
|
|
919
|
+
throw err;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
889
922
|
async* walkTransactions(params = {}) {
|
|
890
923
|
const batchSize = params.batchSize ?? 200;
|
|
891
924
|
let cursor = params.cursor ?? params.fromCursor ?? null;
|
|
@@ -1162,6 +1195,38 @@ async function consumeStreamsEvents(opts) {
|
|
|
1162
1195
|
}
|
|
1163
1196
|
return { cursor, pages, emptyPolls };
|
|
1164
1197
|
}
|
|
1198
|
+
async function* iterateStreamsBatches(opts) {
|
|
1199
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
1200
|
+
let cursor = opts.fromCursor ?? null;
|
|
1201
|
+
while (!opts.signal?.aborted) {
|
|
1202
|
+
const envelope = await opts.fetchEvents({
|
|
1203
|
+
cursor,
|
|
1204
|
+
limit: opts.batchSize,
|
|
1205
|
+
types: opts.types,
|
|
1206
|
+
notTypes: opts.notTypes,
|
|
1207
|
+
contractId: opts.contractId,
|
|
1208
|
+
sender: opts.sender,
|
|
1209
|
+
recipient: opts.recipient,
|
|
1210
|
+
assetIdentifier: opts.assetIdentifier
|
|
1211
|
+
});
|
|
1212
|
+
const checkpoint = envelope.next_cursor ?? cursor;
|
|
1213
|
+
if (envelope.events.length > 0 || envelope.reorgs.length > 0) {
|
|
1214
|
+
yield {
|
|
1215
|
+
events: envelope.events,
|
|
1216
|
+
cursor: checkpoint,
|
|
1217
|
+
tip: envelope.tip,
|
|
1218
|
+
reorgs: envelope.reorgs
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
const advanced = checkpoint !== null && checkpoint !== cursor;
|
|
1222
|
+
cursor = checkpoint;
|
|
1223
|
+
if (!advanced && envelope.events.length === 0) {
|
|
1224
|
+
if (opts.signal?.aborted)
|
|
1225
|
+
return;
|
|
1226
|
+
await sleep(opts.intervalMs, opts.signal);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1165
1230
|
async function* streamStreamsEvents(opts) {
|
|
1166
1231
|
const sleep = opts.sleep ?? defaultSleep;
|
|
1167
1232
|
const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
|
|
@@ -1539,6 +1604,21 @@ function createStreamsClient(options) {
|
|
|
1539
1604
|
})}`);
|
|
1540
1605
|
}
|
|
1541
1606
|
return {
|
|
1607
|
+
consume(params = {}) {
|
|
1608
|
+
return iterateStreamsBatches({
|
|
1609
|
+
fromCursor: params.cursor,
|
|
1610
|
+
batchSize: params.batchSize ?? 100,
|
|
1611
|
+
intervalMs: params.intervalMs ?? 2000,
|
|
1612
|
+
types: params.types,
|
|
1613
|
+
notTypes: params.notTypes,
|
|
1614
|
+
contractId: params.contractId,
|
|
1615
|
+
sender: params.sender,
|
|
1616
|
+
recipient: params.recipient,
|
|
1617
|
+
assetIdentifier: params.assetIdentifier,
|
|
1618
|
+
signal: params.signal,
|
|
1619
|
+
fetchEvents
|
|
1620
|
+
});
|
|
1621
|
+
},
|
|
1542
1622
|
events: {
|
|
1543
1623
|
list: listEvents,
|
|
1544
1624
|
byTxId(txId) {
|
|
@@ -2073,6 +2153,174 @@ function decodePrint(event) {
|
|
|
2073
2153
|
import {
|
|
2074
2154
|
STREAMS_EVENT_TYPES
|
|
2075
2155
|
} from "@secondlayer/shared";
|
|
2156
|
+
// src/x402.ts
|
|
2157
|
+
import {
|
|
2158
|
+
X402_TOKENS,
|
|
2159
|
+
findX402TokenByAsset
|
|
2160
|
+
} from "@secondlayer/shared/x402";
|
|
2161
|
+
import {
|
|
2162
|
+
serializeTransactionHex,
|
|
2163
|
+
signTransactionWithAccount
|
|
2164
|
+
} from "@secondlayer/stacks/transactions";
|
|
2165
|
+
import { buildExactTransfer } from "@secondlayer/stacks/x402";
|
|
2166
|
+
var DEFAULT_PREFER_ASSETS = [
|
|
2167
|
+
"sBTC",
|
|
2168
|
+
"USDCx",
|
|
2169
|
+
"STX"
|
|
2170
|
+
];
|
|
2171
|
+
var DEFAULT_NONCE_NODE_URL = "https://api.hiro.so";
|
|
2172
|
+
|
|
2173
|
+
class X402SpendGuardError extends Error {
|
|
2174
|
+
constructor(message) {
|
|
2175
|
+
super(message);
|
|
2176
|
+
this.name = "X402SpendGuardError";
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
function b64encode(value) {
|
|
2180
|
+
return Buffer.from(JSON.stringify(value), "utf8").toString("base64");
|
|
2181
|
+
}
|
|
2182
|
+
function b64decodeJson(value) {
|
|
2183
|
+
try {
|
|
2184
|
+
return JSON.parse(Buffer.from(value, "base64").toString("utf8"));
|
|
2185
|
+
} catch {
|
|
2186
|
+
return null;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
async function readX402Challenge(res) {
|
|
2190
|
+
const header = res.headers.get("PAYMENT-REQUIRED");
|
|
2191
|
+
if (header) {
|
|
2192
|
+
const decoded = b64decodeJson(header);
|
|
2193
|
+
if (decoded)
|
|
2194
|
+
return decoded;
|
|
2195
|
+
}
|
|
2196
|
+
try {
|
|
2197
|
+
return await res.clone().json();
|
|
2198
|
+
} catch {
|
|
2199
|
+
return null;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
function readX402Receipt(res) {
|
|
2203
|
+
const header = res.headers.get("PAYMENT-RESPONSE");
|
|
2204
|
+
return header ? b64decodeJson(header) : null;
|
|
2205
|
+
}
|
|
2206
|
+
function selectOffer(challenge, opts = {}) {
|
|
2207
|
+
const prefer = opts.preferAssets ?? DEFAULT_PREFER_ASSETS;
|
|
2208
|
+
for (const symbol of prefer) {
|
|
2209
|
+
const token = X402_TOKENS[symbol];
|
|
2210
|
+
const accept = challenge.accepts.find((a) => a.asset === token.asset);
|
|
2211
|
+
if (!accept)
|
|
2212
|
+
continue;
|
|
2213
|
+
const cap = opts.maxAmountPerCall?.[symbol];
|
|
2214
|
+
if (cap !== undefined && BigInt(accept.amount) > cap)
|
|
2215
|
+
continue;
|
|
2216
|
+
return { accept, symbol };
|
|
2217
|
+
}
|
|
2218
|
+
throw new X402SpendGuardError("no x402 offer matched preferAssets within maxAmountPerCall");
|
|
2219
|
+
}
|
|
2220
|
+
async function resolveAccountNonce(address, nodeUrl = DEFAULT_NONCE_NODE_URL) {
|
|
2221
|
+
const res = await fetch(`${nodeUrl.replace(/\/$/, "")}/v2/accounts/${address}?proof=0`);
|
|
2222
|
+
if (!res.ok)
|
|
2223
|
+
throw new Error(`x402 nonce lookup failed: ${res.status}`);
|
|
2224
|
+
const json = await res.json();
|
|
2225
|
+
return json.nonce;
|
|
2226
|
+
}
|
|
2227
|
+
async function buildSignedX402Payment(opts) {
|
|
2228
|
+
const accept = opts.asset ? opts.challenge.accepts.find((a) => a.asset === opts.asset) : opts.challenge.accepts[0];
|
|
2229
|
+
if (!accept) {
|
|
2230
|
+
throw new Error(`No x402 offer${opts.asset ? ` for asset ${opts.asset}` : ""}`);
|
|
2231
|
+
}
|
|
2232
|
+
const token = findX402TokenByAsset(accept.asset);
|
|
2233
|
+
if (!token)
|
|
2234
|
+
throw new Error(`Unknown x402 asset: ${accept.asset}`);
|
|
2235
|
+
const asset = token.contractId && token.assetName ? {
|
|
2236
|
+
kind: "sip010",
|
|
2237
|
+
contractId: token.contractId,
|
|
2238
|
+
assetName: token.assetName
|
|
2239
|
+
} : { kind: "stx" };
|
|
2240
|
+
const tx = buildExactTransfer({
|
|
2241
|
+
asset,
|
|
2242
|
+
amount: BigInt(accept.amount),
|
|
2243
|
+
payTo: accept.payTo,
|
|
2244
|
+
payer: opts.account.address,
|
|
2245
|
+
payerPublicKey: opts.account.publicKey,
|
|
2246
|
+
accountNonce: opts.accountNonce,
|
|
2247
|
+
nonce: accept.extra.nonce,
|
|
2248
|
+
chain: opts.chain
|
|
2249
|
+
});
|
|
2250
|
+
const signed = await signTransactionWithAccount(tx, opts.account);
|
|
2251
|
+
const header = b64encode({
|
|
2252
|
+
x402Version: opts.challenge.x402Version ?? 2,
|
|
2253
|
+
scheme: "exact",
|
|
2254
|
+
network: accept.network,
|
|
2255
|
+
asset: accept.asset,
|
|
2256
|
+
payload: { transaction: serializeTransactionHex(signed) },
|
|
2257
|
+
extra: { nonce: accept.extra.nonce }
|
|
2258
|
+
});
|
|
2259
|
+
return { header, accept };
|
|
2260
|
+
}
|
|
2261
|
+
function withX402(baseFetch, opts) {
|
|
2262
|
+
return async (input, init) => {
|
|
2263
|
+
const run = (extra, signal2) => baseFetch(input, {
|
|
2264
|
+
...init,
|
|
2265
|
+
headers: { ...init?.headers, ...extra },
|
|
2266
|
+
...signal2 ? { signal: signal2 } : {}
|
|
2267
|
+
});
|
|
2268
|
+
const first = await run({});
|
|
2269
|
+
if (first.status !== 402)
|
|
2270
|
+
return first;
|
|
2271
|
+
const challenge = await readX402Challenge(first);
|
|
2272
|
+
if (!challenge)
|
|
2273
|
+
return first;
|
|
2274
|
+
const { accept } = selectOffer(challenge, opts);
|
|
2275
|
+
const accountNonce = opts.accountNonce ?? await resolveAccountNonce(opts.account.address, opts.nodeUrl);
|
|
2276
|
+
const { header } = await buildSignedX402Payment({
|
|
2277
|
+
challenge,
|
|
2278
|
+
account: opts.account,
|
|
2279
|
+
accountNonce,
|
|
2280
|
+
asset: accept.asset,
|
|
2281
|
+
chain: opts.chain
|
|
2282
|
+
});
|
|
2283
|
+
opts.onSettling?.({ asset: accept.asset, amount: accept.amount });
|
|
2284
|
+
const signal = opts.timeoutMs ? AbortSignal.timeout(opts.timeoutMs) : undefined;
|
|
2285
|
+
return run({ "PAYMENT-SIGNATURE": header }, signal);
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
function createX402Client(opts) {
|
|
2289
|
+
const f = withX402(opts.fetch ?? fetch, opts);
|
|
2290
|
+
const base = opts.baseUrl.replace(/\/$/, "");
|
|
2291
|
+
async function request(method, path, o = {}) {
|
|
2292
|
+
const qs = o.query ? `?${new URLSearchParams(o.query).toString()}` : "";
|
|
2293
|
+
const init = { method };
|
|
2294
|
+
if (o.body !== undefined) {
|
|
2295
|
+
init.body = JSON.stringify(o.body);
|
|
2296
|
+
init.headers = { "content-type": "application/json" };
|
|
2297
|
+
}
|
|
2298
|
+
const response = await f(`${base}${path}${qs}`, init);
|
|
2299
|
+
const data = await response.json().catch(() => null);
|
|
2300
|
+
return { data, payment: readX402Receipt(response), response };
|
|
2301
|
+
}
|
|
2302
|
+
return {
|
|
2303
|
+
get: (path, o) => request("GET", path, o),
|
|
2304
|
+
post: (path, o) => request("POST", path, o)
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
async function payAndRetry(doFetch, opts) {
|
|
2308
|
+
const first = await doFetch({});
|
|
2309
|
+
if (first.status !== 402)
|
|
2310
|
+
return first;
|
|
2311
|
+
const challenge = await readX402Challenge(first);
|
|
2312
|
+
if (!challenge)
|
|
2313
|
+
return first;
|
|
2314
|
+
const { accept } = selectOffer(challenge, opts);
|
|
2315
|
+
const { header } = await buildSignedX402Payment({
|
|
2316
|
+
challenge,
|
|
2317
|
+
account: opts.account,
|
|
2318
|
+
accountNonce: opts.accountNonce,
|
|
2319
|
+
asset: accept.asset,
|
|
2320
|
+
chain: opts.chain
|
|
2321
|
+
});
|
|
2322
|
+
return doFetch({ "PAYMENT-SIGNATURE": header });
|
|
2323
|
+
}
|
|
2076
2324
|
// src/webhooks.ts
|
|
2077
2325
|
import { verifySecondlayerSignatureValues } from "@secondlayer/shared/crypto/secondlayer-webhook";
|
|
2078
2326
|
import {
|
|
@@ -2198,11 +2446,17 @@ function verifyTransactionProof(proof, opts) {
|
|
|
2198
2446
|
};
|
|
2199
2447
|
}
|
|
2200
2448
|
export {
|
|
2449
|
+
withX402,
|
|
2201
2450
|
verifyWebhookSignature,
|
|
2202
2451
|
verifyTransactionProof,
|
|
2203
2452
|
verifySecondlayerSignature,
|
|
2204
2453
|
trigger,
|
|
2205
2454
|
toJsonSafe,
|
|
2455
|
+
selectOffer,
|
|
2456
|
+
resolveAccountNonce,
|
|
2457
|
+
readX402Receipt,
|
|
2458
|
+
readX402Challenge,
|
|
2459
|
+
payAndRetry,
|
|
2206
2460
|
isStxTransfer,
|
|
2207
2461
|
isStxMint,
|
|
2208
2462
|
isStxLock,
|
|
@@ -2228,7 +2482,10 @@ export {
|
|
|
2228
2482
|
decodeFtMint,
|
|
2229
2483
|
decodeFtBurn,
|
|
2230
2484
|
decodeClarityValue,
|
|
2485
|
+
createX402Client,
|
|
2231
2486
|
createStreamsClient,
|
|
2487
|
+
buildSignedX402Payment,
|
|
2488
|
+
X402SpendGuardError,
|
|
2232
2489
|
VersionConflictError,
|
|
2233
2490
|
ValidationError,
|
|
2234
2491
|
Subscriptions,
|
|
@@ -2240,6 +2497,7 @@ export {
|
|
|
2240
2497
|
Projects,
|
|
2241
2498
|
Index,
|
|
2242
2499
|
Datasets,
|
|
2500
|
+
DEFAULT_PREFER_ASSETS,
|
|
2243
2501
|
Cursor,
|
|
2244
2502
|
Contracts,
|
|
2245
2503
|
CURSOR_SLUGS,
|
|
@@ -2249,5 +2507,5 @@ export {
|
|
|
2249
2507
|
ApiError
|
|
2250
2508
|
};
|
|
2251
2509
|
|
|
2252
|
-
//# debugId=
|
|
2510
|
+
//# debugId=EFE64A13154F57C864756E2164756E21
|
|
2253
2511
|
//# sourceMappingURL=index.js.map
|