@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/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=AD881ECDD1B046B864756E2164756E21
2510
+ //# debugId=EFE64A13154F57C864756E2164756E21
2273
2511
  //# sourceMappingURL=index.js.map