@secondlayer/sdk 6.12.0 → 6.14.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 CHANGED
@@ -128,6 +128,31 @@ for await (const event of streams.events.stream({
128
128
  }
129
129
  ```
130
130
 
131
+ Real-time push (`events.subscribe`).
132
+
133
+ Use `client.events.subscribe` for callback-style live delivery — it pushes each
134
+ event to `onEvent` as it lands. It's fetch-based (so it carries the Bearer key)
135
+ and works in browsers and Node 18+. It auto-reconnects from the last delivered
136
+ cursor on a dropped connection, and returns an unsubscribe function.
137
+
138
+ ```typescript
139
+ const unsubscribe = streams.events.subscribe({
140
+ types: ["ft_transfer"], // notTypes / contractId / sender / recipient / assetIdentifier also filter
141
+ // fromCursor: lastCursor, // resume strictly after this cursor; omit to tail from the tip
142
+ onEvent: async (event) => {
143
+ console.log(event.cursor, event.tx_id);
144
+ },
145
+ onError: (err) => console.error("reconnecting…", err),
146
+ });
147
+
148
+ // later
149
+ unsubscribe(); // or pass `signal` and abort it
150
+ ```
151
+
152
+ Each pushed frame is `{ event, sig, key_id }`. When the client was created with
153
+ `verify` (or `{ publicKey }`), the per-frame ed25519 signature is checked before
154
+ `onEvent` runs; a bad/missing signature throws `StreamsSignatureError`.
155
+
131
156
  Bulk parquet dumps.
132
157
 
133
158
  Finalized history is published as public parquet files. Set `dumpsBaseUrl`
@@ -135,6 +160,12 @@ Finalized history is published as public parquet files. Set `dumpsBaseUrl`
135
160
  decode parquet; `download` hands you sha256-verified bytes to process with your
136
161
  own tooling.
137
162
 
163
+ The bulk **manifest is ed25519-signed**, and the SDK verifies that signature
164
+ before it trusts any per-file sha256 listed in it. The `verifyDumpsManifest`
165
+ option **defaults to `true`** — `dumps.list()` and `events.replay()` enforce it,
166
+ so you don't trust the file hashes unless the manifest itself verifies. Opt out
167
+ with `verifyDumpsManifest: false`.
168
+
138
169
  ```typescript
139
170
  const streams = createStreamsClient({
140
171
  apiKey: process.env.SL_API_KEY!,
@@ -215,6 +246,75 @@ for await (const transfer of sl.index.ftTransfers.walk({
215
246
  }
216
247
  ```
217
248
 
249
+ ## Transaction-inclusion proofs
250
+
251
+ Verify — **without trusting Second Layer** — that a transaction is included in a
252
+ Stacks (Nakamoto) block, and that ≥70% of the reward cycle's signer weight
253
+ attested to that block. `verifyTransactionProof` recomputes everything
254
+ client-side and trusts nothing the API returned.
255
+
256
+ > Verification uses Node's crypto via `@secondlayer/shared` — Node/server-side use.
257
+
258
+ ```typescript
259
+ import { verifyTransactionProof, fetchRewardSet } from "@secondlayer/sdk";
260
+
261
+ const proof = await fetch(
262
+ `https://api.secondlayer.tools/v1/index/transactions/${txid}/proof`,
263
+ ).then((r) => r.json());
264
+
265
+ const result = verifyTransactionProof(proof); // anchored + consensus (embedded set)
266
+ // result.ok, result.level === "consensus", result.signerWeightBps
267
+
268
+ // Fully trustless — resolve the reward set from your own node:
269
+ const rewardSet = await fetchRewardSet({
270
+ nodeUrl: "https://your-stacks-node:20443",
271
+ cycle: proof.consensus.reward_cycle,
272
+ });
273
+ const trustless = verifyTransactionProof(proof, { rewardSet }); // rewardSetSource: "provided"
274
+ ```
275
+
276
+ Two trust levels:
277
+
278
+ - **Anchored** — recompute the txid from `raw_tx`, fold `tx_merkle_path` up to the
279
+ header's `tx_merkle_root`, and recompute `block_hash` + `index_block_hash` from
280
+ `raw_header`. The tx is in a header any node can corroborate.
281
+ - **Consensus** — additionally recover the header's signer signatures and confirm
282
+ ≥70% of the reward cycle's signer weight signed the block. Fully trustless when
283
+ you pass a `rewardSet` resolved yourself via `fetchRewardSet`
284
+ (`rewardSetSource: "provided"`); otherwise it uses the proof's embedded set
285
+ (`rewardSetSource: "embedded"`).
286
+
287
+ ```typescript
288
+ verifyTransactionProof(
289
+ proof: TransactionProof,
290
+ opts?: { rewardSet?: RewardSet },
291
+ ): TransactionProofVerifyResult;
292
+
293
+ fetchRewardSet(opts: {
294
+ nodeUrl: string; // your own stacks-node
295
+ cycle: number; // reward cycle — proof.consensus.reward_cycle
296
+ fetchImpl?: typeof fetch;
297
+ }): Promise<RewardSet | null>; // reads /v3/stacker_set/{cycle}
298
+ ```
299
+
300
+ `verifyTransactionProof` returns a `TransactionProofVerifyResult`:
301
+
302
+ ```typescript
303
+ {
304
+ level: "anchored" | "consensus";
305
+ txidMatches: boolean;
306
+ includedInHeader: boolean;
307
+ headerSelfConsistent: boolean;
308
+ signerWeightBps?: number; // consensus only
309
+ thresholdMet?: boolean; // consensus only — ≥70% (7000 bps)
310
+ rewardSetSource?: "provided" | "embedded";
311
+ ok: boolean;
312
+ errors: string[];
313
+ }
314
+ ```
315
+
316
+ Exported types: `TransactionProof`, `TransactionProofVerifyResult`, `RewardSet`.
317
+
218
318
  ## Stacks Subgraphs
219
319
 
220
320
  Deploy and query app-specific L3 tables.
@@ -248,6 +348,30 @@ const gaps = await sl.subgraphs.gaps("my-subgraph");
248
348
  const result = await sl.subgraphs.deploy({ name, sources, schema, handlerCode });
249
349
  ```
250
350
 
351
+ Stream rows live with the typed client — each table exposes `subscribe`
352
+ alongside `findMany`/`count`:
353
+
354
+ ```typescript
355
+ const subgraph = sl.subgraphs.typed(myDefinition); // { transfers, ... }
356
+
357
+ const unsubscribe = subgraph.transfers.subscribe(
358
+ (row) => console.log(row),
359
+ {
360
+ where: { amount: { gte: "1000000" } }, // optional row filter
361
+ since: 180000, // optional: replay from this block_height, then tail
362
+ onError: (err) => console.error(err),
363
+ },
364
+ );
365
+
366
+ // later
367
+ unsubscribe();
368
+ ```
369
+
370
+ `subscribe` is an SSE stream over the global `EventSource` (available in
371
+ browsers and Node ≥ 22; it throws if no `EventSource` is present). Frames are
372
+ unsigned rows. `since: <block_height>` replays matching rows from that height,
373
+ then tails the live edge; omit it to tail only.
374
+
251
375
  ## Subscriptions
252
376
 
253
377
  Signed HTTP webhooks. Subscriptions are polymorphic — pick one kind:
@@ -327,6 +451,38 @@ const { data: dead } = await sl.subscriptions.dead(id);
327
451
  await sl.subscriptions.requeueDead(id, outboxId);
328
452
  ```
329
453
 
454
+ ### Verifying deliveries
455
+
456
+ Every delivery — any kind, any `format` — also carries a universal authenticity
457
+ signature you can verify with one published key, no per-subscription secret. The
458
+ headers are `webhook-id`, `x-secondlayer-signature`, and
459
+ `x-secondlayer-signature-keyid`; the signed content is `` `${webhook-id}.${rawBody}` ``
460
+ (ed25519). Fetch the public key from `GET /public/streams/signing-key`.
461
+
462
+ ```typescript
463
+ import { verifySecondlayerSignature } from "@secondlayer/sdk";
464
+
465
+ app.post("/webhook", async (c) => {
466
+ const raw = await c.req.text(); // raw body — never re-stringify the parsed JSON
467
+ if (!verifySecondlayerSignature(raw, c.req.raw.headers, SECONDLAYER_PUBLIC_KEY)) {
468
+ return c.text("Invalid signature", 401);
469
+ }
470
+ // ... trusted ...
471
+ return c.body(null, 204);
472
+ });
473
+ ```
474
+
475
+ ```typescript
476
+ verifySecondlayerSignature(
477
+ rawBody: string,
478
+ headers: WebhookHeaderInput, // plain object, Fetch `Headers`, or a lookup fn
479
+ publicKeyPem: string,
480
+ ): boolean;
481
+ ```
482
+
483
+ Prefer the per-subscription HMAC (Standard Webhooks) secret instead? Use
484
+ `verifyWebhookSignature(rawBody, headers, secret)` — raw body first.
485
+
330
486
  ## Error Handling
331
487
 
332
488
  ```typescript
package/dist/index.d.ts CHANGED
@@ -1014,6 +1014,14 @@ type StreamsTip = {
1014
1014
  */
1015
1015
  finalized_height?: number
1016
1016
  lag_seconds: number
1017
+ /**
1018
+ * Oldest height still seekable on the live API for the caller's tier
1019
+ * (`tip - retention`). `null` = unlimited retention. Older reads must use the
1020
+ * cold dumps lane. Optional for back-compat.
1021
+ */
1022
+ oldest_seekable_height?: number | null
1023
+ /** Oldest seekable cursor (`<oldest_seekable_height>:0`); `null` = unlimited. */
1024
+ oldest_cursor?: string | null
1017
1025
  };
1018
1026
  type StreamsCanonicalBlock = {
1019
1027
  block_height: number
@@ -1274,7 +1282,7 @@ type StreamsUsage = {
1274
1282
  events_this_month: number
1275
1283
  }
1276
1284
  };
1277
- import { CreateSubscriptionRequest, CreateSubscriptionResponse, DeadRow, DeliveryRow, ReplayResult, RotateSecretResponse, SubscriptionDetail, SubscriptionSummary, UpdateSubscriptionRequest } from "@secondlayer/shared/schemas/subscriptions";
1285
+ import { CreateSubscriptionRequest, CreateSubscriptionResponse, DeadRow, DeliveryRow, ReplayResult, RotateSecretResponse, SubscriptionDetail, SubscriptionSummary, SubscriptionTestResult, UpdateSubscriptionRequest } from "@secondlayer/shared/schemas/subscriptions";
1278
1286
  import { ChainTrigger, ChainTriggerType, CreateSubscriptionRequest as CreateSubscriptionRequest2, CreateSubscriptionResponse as CreateSubscriptionResponse2, DeadRow as DeadRow2, DeliveryRow as DeliveryRow2, ReplayResult as ReplayResult2, RotateSecretResponse as RotateSecretResponse2, SubscriptionDetail as SubscriptionDetail2, SubscriptionFormat, SubscriptionKind, SubscriptionRuntime, SubscriptionStatus, SubscriptionSummary as SubscriptionSummary2, UpdateSubscriptionRequest as UpdateSubscriptionRequest2 } from "@secondlayer/shared/schemas/subscriptions";
1279
1287
  import { trigger } from "@secondlayer/shared/schemas/subscriptions";
1280
1288
  declare class Subscriptions extends BaseClient {
@@ -1290,6 +1298,9 @@ declare class Subscriptions extends BaseClient {
1290
1298
  ok: true
1291
1299
  }>;
1292
1300
  rotateSecret(id: string): Promise<RotateSecretResponse>;
1301
+ /** Send a one-off test webhook to the subscription's URL (built for its
1302
+ * format, SSRF-guarded). Logged as a delivery row, visible via recentDeliveries. */
1303
+ test(id: string): Promise<SubscriptionTestResult>;
1293
1304
  recentDeliveries(id: string): Promise<{
1294
1305
  data: DeliveryRow[]
1295
1306
  }>;
@@ -1828,10 +1839,90 @@ declare function verifyWebhookSignature(rawBody: string, headers: WebhookHeaderI
1828
1839
  * ```
1829
1840
  */
1830
1841
  declare function verifySecondlayerSignature(rawBody: string, headers: WebhookHeaderInput, publicKeyPem: string): boolean;
1842
+ import { RewardSet } from "@secondlayer/shared/node/consensus";
1843
+ import { MerkleProofStep } from "@secondlayer/shared/node/nakamoto";
1844
+ /**
1845
+ * Trustless transaction-inclusion proof verification.
1846
+ *
1847
+ * Given a proof from `GET /v1/index/transactions/:txid/proof`, the consumer
1848
+ * re-derives everything itself — it does NOT trust any value Secondlayer
1849
+ * computed. Anchored level: (1) recompute the txid from the raw tx bytes, (2)
1850
+ * fold it up the merkle path to the header's `tx_merkle_root`, (3) recompute the
1851
+ * header's `block_hash` and `index_block_hash` from the raw header — "this tx is
1852
+ * included in a header any node can corroborate". Consensus level (when the proof
1853
+ * carries a `consensus` field, or a `rewardSet` is passed): additionally recover
1854
+ * the header's signer signatures and confirm ≥70% of reward-set signer weight
1855
+ * signed the block.
1856
+ *
1857
+ * Note: uses Node's crypto via `@secondlayer/shared` (same as the Streams
1858
+ * signature verify); intended for Node/server verification.
1859
+ */
1860
+ interface TransactionProof {
1861
+ txid: string;
1862
+ index_block_hash: string;
1863
+ block_height: number;
1864
+ tx_index: number;
1865
+ /** Raw consensus-serialized transaction bytes (hex). */
1866
+ raw_tx: string;
1867
+ /** Raw Nakamoto block-header bytes (hex) — parsed + re-hashed by the verifier. */
1868
+ raw_header: string;
1869
+ /** Authentication path from the tx leaf to `tx_merkle_root`. */
1870
+ tx_merkle_path: MerkleProofStep[];
1871
+ /** Present when consensus-level verification is available: the reward cycle and
1872
+ * its signer set, against which the header's signer signatures are checked. */
1873
+ consensus?: {
1874
+ reward_cycle: number
1875
+ reward_set: RewardSet
1876
+ };
1877
+ }
1878
+ interface TransactionProofVerifyResult {
1879
+ /** Highest level actually verified. "consensus" requires the proof's
1880
+ * `consensus` field and a met signer-weight threshold. */
1881
+ level: "anchored" | "consensus";
1882
+ /** Recomputed txid === proof.txid. */
1883
+ txidMatches: boolean;
1884
+ /** Merkle path folds the txid to the header's tx_merkle_root. */
1885
+ includedInHeader: boolean;
1886
+ /** Recomputed block_hash + index_block_hash match the header / proof. */
1887
+ headerSelfConsistent: boolean;
1888
+ /** Basis points (0–10000) of reward-set signer weight that signed the block.
1889
+ * Only set when the proof carries a `consensus` field. */
1890
+ signerWeightBps?: number;
1891
+ /** ≥70% of signer weight signed. Only set with a `consensus` field. */
1892
+ thresholdMet?: boolean;
1893
+ /** Which reward set the signer check used: "provided" (caller-resolved →
1894
+ * fully trustless) or "embedded" (the one Secondlayer put in the proof). */
1895
+ rewardSetSource?: "provided" | "embedded";
1896
+ /** All applicable checks passed (incl. the threshold when consensus is present). */
1897
+ ok: boolean;
1898
+ errors: string[];
1899
+ }
1900
+ /**
1901
+ * Resolve a reward set directly from a stacks-node (`/v3/stacker_set/{cycle}`),
1902
+ * so a caller can verify the consensus layer against a node IT trusts rather than
1903
+ * the reward set Secondlayer embedded in the proof. Pass the result as
1904
+ * `verifyTransactionProof(proof, { rewardSet })`.
1905
+ */
1906
+ declare function fetchRewardSet(opts: {
1907
+ nodeUrl: string
1908
+ cycle: number
1909
+ fetchImpl?: typeof fetch
1910
+ }): Promise<RewardSet | null>;
1911
+ /**
1912
+ * Verify a transaction-inclusion proof. Every check is recomputed client-side,
1913
+ * so a `true` result does not rely on trusting Secondlayer. Pass
1914
+ * `{ rewardSet }` (resolved via {@link fetchRewardSet} from your own node) to
1915
+ * verify the consensus layer against a reward set you trust rather than the one
1916
+ * embedded in the proof.
1917
+ */
1918
+ declare function verifyTransactionProof(proof: TransactionProof, opts?: {
1919
+ rewardSet?: RewardSet
1920
+ }): TransactionProofVerifyResult;
1921
+ import { RewardSet as RewardSet2 } from "@secondlayer/shared/node/consensus";
1831
1922
  /** Make a cvToValue result JSON-serializable: Clarity (u)ints decode to bigint,
1832
1923
  * which JSON.stringify can't handle — convert recursively to strings. */
1833
1924
  declare function toJsonSafe(value: unknown): unknown;
1834
1925
  /** Decode a hex-encoded Clarity value to JSON-safe JS (uints as strings,
1835
1926
  * buffers as `0x…` hex, tuples as objects). Returns the input hex on failure. */
1836
1927
  declare function decodeClarityValue(hex: string): unknown;
1837
- export { verifyWebhookSignature, verifySecondlayerSignature, trigger, toJsonSafe, isStxTransfer, isStxMint, isStxLock, isStxBurn, isPrint, isNftTransfer, isNftMint, isNftBurn, isFtTransfer, isFtMint, isFtBurn, getSubgraph, decodeStxTransfer, decodeStxMint, decodeStxLock, decodeStxBurn, decodePrint, decodeNftTransfer, decodeNftMint, decodeNftBurn, decodeFtTransfer, decodeFtMint, decodeFtBurn, decodeClarityValue, createStreamsClient, VersionConflictError, ValidationError, UpdateSubscriptionRequest2 as UpdateSubscriptionRequest, TransactionsWalkParams, TransactionsListParams, TransactionsEnvelope, TransactionEnvelope, Subscriptions, SubscriptionSummary2 as SubscriptionSummary, SubscriptionStatus, SubscriptionRuntime, SubscriptionKind, SubscriptionFormat, SubscriptionDetail2 as SubscriptionDetail, Subgraphs, SubgraphSpecOptions3 as SubgraphSpecOptions, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphOperationStatus, SubgraphAgentSchema3 as SubgraphAgentSchema, StreamsUsage, StreamsTip, StreamsSignatureError, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorgContext, StreamsReorg, StreamsEventsSubscribeParams, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsDumpsManifest, StreamsDumps, StreamsDumpFile, StreamsClient, StreamsCanonicalBlock, StreamsBatchContext, StackingWalkParams, StackingListParams, StackingEnvelope, SecondLayerOptions, SecondLayer, ScopedKeyProduct, RotateSecretResponse2 as RotateSecretResponse, ReplayResult2 as ReplayResult, RateLimitError, Pox4CallsParams, NftTransfersWalkParams, NftTransfersListParams, NftTransfersEnvelope, NftTransferPayload, NftTransferEvent, NftTransfer, MempoolWalkParams, MempoolTransactionEnvelope, MempoolListParams, MempoolEnvelope, IndexUsage, IndexTransaction, IndexTip, IndexStackingAction, IndexReorg, IndexPostCondition, IndexMempoolTransaction, IndexEventType, IndexEvent, IndexContractCall, IndexCanonicalBlock, IndexBlock, Index, FtTransfersWalkParams, FtTransfersListParams, FtTransfersEnvelope, FtTransferPayload, FtTransferEvent, FtTransfer, FetchLike2 as FetchLike, EventsWalkParams, EventsListParams, EventsEnvelope, DeliveryRow2 as DeliveryRow, DecodedStxTransferPayload, DecodedStxTransfer, DecodedStxMintPayload, DecodedStxMint, DecodedStxLockPayload, DecodedStxLock, DecodedStxBurnPayload, DecodedStxBurn, DecodedPrintValue, DecodedPrintPayload, DecodedPrint, DecodedNftTransferPayload, DecodedNftTransfer, DecodedNftMintPayload, DecodedNftMint, DecodedNftBurnPayload, DecodedNftBurn, DecodedFtTransferPayload, DecodedFtTransfer, DecodedFtMintPayload, DecodedFtMint, DecodedFtBurnPayload, DecodedFtBurn, DecodedEventRow, DecodedEventColumns, DeadRow2 as DeadRow, Datasets, DatasetRow, CursorListParams, CursorEnvelope, Cursor, CreateSubscriptionResponse2 as CreateSubscriptionResponse, CreateSubscriptionRequest2 as CreateSubscriptionRequest, CreateApiKeyResponse, CreateApiKeyParams, ContractsListParams, ContractsEnvelope, Contracts, ContractSummary, ContractConformance, ContractCallsWalkParams, ContractCallsListParams, ContractCallsEnvelope, ContextSnapshot, ContextAccount, ChainTriggerType, ChainTrigger, CanonicalWalkParams, CanonicalListParams, CanonicalEnvelope, CURSOR_SLUGS, BlocksWalkParams, BlocksListParams, BlocksEnvelope, BlockEnvelope, AuthError, ApiKeys, ApiError, ActiveSubgraphOperation };
1928
+ export { verifyWebhookSignature, verifyTransactionProof, verifySecondlayerSignature, trigger, toJsonSafe, isStxTransfer, isStxMint, isStxLock, isStxBurn, isPrint, isNftTransfer, isNftMint, isNftBurn, isFtTransfer, isFtMint, isFtBurn, getSubgraph, fetchRewardSet, decodeStxTransfer, decodeStxMint, decodeStxLock, decodeStxBurn, decodePrint, decodeNftTransfer, decodeNftMint, decodeNftBurn, decodeFtTransfer, decodeFtMint, decodeFtBurn, decodeClarityValue, createStreamsClient, VersionConflictError, ValidationError, UpdateSubscriptionRequest2 as UpdateSubscriptionRequest, TransactionsWalkParams, TransactionsListParams, TransactionsEnvelope, TransactionProofVerifyResult, TransactionProof, TransactionEnvelope, Subscriptions, SubscriptionSummary2 as SubscriptionSummary, SubscriptionStatus, SubscriptionRuntime, SubscriptionKind, SubscriptionFormat, SubscriptionDetail2 as SubscriptionDetail, Subgraphs, SubgraphSpecOptions3 as SubgraphSpecOptions, SubgraphSpecFormat2 as SubgraphSpecFormat, SubgraphOperationStatus, SubgraphAgentSchema3 as SubgraphAgentSchema, StreamsUsage, StreamsTip, StreamsSignatureError, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorgContext, StreamsReorg, StreamsEventsSubscribeParams, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsDumpsManifest, StreamsDumps, StreamsDumpFile, StreamsClient, StreamsCanonicalBlock, StreamsBatchContext, StackingWalkParams, StackingListParams, StackingEnvelope, SecondLayerOptions, SecondLayer, ScopedKeyProduct, RotateSecretResponse2 as RotateSecretResponse, RewardSet2 as RewardSet, ReplayResult2 as ReplayResult, RateLimitError, Pox4CallsParams, NftTransfersWalkParams, NftTransfersListParams, NftTransfersEnvelope, NftTransferPayload, NftTransferEvent, NftTransfer, MempoolWalkParams, MempoolTransactionEnvelope, MempoolListParams, MempoolEnvelope, IndexUsage, IndexTransaction, IndexTip, IndexStackingAction, IndexReorg, IndexPostCondition, IndexMempoolTransaction, IndexEventType, IndexEvent, IndexContractCall, IndexCanonicalBlock, IndexBlock, Index, FtTransfersWalkParams, FtTransfersListParams, FtTransfersEnvelope, FtTransferPayload, FtTransferEvent, FtTransfer, FetchLike2 as FetchLike, EventsWalkParams, EventsListParams, EventsEnvelope, DeliveryRow2 as DeliveryRow, DecodedStxTransferPayload, DecodedStxTransfer, DecodedStxMintPayload, DecodedStxMint, DecodedStxLockPayload, DecodedStxLock, DecodedStxBurnPayload, DecodedStxBurn, DecodedPrintValue, DecodedPrintPayload, DecodedPrint, DecodedNftTransferPayload, DecodedNftTransfer, DecodedNftMintPayload, DecodedNftMint, DecodedNftBurnPayload, DecodedNftBurn, DecodedFtTransferPayload, DecodedFtTransfer, DecodedFtMintPayload, DecodedFtMint, DecodedFtBurnPayload, DecodedFtBurn, DecodedEventRow, DecodedEventColumns, DeadRow2 as DeadRow, Datasets, DatasetRow, CursorListParams, CursorEnvelope, Cursor, CreateSubscriptionResponse2 as CreateSubscriptionResponse, CreateSubscriptionRequest2 as CreateSubscriptionRequest, CreateApiKeyResponse, CreateApiKeyParams, ContractsListParams, ContractsEnvelope, Contracts, ContractSummary, ContractConformance, ContractCallsWalkParams, ContractCallsListParams, ContractCallsEnvelope, ContextSnapshot, ContextAccount, ChainTriggerType, ChainTrigger, CanonicalWalkParams, CanonicalListParams, CanonicalEnvelope, CURSOR_SLUGS, BlocksWalkParams, BlocksListParams, BlocksEnvelope, BlockEnvelope, AuthError, ApiKeys, ApiError, ActiveSubgraphOperation };
package/dist/index.js CHANGED
@@ -1542,6 +1542,9 @@ class Subscriptions extends BaseClient {
1542
1542
  async rotateSecret(id) {
1543
1543
  return this.request("POST", `/api/subscriptions/${id}/rotate-secret`);
1544
1544
  }
1545
+ async test(id) {
1546
+ return this.request("POST", `/api/subscriptions/${id}/test`);
1547
+ }
1545
1548
  async recentDeliveries(id) {
1546
1549
  return this.request("GET", `/api/subscriptions/${id}/deliveries`);
1547
1550
  }
@@ -1965,8 +1968,84 @@ function verifySecondlayerSignature(rawBody, headers, publicKeyPem) {
1965
1968
  const signature = pickHeader(headers, "x-secondlayer-signature");
1966
1969
  return verifySecondlayerSignatureValues(rawBody, id, signature, publicKeyPem);
1967
1970
  }
1971
+ // src/proofs.ts
1972
+ import {
1973
+ verifySignerSignatures
1974
+ } from "@secondlayer/shared/node/consensus";
1975
+ import {
1976
+ nakamotoBlockHash,
1977
+ nakamotoBlockId,
1978
+ parseNakamotoBlockHeader,
1979
+ stacksTxid,
1980
+ verifyTxMerkleProof
1981
+ } from "@secondlayer/shared/node/nakamoto";
1982
+ var strip = (h) => h.startsWith("0x") ? h.slice(2) : h;
1983
+ var bytes = (h) => Uint8Array.from(Buffer.from(strip(h), "hex"));
1984
+ async function fetchRewardSet(opts) {
1985
+ const f = opts.fetchImpl ?? fetch;
1986
+ const res = await f(`${opts.nodeUrl.replace(/\/+$/, "")}/v3/stacker_set/${opts.cycle}`);
1987
+ if (res.status === 404)
1988
+ return null;
1989
+ if (!res.ok) {
1990
+ throw new Error(`/v3/stacker_set/${opts.cycle} returned ${res.status}`);
1991
+ }
1992
+ const body = await res.json();
1993
+ const signers = body.stacker_set.signers.map((s) => ({
1994
+ signing_key: strip(s.signing_key),
1995
+ weight: s.weight
1996
+ }));
1997
+ return {
1998
+ signers,
1999
+ total_weight: signers.reduce((sum, s) => sum + s.weight, 0)
2000
+ };
2001
+ }
2002
+ function verifyTransactionProof(proof, opts) {
2003
+ const errors = [];
2004
+ const computedTxid = stacksTxid(bytes(proof.raw_tx));
2005
+ const txidMatches = computedTxid === strip(proof.txid);
2006
+ if (!txidMatches)
2007
+ errors.push("txid does not match raw_tx");
2008
+ const header = parseNakamotoBlockHeader(bytes(proof.raw_header));
2009
+ const includedInHeader = verifyTxMerkleProof(computedTxid, proof.tx_merkle_path, header.txMerkleRoot);
2010
+ if (!includedInHeader)
2011
+ errors.push("merkle path does not reach tx_merkle_root");
2012
+ const blockHash = nakamotoBlockHash(header);
2013
+ const indexBlockHash = nakamotoBlockId(blockHash, header.consensusHash);
2014
+ const headerSelfConsistent = indexBlockHash === strip(proof.index_block_hash);
2015
+ if (!headerSelfConsistent) {
2016
+ errors.push("recomputed index_block_hash does not match proof");
2017
+ }
2018
+ const anchoredOk = txidMatches && includedInHeader && headerSelfConsistent;
2019
+ const rewardSet = opts?.rewardSet ?? proof.consensus?.reward_set;
2020
+ let level = "anchored";
2021
+ let signerWeightBps;
2022
+ let thresholdMet;
2023
+ let rewardSetSource;
2024
+ if (rewardSet) {
2025
+ const v = verifySignerSignatures(blockHash, header.signerSignatures, rewardSet);
2026
+ signerWeightBps = v.totalWeight > 0 ? Math.round(v.signedWeight / v.totalWeight * 1e4) : 0;
2027
+ thresholdMet = v.thresholdMet;
2028
+ rewardSetSource = opts?.rewardSet ? "provided" : "embedded";
2029
+ if (!thresholdMet)
2030
+ errors.push("signer weight below the 70% threshold");
2031
+ if (anchoredOk && thresholdMet)
2032
+ level = "consensus";
2033
+ }
2034
+ return {
2035
+ level,
2036
+ txidMatches,
2037
+ includedInHeader,
2038
+ headerSelfConsistent,
2039
+ signerWeightBps,
2040
+ thresholdMet,
2041
+ rewardSetSource,
2042
+ ok: anchoredOk && (rewardSet ? thresholdMet === true : true),
2043
+ errors
2044
+ };
2045
+ }
1968
2046
  export {
1969
2047
  verifyWebhookSignature,
2048
+ verifyTransactionProof,
1970
2049
  verifySecondlayerSignature,
1971
2050
  trigger,
1972
2051
  toJsonSafe,
@@ -1982,6 +2061,7 @@ export {
1982
2061
  isFtMint,
1983
2062
  isFtBurn,
1984
2063
  getSubgraph,
2064
+ fetchRewardSet,
1985
2065
  decodeStxTransfer,
1986
2066
  decodeStxMint,
1987
2067
  decodeStxLock,
@@ -2013,5 +2093,5 @@ export {
2013
2093
  ApiError
2014
2094
  };
2015
2095
 
2016
- //# debugId=7E891A303DCCD65B64756E2164756E21
2096
+ //# debugId=9DCF0337E6524B2364756E2164756E21
2017
2097
  //# sourceMappingURL=index.js.map