@secondlayer/sdk 6.19.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 +256 -14
- package/dist/index.js +245 -7
- package/dist/index.js.map +9 -8
- 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 +109 -13
- package/dist/subgraphs/index.js +67 -7
- package/dist/subgraphs/index.js.map +6 -6
- 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
|
}
|
|
@@ -606,18 +619,18 @@ class Index extends BaseClient {
|
|
|
606
619
|
discover() {
|
|
607
620
|
return this.request("GET", "/v1/index");
|
|
608
621
|
}
|
|
609
|
-
ftTransfers = {
|
|
622
|
+
ftTransfers = Object.assign((params = {}) => this.listFtTransfers(params), {
|
|
610
623
|
list: (params = {}) => this.listFtTransfers(params),
|
|
611
624
|
walk: (params = {}) => this.walkFtTransfers(params)
|
|
612
|
-
};
|
|
613
|
-
nftTransfers = {
|
|
625
|
+
});
|
|
626
|
+
nftTransfers = Object.assign((params = {}) => this.listNftTransfers(params), {
|
|
614
627
|
list: (params = {}) => this.listNftTransfers(params),
|
|
615
628
|
walk: (params = {}) => this.walkNftTransfers(params)
|
|
616
|
-
};
|
|
617
|
-
events = {
|
|
629
|
+
});
|
|
630
|
+
events = Object.assign((params) => this.listEvents(params), {
|
|
618
631
|
list: (params) => this.listEvents(params),
|
|
619
632
|
walk: (params) => this.walkEvents(params)
|
|
620
|
-
};
|
|
633
|
+
});
|
|
621
634
|
contractCalls = {
|
|
622
635
|
list: (params = {}) => this.listContractCalls(params),
|
|
623
636
|
walk: (params = {}) => this.walkContractCalls(params)
|
|
@@ -1182,6 +1195,38 @@ async function consumeStreamsEvents(opts) {
|
|
|
1182
1195
|
}
|
|
1183
1196
|
return { cursor, pages, emptyPolls };
|
|
1184
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
|
+
}
|
|
1185
1230
|
async function* streamStreamsEvents(opts) {
|
|
1186
1231
|
const sleep = opts.sleep ?? defaultSleep;
|
|
1187
1232
|
const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
|
|
@@ -1559,6 +1604,21 @@ function createStreamsClient(options) {
|
|
|
1559
1604
|
})}`);
|
|
1560
1605
|
}
|
|
1561
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
|
+
},
|
|
1562
1622
|
events: {
|
|
1563
1623
|
list: listEvents,
|
|
1564
1624
|
byTxId(txId) {
|
|
@@ -2093,6 +2153,174 @@ function decodePrint(event) {
|
|
|
2093
2153
|
import {
|
|
2094
2154
|
STREAMS_EVENT_TYPES
|
|
2095
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
|
+
}
|
|
2096
2324
|
// src/webhooks.ts
|
|
2097
2325
|
import { verifySecondlayerSignatureValues } from "@secondlayer/shared/crypto/secondlayer-webhook";
|
|
2098
2326
|
import {
|
|
@@ -2218,11 +2446,17 @@ function verifyTransactionProof(proof, opts) {
|
|
|
2218
2446
|
};
|
|
2219
2447
|
}
|
|
2220
2448
|
export {
|
|
2449
|
+
withX402,
|
|
2221
2450
|
verifyWebhookSignature,
|
|
2222
2451
|
verifyTransactionProof,
|
|
2223
2452
|
verifySecondlayerSignature,
|
|
2224
2453
|
trigger,
|
|
2225
2454
|
toJsonSafe,
|
|
2455
|
+
selectOffer,
|
|
2456
|
+
resolveAccountNonce,
|
|
2457
|
+
readX402Receipt,
|
|
2458
|
+
readX402Challenge,
|
|
2459
|
+
payAndRetry,
|
|
2226
2460
|
isStxTransfer,
|
|
2227
2461
|
isStxMint,
|
|
2228
2462
|
isStxLock,
|
|
@@ -2248,7 +2482,10 @@ export {
|
|
|
2248
2482
|
decodeFtMint,
|
|
2249
2483
|
decodeFtBurn,
|
|
2250
2484
|
decodeClarityValue,
|
|
2485
|
+
createX402Client,
|
|
2251
2486
|
createStreamsClient,
|
|
2487
|
+
buildSignedX402Payment,
|
|
2488
|
+
X402SpendGuardError,
|
|
2252
2489
|
VersionConflictError,
|
|
2253
2490
|
ValidationError,
|
|
2254
2491
|
Subscriptions,
|
|
@@ -2260,6 +2497,7 @@ export {
|
|
|
2260
2497
|
Projects,
|
|
2261
2498
|
Index,
|
|
2262
2499
|
Datasets,
|
|
2500
|
+
DEFAULT_PREFER_ASSETS,
|
|
2263
2501
|
Cursor,
|
|
2264
2502
|
Contracts,
|
|
2265
2503
|
CURSOR_SLUGS,
|
|
@@ -2269,5 +2507,5 @@ export {
|
|
|
2269
2507
|
ApiError
|
|
2270
2508
|
};
|
|
2271
2509
|
|
|
2272
|
-
//# debugId=
|
|
2510
|
+
//# debugId=EFE64A13154F57C864756E2164756E21
|
|
2273
2511
|
//# sourceMappingURL=index.js.map
|