@secondlayer/stacks 2.8.1 → 2.9.1
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 +88 -0
- package/dist/shared/{stacks-sfpr4b0y.js → stacks-53bjdb6p.js} +2 -2
- package/dist/shared/{stacks-9z833ww5.js → stacks-5nmz5ad9.js} +3 -3
- package/dist/shared/{stacks-9z833ww5.js.map → stacks-5nmz5ad9.js.map} +1 -1
- package/dist/shared/{stacks-s9148m6h.js → stacks-8mj5jw77.js} +6 -4
- package/dist/shared/{stacks-s9148m6h.js.map → stacks-8mj5jw77.js.map} +2 -2
- package/dist/shared/stacks-cdvn8qbh.cjs +20 -0
- package/dist/shared/stacks-cdvn8qbh.cjs.map +10 -0
- package/dist/shared/{stacks-y9txtw3p.cjs → stacks-maamjqma.cjs} +2 -20
- package/dist/shared/{stacks-y9txtw3p.cjs.map → stacks-maamjqma.cjs.map} +3 -4
- package/dist/shared/stacks-q1mjkt15.js +34 -0
- package/dist/shared/stacks-q1mjkt15.js.map +10 -0
- package/dist/shared/{stacks-a63xq06y.js → stacks-z1j5d6fd.js} +3 -21
- package/dist/shared/{stacks-a63xq06y.js.map → stacks-z1j5d6fd.js.map} +3 -4
- package/dist/src/actions/index.js +7 -5
- package/dist/src/actions/index.js.map +1 -1
- package/dist/src/bitcoin/index.cjs +695 -0
- package/dist/src/bitcoin/index.cjs.map +19 -0
- package/dist/src/bitcoin/index.d.cts +752 -0
- package/dist/src/bitcoin/index.d.ts +752 -0
- package/dist/src/bitcoin/index.js +720 -0
- package/dist/src/bitcoin/index.js.map +19 -0
- package/dist/src/bns/index.js +4 -3
- package/dist/src/bns/index.js.map +2 -2
- package/dist/src/index.js +6 -4
- package/dist/src/index.js.map +2 -2
- package/dist/src/pox/index.js +5 -4
- package/dist/src/pox/index.js.map +2 -2
- package/dist/src/sbtc/index.js +5 -4
- package/dist/src/sbtc/index.js.map +2 -2
- package/dist/src/stackingdao/index.js +4 -3
- package/dist/src/stackingdao/index.js.map +2 -2
- package/package.json +8 -4
- /package/dist/shared/{stacks-sfpr4b0y.js.map → stacks-53bjdb6p.js.map} +0 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
type IntCV = {
|
|
2
|
+
readonly type: "int"
|
|
3
|
+
readonly value: bigint
|
|
4
|
+
};
|
|
5
|
+
type UIntCV = {
|
|
6
|
+
readonly type: "uint"
|
|
7
|
+
readonly value: bigint
|
|
8
|
+
};
|
|
9
|
+
type BooleanCV = TrueCV | FalseCV;
|
|
10
|
+
type TrueCV = {
|
|
11
|
+
readonly type: "true"
|
|
12
|
+
};
|
|
13
|
+
type FalseCV = {
|
|
14
|
+
readonly type: "false"
|
|
15
|
+
};
|
|
16
|
+
type BufferCV = {
|
|
17
|
+
readonly type: "buffer"
|
|
18
|
+
readonly value: string
|
|
19
|
+
};
|
|
20
|
+
type NoneCV = {
|
|
21
|
+
readonly type: "none"
|
|
22
|
+
};
|
|
23
|
+
type SomeCV = {
|
|
24
|
+
readonly type: "some"
|
|
25
|
+
readonly value: ClarityValue
|
|
26
|
+
};
|
|
27
|
+
type ResponseOkCV = {
|
|
28
|
+
readonly type: "ok"
|
|
29
|
+
readonly value: ClarityValue
|
|
30
|
+
};
|
|
31
|
+
type ResponseErrorCV = {
|
|
32
|
+
readonly type: "err"
|
|
33
|
+
readonly value: ClarityValue
|
|
34
|
+
};
|
|
35
|
+
type StandardPrincipalCV = {
|
|
36
|
+
readonly type: "address"
|
|
37
|
+
readonly value: string
|
|
38
|
+
};
|
|
39
|
+
type ContractPrincipalCV = {
|
|
40
|
+
readonly type: "contract"
|
|
41
|
+
readonly value: string
|
|
42
|
+
};
|
|
43
|
+
type ListCV = {
|
|
44
|
+
type: "list"
|
|
45
|
+
value: ClarityValue[]
|
|
46
|
+
};
|
|
47
|
+
type TupleData = {
|
|
48
|
+
[key: string]: ClarityValue
|
|
49
|
+
};
|
|
50
|
+
type TupleCV = {
|
|
51
|
+
type: "tuple"
|
|
52
|
+
value: TupleData
|
|
53
|
+
};
|
|
54
|
+
type StringAsciiCV = {
|
|
55
|
+
readonly type: "ascii"
|
|
56
|
+
readonly value: string
|
|
57
|
+
};
|
|
58
|
+
type StringUtf8CV = {
|
|
59
|
+
readonly type: "utf8"
|
|
60
|
+
readonly value: string
|
|
61
|
+
};
|
|
62
|
+
type ClarityValue = IntCV | UIntCV | BooleanCV | BufferCV | NoneCV | SomeCV | ResponseOkCV | ResponseErrorCV | StandardPrincipalCV | ContractPrincipalCV | ListCV | TupleCV | StringAsciiCV | StringUtf8CV;
|
|
63
|
+
/**
|
|
64
|
+
* Bitcoin double-SHA256: `sha256(sha256(bytes))`. This is the hash Bitcoin uses
|
|
65
|
+
* for txids, block headers, and merkle nodes — NOT the same as the Stacks
|
|
66
|
+
* `txidFromBytes` (which uses sha512_256), so it lives here, not in `utils/hash`.
|
|
67
|
+
*/
|
|
68
|
+
declare function doubleSha256(bytes: Uint8Array): Uint8Array;
|
|
69
|
+
/**
|
|
70
|
+
* Reverse a byte array. Bitcoin hashes are computed and stored in *internal*
|
|
71
|
+
* (little-endian) order but *displayed* big-endian, so the two differ by a
|
|
72
|
+
* reversal. The SIP-044 built-ins consume internal order; only reverse for
|
|
73
|
+
* display / when matching an explorer value.
|
|
74
|
+
*/
|
|
75
|
+
declare function reverseBytes(bytes: Uint8Array): Uint8Array;
|
|
76
|
+
/**
|
|
77
|
+
* A little-endian, varint-aware byte reader for Bitcoin serialization. The
|
|
78
|
+
* shared `utils/BytesReader` is big-endian only; Bitcoin tx fields are
|
|
79
|
+
* little-endian with compact-size (varint) length prefixes.
|
|
80
|
+
*/
|
|
81
|
+
declare class BtcReader {
|
|
82
|
+
private readonly data;
|
|
83
|
+
private readonly view;
|
|
84
|
+
offset: number;
|
|
85
|
+
constructor(data: Uint8Array);
|
|
86
|
+
get length(): number;
|
|
87
|
+
readUInt8(): number;
|
|
88
|
+
/** Read the next byte without advancing. */
|
|
89
|
+
peekUInt8(): number;
|
|
90
|
+
readUInt32LE(): number;
|
|
91
|
+
readUInt64LE(): bigint;
|
|
92
|
+
readBytes(n: number): Uint8Array;
|
|
93
|
+
/** Read a Bitcoin compact-size unsigned integer (varint). */
|
|
94
|
+
readVarInt(): number;
|
|
95
|
+
}
|
|
96
|
+
interface BitcoinTxInput {
|
|
97
|
+
/** Previous output txid, in serialized (internal) byte order. */
|
|
98
|
+
prevTxid: Uint8Array;
|
|
99
|
+
vout: number;
|
|
100
|
+
scriptSig: Uint8Array;
|
|
101
|
+
sequence: number;
|
|
102
|
+
}
|
|
103
|
+
interface BitcoinTxOutput {
|
|
104
|
+
/** Output value in satoshis. */
|
|
105
|
+
value: bigint;
|
|
106
|
+
scriptPubKey: Uint8Array;
|
|
107
|
+
}
|
|
108
|
+
interface ParsedBitcoinTx {
|
|
109
|
+
version: number;
|
|
110
|
+
hasWitness: boolean;
|
|
111
|
+
inputs: BitcoinTxInput[];
|
|
112
|
+
outputs: BitcoinTxOutput[];
|
|
113
|
+
locktime: number;
|
|
114
|
+
/**
|
|
115
|
+
* The legacy txid as the raw double-SHA256 in *internal* byte order — ready
|
|
116
|
+
* to pass as the leaf to `verify-merkle-proof`. Witness data is excluded
|
|
117
|
+
* (the txid is computed over the legacy serialization), so this is stable for
|
|
118
|
+
* both legacy and SegWit txs. Use `reverseBytes` for the displayed form.
|
|
119
|
+
*/
|
|
120
|
+
txidInternal: Uint8Array;
|
|
121
|
+
}
|
|
122
|
+
/** Parse a serialized Bitcoin tx (legacy or SegWit) into its fields + txid. */
|
|
123
|
+
declare function parseBitcoinTx(rawTx: Uint8Array): ParsedBitcoinTx;
|
|
124
|
+
/**
|
|
125
|
+
* Return the legacy (witness-stripped) serialization of a tx. For a legacy tx
|
|
126
|
+
* this is the input unchanged; for a SegWit tx the marker, flag, and witness
|
|
127
|
+
* stack are removed. This is the byte string the txid is hashed over.
|
|
128
|
+
*/
|
|
129
|
+
declare function stripWitness(rawTx: Uint8Array): Uint8Array;
|
|
130
|
+
/**
|
|
131
|
+
* Compute a tx's id from its raw serialization. Internal byte order by default
|
|
132
|
+
* (the merkle leaf / built-in input); pass `{ display: true }` for the
|
|
133
|
+
* explorer-style big-endian form.
|
|
134
|
+
*/
|
|
135
|
+
declare function bitcoinTxid(rawTx: Uint8Array, { display }?: {
|
|
136
|
+
display?: boolean
|
|
137
|
+
}): Uint8Array;
|
|
138
|
+
interface BlockHeader {
|
|
139
|
+
version: number;
|
|
140
|
+
/** Previous block hash, internal byte order. */
|
|
141
|
+
prevBlock: Uint8Array;
|
|
142
|
+
/** Merkle root, internal byte order — pairs with `verify-merkle-proof`. */
|
|
143
|
+
merkleRoot: Uint8Array;
|
|
144
|
+
timestamp: number;
|
|
145
|
+
bits: number;
|
|
146
|
+
nonce: number;
|
|
147
|
+
}
|
|
148
|
+
/** Parse an 80-byte Bitcoin block header into its fields (hashes internal order). */
|
|
149
|
+
declare function parseBlockHeader(header: Uint8Array): BlockHeader;
|
|
150
|
+
/**
|
|
151
|
+
* Hash an 80-byte block header (double-SHA256). Internal byte order by default;
|
|
152
|
+
* `{ display: true }` for the explorer-style block hash.
|
|
153
|
+
*/
|
|
154
|
+
declare function blockHash(header: Uint8Array, { display }?: {
|
|
155
|
+
display?: boolean
|
|
156
|
+
}): Uint8Array;
|
|
157
|
+
/**
|
|
158
|
+
* A Bitcoin merkle inclusion proof, shaped for the SIP-044 `verify-merkle-proof`
|
|
159
|
+
* built-in: `(leaf-hash, root-hash, tx-index, tx-count, sibling-hashes)`.
|
|
160
|
+
*
|
|
161
|
+
* - `siblings` are the sibling node hashes from the leaf up to (excluding) the
|
|
162
|
+
* root, in *internal* byte order — never reversed.
|
|
163
|
+
* - `txCount` (not tree-depth) pins the canonical tree shape; the built-in
|
|
164
|
+
* rejects any proof whose length differs from `ceil(log2(tx-count))`.
|
|
165
|
+
*/
|
|
166
|
+
interface MerkleProof {
|
|
167
|
+
siblings: Uint8Array[];
|
|
168
|
+
txIndex: number;
|
|
169
|
+
txCount: number;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Compute the merkle root over txids in *internal* byte order. The result is
|
|
173
|
+
* also internal order — it matches the `merkle-root` field read straight out of
|
|
174
|
+
* an 80-byte block header, so it can be cross-checked against the header before
|
|
175
|
+
* a proof is trusted.
|
|
176
|
+
*/
|
|
177
|
+
declare function merkleRoot2(txidsInternal: Uint8Array[]): Uint8Array;
|
|
178
|
+
/**
|
|
179
|
+
* Build the merkle inclusion proof for the tx at `txIndex`. Sibling count is
|
|
180
|
+
* exactly `ceil(log2(txCount))`, as the SIP-044 built-in requires.
|
|
181
|
+
*/
|
|
182
|
+
declare function buildMerkleProof(txidsInternal: Uint8Array[], txIndex: number): MerkleProof;
|
|
183
|
+
/**
|
|
184
|
+
* Recompute the merkle root from a leaf + its proof — the same fold the on-chain
|
|
185
|
+
* `verify-merkle-proof` performs. Use it off-chain to self-check a constructed
|
|
186
|
+
* proof against the header's merkle root before submitting a contract call.
|
|
187
|
+
*/
|
|
188
|
+
declare function rootFromProof(leafInternal: Uint8Array, proof: MerkleProof): Uint8Array;
|
|
189
|
+
/**
|
|
190
|
+
* A self-contained Bitcoin SPV proof: everything the SIP-044 built-ins need to
|
|
191
|
+
* prove a tx (and one of its outputs) is committed in a confirmed block. Hashes
|
|
192
|
+
* are internal byte order throughout.
|
|
193
|
+
*/
|
|
194
|
+
interface SpvProof {
|
|
195
|
+
rawTx: Uint8Array;
|
|
196
|
+
/** The tx's txid, internal order — the merkle leaf. */
|
|
197
|
+
txidInternal: Uint8Array;
|
|
198
|
+
/** Output index of interest, if the proof targets a specific output. */
|
|
199
|
+
vout?: number;
|
|
200
|
+
merkle: MerkleProof;
|
|
201
|
+
/** The 80-byte block header that commits the tx. */
|
|
202
|
+
header: Uint8Array;
|
|
203
|
+
/** Bitcoin block height. */
|
|
204
|
+
height: number;
|
|
205
|
+
}
|
|
206
|
+
/** The block context a `ProofSource` resolves for a confirmed tx. */
|
|
207
|
+
interface BlockForTx {
|
|
208
|
+
/** 80-byte block header. */
|
|
209
|
+
header: Uint8Array;
|
|
210
|
+
height: number;
|
|
211
|
+
/** All of the block's txids, internal order, in block order. */
|
|
212
|
+
txidsInternal: Uint8Array[];
|
|
213
|
+
/** Index of the target tx within the block. */
|
|
214
|
+
txIndex: number;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Where proof inputs come from. The default is the integrator's own Bitcoin node
|
|
218
|
+
* (`bitcoinRpcSource`) — trustless; a hosted Esplora-compatible endpoint
|
|
219
|
+
* (`esploraSource`) is the fallback. `buildTxProof` independently re-checks
|
|
220
|
+
* whatever a source returns, so a wrong or hostile source fails loudly rather
|
|
221
|
+
* than producing a bad proof.
|
|
222
|
+
*/
|
|
223
|
+
interface ProofSource {
|
|
224
|
+
/** Raw (serialized) tx bytes for a txid (display-order hex). */
|
|
225
|
+
getRawTx(txid: string): Promise<Uint8Array>;
|
|
226
|
+
/** The confirming block's header, height, and txid set for a txid. */
|
|
227
|
+
getBlockForTx(txid: string): Promise<BlockForTx>;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Assemble an `SpvProof` for a txid from a `ProofSource`, validating every claim
|
|
231
|
+
* the source makes:
|
|
232
|
+
* - the returned raw tx actually hashes to the requested txid,
|
|
233
|
+
* - the claimed `txIndex` points at that txid in the block, and
|
|
234
|
+
* - the resulting merkle proof folds back to the block header's merkle root.
|
|
235
|
+
* Any mismatch throws — the proof is never returned half-trusted.
|
|
236
|
+
*/
|
|
237
|
+
declare function buildTxProof(source: ProofSource, params: {
|
|
238
|
+
txid: string
|
|
239
|
+
vout?: number
|
|
240
|
+
}): Promise<SpvProof>;
|
|
241
|
+
/**
|
|
242
|
+
* Compose sources into an ordered fallback chain: each call tries them in turn
|
|
243
|
+
* and returns the first success, throwing the last error if all fail. Put the
|
|
244
|
+
* trustless integrator node first and a hosted endpoint last.
|
|
245
|
+
*/
|
|
246
|
+
declare function fallbackProofSource(sources: ProofSource[]): ProofSource;
|
|
247
|
+
interface BitcoinRpcConfig {
|
|
248
|
+
/** Bitcoin Core JSON-RPC endpoint URL. */
|
|
249
|
+
url: string;
|
|
250
|
+
/** Basic auth — `{ username, password }` or a pre-encoded base64 string. */
|
|
251
|
+
auth?: {
|
|
252
|
+
username: string
|
|
253
|
+
password: string
|
|
254
|
+
} | string;
|
|
255
|
+
/** Override the fetch implementation (testing / custom agents). */
|
|
256
|
+
fetch?: typeof fetch;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* A `ProofSource` backed by the integrator's own Bitcoin Core node over
|
|
260
|
+
* JSON-RPC. This is the trustless default. The node must run with `-txindex`
|
|
261
|
+
* (or the tx must be in the mempool's block view) so `getrawtransaction` can
|
|
262
|
+
* resolve the confirming block.
|
|
263
|
+
*/
|
|
264
|
+
declare function bitcoinRpcSource(config: BitcoinRpcConfig): ProofSource;
|
|
265
|
+
interface EsploraConfig {
|
|
266
|
+
/** Esplora REST base URL, e.g. `https://blockstream.info/api` or a self-hosted instance. */
|
|
267
|
+
url: string;
|
|
268
|
+
/** Override the fetch implementation. */
|
|
269
|
+
fetch?: typeof fetch;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* A `ProofSource` backed by an Esplora REST API (self-hosted, or a hosted
|
|
273
|
+
* provider as a fallback). Provider-agnostic: any Esplora-compatible endpoint
|
|
274
|
+
* works. Use as the hosted fallback behind `bitcoinRpcSource`.
|
|
275
|
+
*/
|
|
276
|
+
declare function esploraSource(config: EsploraConfig): ProofSource;
|
|
277
|
+
/**
|
|
278
|
+
* Encode the argument vector for the SIP-044 native built-in
|
|
279
|
+
* `(verify-merkle-proof leaf-hash root-hash tx-index tx-count sibling-hashes)`.
|
|
280
|
+
*
|
|
281
|
+
* Returns the five args in order: `[leaf, root, tx-index, tx-count, siblings]`.
|
|
282
|
+
* All hashes are passed in *internal* (raw) byte order — the built-in does NOT
|
|
283
|
+
* reverse, and neither does this. The leaf is the tx's `txidInternal`; the root
|
|
284
|
+
* is the block header's merkle root in internal order.
|
|
285
|
+
*
|
|
286
|
+
* Throws on shapes the built-in would reject, so a caller fails locally with a
|
|
287
|
+
* clear message instead of an opaque on-chain `false`.
|
|
288
|
+
*/
|
|
289
|
+
declare function encodeMerkleProofArgs(params: {
|
|
290
|
+
leaf: Uint8Array
|
|
291
|
+
root: Uint8Array
|
|
292
|
+
proof: MerkleProof
|
|
293
|
+
}): [BufferCV, BufferCV, UIntCV, UIntCV, ListCV];
|
|
294
|
+
interface DecodedTxOutput {
|
|
295
|
+
/** scriptPubKey bytes. */
|
|
296
|
+
script: Uint8Array;
|
|
297
|
+
/** Output value in satoshis. */
|
|
298
|
+
amount: bigint;
|
|
299
|
+
/** The tx's txid in internal byte order — ready as a merkle leaf. */
|
|
300
|
+
txid: Uint8Array;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Decode the tuple returned by `get-bitcoin-tx-output?`:
|
|
304
|
+
* `(tuple (script (buff 1024)) (amount uint) (txid (buff 32)))`.
|
|
305
|
+
*
|
|
306
|
+
* Accepts either the bare tuple or a `(response ok ...)` wrapping it, so it
|
|
307
|
+
* works whether or not the caller has already unwrapped the response.
|
|
308
|
+
*/
|
|
309
|
+
declare function decodeTxOutput(cv: ClarityValue): DecodedTxOutput;
|
|
310
|
+
type OutputScriptType = "p2pkh" | "p2sh" | "p2wpkh" | "p2wsh" | "p2tr" | "op_return" | "unknown";
|
|
311
|
+
interface ParsedOutputScript {
|
|
312
|
+
type: OutputScriptType;
|
|
313
|
+
/**
|
|
314
|
+
* The hash / witness program for the recognized output types: the 20-byte
|
|
315
|
+
* pubkey-hash (p2pkh), 20-byte script-hash (p2sh), or the 20/32-byte witness
|
|
316
|
+
* program (p2wpkh/p2wsh/p2tr). Undefined for op_return / unknown.
|
|
317
|
+
*
|
|
318
|
+
* Address formatting is intentionally left to the caller — base58/bech32 HRPs
|
|
319
|
+
* and version bytes are network-dependent, so it belongs where the network is
|
|
320
|
+
* known (see `verifyBitcoinPayment`), not in this pure decoder.
|
|
321
|
+
*/
|
|
322
|
+
hash?: Uint8Array;
|
|
323
|
+
/** OP_RETURN payload (the pushed data after the OP_RETURN opcode). */
|
|
324
|
+
data?: Uint8Array;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Classify a Bitcoin output scriptPubKey by its standard template and surface
|
|
328
|
+
* the embedded hash / witness program. Recognizes P2PKH, P2SH, P2WPKH, P2WSH,
|
|
329
|
+
* P2TR, and OP_RETURN; anything else is `unknown`.
|
|
330
|
+
*/
|
|
331
|
+
declare function parseOutputScript(script: Uint8Array): ParsedOutputScript;
|
|
332
|
+
type BitcoinNetwork = "mainnet" | "testnet";
|
|
333
|
+
interface SpvAdapterRef {
|
|
334
|
+
/** Deployer principal. */
|
|
335
|
+
address: string;
|
|
336
|
+
/** Contract name. */
|
|
337
|
+
name: string;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Reference `spv-adapter` deployments (the read-only wrapper around the SIP-044
|
|
341
|
+
* built-ins) — the single source of truth for the published adapter principal.
|
|
342
|
+
* Empty until Clarity 6 / Epoch 4.0 activates and the contract is deployed; drop
|
|
343
|
+
* the real principal in here at that point (deploy recipe: `contracts/README.md`).
|
|
344
|
+
* Until then, `verifyBitcoinPayment` requires an explicit `contract`.
|
|
345
|
+
*/
|
|
346
|
+
declare const SPV_ADAPTER_CONTRACTS: Partial<Record<BitcoinNetwork, SpvAdapterRef>>;
|
|
347
|
+
/** Resolve the reference adapter for a network, or `undefined` if none is deployed yet. */
|
|
348
|
+
declare function getSpvAdapter(network: BitcoinNetwork): SpvAdapterRef | undefined;
|
|
349
|
+
/** A `"address.name"` contract principal from an adapter ref. */
|
|
350
|
+
declare function spvAdapterPrincipal(ref: SpvAdapterRef): string;
|
|
351
|
+
/**
|
|
352
|
+
* ABI for the reference `spv-adapter` contract — a thin, read-only wrapper that
|
|
353
|
+
* exposes the SIP-044 Bitcoin built-ins (callable only from within a Clarity
|
|
354
|
+
* contract) over read-only RPC. Plan 013 deploys a contract matching this shape;
|
|
355
|
+
* `bitcoinVerifier` binds to it (or to an integrator's own contract with the
|
|
356
|
+
* same surface).
|
|
357
|
+
*
|
|
358
|
+
* Hashes are internal (raw) byte order — the same as the built-ins. Do NOT
|
|
359
|
+
* reverse before calling.
|
|
360
|
+
*/
|
|
361
|
+
declare const SPV_ADAPTER_ABI: {
|
|
362
|
+
readonly functions: readonly [{
|
|
363
|
+
readonly name: "get-tx-output"
|
|
364
|
+
readonly access: "read-only"
|
|
365
|
+
readonly args: readonly [{
|
|
366
|
+
readonly name: "tx"
|
|
367
|
+
readonly type: {
|
|
368
|
+
readonly buff: {
|
|
369
|
+
readonly length: 4096
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}, {
|
|
373
|
+
readonly name: "vout"
|
|
374
|
+
readonly type: "uint128"
|
|
375
|
+
}]
|
|
376
|
+
readonly outputs: {
|
|
377
|
+
readonly response: {
|
|
378
|
+
readonly ok: {
|
|
379
|
+
readonly tuple: readonly [{
|
|
380
|
+
readonly name: "script"
|
|
381
|
+
readonly type: {
|
|
382
|
+
readonly buff: {
|
|
383
|
+
readonly length: 1024
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}, {
|
|
387
|
+
readonly name: "amount"
|
|
388
|
+
readonly type: "uint128"
|
|
389
|
+
}, {
|
|
390
|
+
readonly name: "txid"
|
|
391
|
+
readonly type: {
|
|
392
|
+
readonly buff: {
|
|
393
|
+
readonly length: 32
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}]
|
|
397
|
+
}
|
|
398
|
+
readonly error: "uint128"
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}, {
|
|
402
|
+
readonly name: "verify-merkle"
|
|
403
|
+
readonly access: "read-only"
|
|
404
|
+
readonly args: readonly [{
|
|
405
|
+
readonly name: "leaf"
|
|
406
|
+
readonly type: {
|
|
407
|
+
readonly buff: {
|
|
408
|
+
readonly length: 32
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}, {
|
|
412
|
+
readonly name: "root"
|
|
413
|
+
readonly type: {
|
|
414
|
+
readonly buff: {
|
|
415
|
+
readonly length: 32
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}, {
|
|
419
|
+
readonly name: "tx-index"
|
|
420
|
+
readonly type: "uint128"
|
|
421
|
+
}, {
|
|
422
|
+
readonly name: "tx-count"
|
|
423
|
+
readonly type: "uint128"
|
|
424
|
+
}, {
|
|
425
|
+
readonly name: "siblings"
|
|
426
|
+
readonly type: {
|
|
427
|
+
readonly list: {
|
|
428
|
+
readonly type: {
|
|
429
|
+
readonly buff: {
|
|
430
|
+
readonly length: 32
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
readonly length: 24
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}]
|
|
437
|
+
readonly outputs: "bool"
|
|
438
|
+
}, {
|
|
439
|
+
readonly name: "was-tx-mined"
|
|
440
|
+
readonly access: "read-only"
|
|
441
|
+
readonly args: readonly [{
|
|
442
|
+
readonly name: "header"
|
|
443
|
+
readonly type: {
|
|
444
|
+
readonly buff: {
|
|
445
|
+
readonly length: 80
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}, {
|
|
449
|
+
readonly name: "height"
|
|
450
|
+
readonly type: "uint128"
|
|
451
|
+
}, {
|
|
452
|
+
readonly name: "leaf"
|
|
453
|
+
readonly type: {
|
|
454
|
+
readonly buff: {
|
|
455
|
+
readonly length: 32
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}, {
|
|
459
|
+
readonly name: "tx-index"
|
|
460
|
+
readonly type: "uint128"
|
|
461
|
+
}, {
|
|
462
|
+
readonly name: "tx-count"
|
|
463
|
+
readonly type: "uint128"
|
|
464
|
+
}, {
|
|
465
|
+
readonly name: "siblings"
|
|
466
|
+
readonly type: {
|
|
467
|
+
readonly list: {
|
|
468
|
+
readonly type: {
|
|
469
|
+
readonly buff: {
|
|
470
|
+
readonly length: 32
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
readonly length: 24
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}]
|
|
477
|
+
readonly outputs: {
|
|
478
|
+
readonly response: {
|
|
479
|
+
readonly ok: "bool"
|
|
480
|
+
readonly error: "uint128"
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}]
|
|
484
|
+
};
|
|
485
|
+
/** Account derived from a local private key (mnemonic or raw key). */
|
|
486
|
+
type LocalAccount = {
|
|
487
|
+
type: "local"
|
|
488
|
+
address: string
|
|
489
|
+
/** Compressed public key (hex) */
|
|
490
|
+
publicKey: string
|
|
491
|
+
/** Raw ECDSA sign over a hash */
|
|
492
|
+
sign(hash: Uint8Array): Uint8Array
|
|
493
|
+
/** Sign a structured message (SIP-018) */
|
|
494
|
+
signMessage(message: string | Uint8Array): string
|
|
495
|
+
};
|
|
496
|
+
/** Account with a user-provided signing function (sync or async). */
|
|
497
|
+
type CustomAccount = {
|
|
498
|
+
type: "custom"
|
|
499
|
+
address: string
|
|
500
|
+
publicKey: string
|
|
501
|
+
sign(hash: Uint8Array): Promise<Uint8Array> | Uint8Array
|
|
502
|
+
};
|
|
503
|
+
/** Browser wallet provider interface (e.g. Leather, Xverse). */
|
|
504
|
+
type StacksProvider = {
|
|
505
|
+
request(method: string, params?: any): Promise<any>
|
|
506
|
+
};
|
|
507
|
+
/** Account backed by a browser wallet {@link StacksProvider}. */
|
|
508
|
+
type ProviderAccount = {
|
|
509
|
+
type: "provider"
|
|
510
|
+
address: string
|
|
511
|
+
publicKey: string
|
|
512
|
+
provider: StacksProvider
|
|
513
|
+
};
|
|
514
|
+
/** Allocates mempool-safe sequential nonces across rapid broadcasts from one account. */
|
|
515
|
+
type NonceManager = {
|
|
516
|
+
consume(params: {
|
|
517
|
+
client: Client
|
|
518
|
+
address: string
|
|
519
|
+
}): Promise<bigint>
|
|
520
|
+
reset(params: {
|
|
521
|
+
client: Client
|
|
522
|
+
address: string
|
|
523
|
+
}): void | Promise<void>
|
|
524
|
+
/** Next nonce that {@link NonceManager.consume} would return without consuming it, or `undefined` if untracked. */
|
|
525
|
+
peek(params: {
|
|
526
|
+
client: Client
|
|
527
|
+
address: string
|
|
528
|
+
}): Promise<bigint | undefined>
|
|
529
|
+
};
|
|
530
|
+
/** Full chain descriptor used by clients and transports for network-aware operations. */
|
|
531
|
+
type StacksChain = {
|
|
532
|
+
/** Chain ID (e.g. 0x00000001 for mainnet) */
|
|
533
|
+
id: number
|
|
534
|
+
/** Human-readable name */
|
|
535
|
+
name: string
|
|
536
|
+
/** Network type */
|
|
537
|
+
network: "mainnet" | "testnet"
|
|
538
|
+
/** Transaction version byte for serialization */
|
|
539
|
+
transactionVersion: number
|
|
540
|
+
/** Peer network ID for P2P broadcasting */
|
|
541
|
+
peerNetworkId: number
|
|
542
|
+
/** Address version bytes */
|
|
543
|
+
addressVersion: {
|
|
544
|
+
singleSig: number
|
|
545
|
+
multiSig: number
|
|
546
|
+
}
|
|
547
|
+
/** Magic bytes for network identification */
|
|
548
|
+
magicBytes: string
|
|
549
|
+
/** Boot address (system contracts deployer) */
|
|
550
|
+
bootAddress: string
|
|
551
|
+
/** Native currency info */
|
|
552
|
+
nativeCurrency: {
|
|
553
|
+
name: string
|
|
554
|
+
symbol: string
|
|
555
|
+
decimals: number
|
|
556
|
+
}
|
|
557
|
+
/** Default RPC URLs */
|
|
558
|
+
rpcUrls: {
|
|
559
|
+
default: {
|
|
560
|
+
http: string[]
|
|
561
|
+
ws?: string[]
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
/** Block explorer URLs */
|
|
565
|
+
blockExplorers?: {
|
|
566
|
+
default: {
|
|
567
|
+
name: string
|
|
568
|
+
url: string
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
/** Function that sends an HTTP request to a Stacks node API path. */
|
|
573
|
+
type RequestFn = (path: string, options?: RequestOptions) => Promise<any>;
|
|
574
|
+
/** Options for a transport-level HTTP request. */
|
|
575
|
+
type RequestOptions = {
|
|
576
|
+
method?: "GET" | "POST" | "PUT" | "DELETE"
|
|
577
|
+
body?: unknown
|
|
578
|
+
headers?: Record<string, string>
|
|
579
|
+
};
|
|
580
|
+
/** Shared configuration for all transport types. */
|
|
581
|
+
type TransportConfig = {
|
|
582
|
+
url?: string
|
|
583
|
+
timeout?: number
|
|
584
|
+
retryCount?: number
|
|
585
|
+
retryDelay?: number
|
|
586
|
+
fetchOptions?: RequestInit
|
|
587
|
+
apiKey?: string
|
|
588
|
+
};
|
|
589
|
+
/** A resolved transport instance with a bound request function. */
|
|
590
|
+
type Transport = {
|
|
591
|
+
type: string
|
|
592
|
+
request: RequestFn
|
|
593
|
+
config: TransportConfig
|
|
594
|
+
destroy?: () => void
|
|
595
|
+
};
|
|
596
|
+
/** Union of all supported account types (local key, custom signer, or browser provider). */
|
|
597
|
+
type Account = LocalAccount | CustomAccount | ProviderAccount;
|
|
598
|
+
/**
|
|
599
|
+
* Core client instance that holds chain context, transport, and extensible actions.
|
|
600
|
+
* Created via {@link createClient}, {@link createPublicClient}, or {@link createWalletClient}.
|
|
601
|
+
*/
|
|
602
|
+
type Client<TExtended extends Record<string, unknown> = Record<string, unknown>> = {
|
|
603
|
+
chain?: StacksChain
|
|
604
|
+
account?: Account
|
|
605
|
+
transport: Transport
|
|
606
|
+
request: RequestFn
|
|
607
|
+
/** Optional nonce manager for mempool-safe sequential nonces across rapid broadcasts. */
|
|
608
|
+
nonceManager?: NonceManager
|
|
609
|
+
extend: <const TNew extends Record<string, unknown>>(fn: (client: Client<TExtended>) => TNew) => Client<TExtended & TNew> & TNew
|
|
610
|
+
} & TExtended;
|
|
611
|
+
/**
|
|
612
|
+
* SIP-044 (the native Bitcoin SPV built-ins) activates as part of the **Stacks
|
|
613
|
+
* Epoch 4.0 hard fork**. The exact activation Bitcoin block height is only
|
|
614
|
+
* selected after the SIP vote passes, so there is no constant to hardcode — a
|
|
615
|
+
* caller passes it once it is known.
|
|
616
|
+
*/
|
|
617
|
+
interface Clarity6Gate {
|
|
618
|
+
/**
|
|
619
|
+
* Bitcoin burn block height at which Clarity 6 / Epoch 4.0 activates. Pass it
|
|
620
|
+
* once the vote selects it; until then `isClarity6Active` cannot answer and
|
|
621
|
+
* throws.
|
|
622
|
+
*/
|
|
623
|
+
activationBurnHeight?: number;
|
|
624
|
+
}
|
|
625
|
+
/** Read the node's current Bitcoin burn block height from `/v2/info`. */
|
|
626
|
+
declare function getBurnBlockHeight(client: Client): Promise<number>;
|
|
627
|
+
/**
|
|
628
|
+
* Whether Clarity 6 (the native SPV built-ins) is active on the node behind
|
|
629
|
+
* `client`. Compares the node's current burn height to the SIP-044 / Epoch 4.0
|
|
630
|
+
* activation height, which must be supplied (it is unknown until the vote — see
|
|
631
|
+
* `Clarity6Gate`). `bitcoinVerifier` uses this to refuse calls before activation
|
|
632
|
+
* rather than failing with an opaque contract error.
|
|
633
|
+
*/
|
|
634
|
+
declare function isClarity6Active(client: Client, gate?: Clarity6Gate): Promise<boolean>;
|
|
635
|
+
interface BitcoinVerifierConfig {
|
|
636
|
+
/** Adapter contract principal, `"address.name"` (the reference `spv-adapter` or an integrator's own). */
|
|
637
|
+
contract: string;
|
|
638
|
+
/** Optional read-only call sender; defaults to the contract address. */
|
|
639
|
+
sender?: string;
|
|
640
|
+
}
|
|
641
|
+
interface BitcoinVerifier {
|
|
642
|
+
/**
|
|
643
|
+
* Verify a merkle inclusion proof against a supplied root via the adapter's
|
|
644
|
+
* `verify-merkle` (the native `verify-merkle-proof`). This proves membership
|
|
645
|
+
* under `root`; authenticating that `root` belongs to a canonical Bitcoin
|
|
646
|
+
* block is a separate step (header → height), composed in `verifyBitcoinPayment`.
|
|
647
|
+
*/
|
|
648
|
+
verifyMerkleProof(input: {
|
|
649
|
+
leaf: Uint8Array
|
|
650
|
+
root: Uint8Array
|
|
651
|
+
proof: MerkleProof
|
|
652
|
+
}): Promise<boolean>;
|
|
653
|
+
/** Verify an `SpvProof`'s merkle inclusion against its own header root (membership only — not chain-authenticated). */
|
|
654
|
+
verifySpvProof(proof: SpvProof): Promise<boolean>;
|
|
655
|
+
/**
|
|
656
|
+
* Full header-authenticated SPV check via the adapter's `was-tx-mined`: the
|
|
657
|
+
* contract authenticates the proof's header against the chain
|
|
658
|
+
* (`get-burn-block-info? header-hash`), extracts its root, and proves
|
|
659
|
+
* inclusion — atomically. Returns `false` if the header isn't canonical at
|
|
660
|
+
* its height or the tx isn't included. This is the real "is it on Bitcoin" check.
|
|
661
|
+
*/
|
|
662
|
+
wasTxMined(proof: SpvProof): Promise<boolean>;
|
|
663
|
+
/**
|
|
664
|
+
* Decode one output of a serialized Bitcoin tx via the adapter's
|
|
665
|
+
* `get-tx-output` (the native `get-bitcoin-tx-output?`).
|
|
666
|
+
*/
|
|
667
|
+
getTxOutput(rawTx: Uint8Array, vout: number): Promise<DecodedTxOutput>;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Bind a `BitcoinVerifier` to a deployed adapter contract. There is a single,
|
|
671
|
+
* native target — no caps and no legacy `clarity-bitcoin` path. The built-ins do
|
|
672
|
+
* not exist until Clarity 6 / Epoch 4.0 activates, so read-only calls only
|
|
673
|
+
* succeed on a node at that epoch (a local Clarity-6 devnet, or mainnet after
|
|
674
|
+
* activation); guard with `isClarity6Active` when in doubt.
|
|
675
|
+
*/
|
|
676
|
+
declare function bitcoinVerifier(client: Client, config: BitcoinVerifierConfig): BitcoinVerifier;
|
|
677
|
+
/**
|
|
678
|
+
* Render a parsed output script as a Bitcoin address for the given network.
|
|
679
|
+
* Returns `undefined` for scripts without a standard address (OP_RETURN, P2PK,
|
|
680
|
+
* unknown). Address encoding is network-dependent, which is why this is separate
|
|
681
|
+
* from the pure `parseOutputScript` decoder.
|
|
682
|
+
*/
|
|
683
|
+
declare function formatBitcoinAddress(parsed: ParsedOutputScript, network?: BitcoinNetwork): string | undefined;
|
|
684
|
+
interface BitcoinPaymentOutput {
|
|
685
|
+
vout: number;
|
|
686
|
+
/** scriptPubKey bytes. */
|
|
687
|
+
script: Uint8Array;
|
|
688
|
+
/** Value in satoshis. */
|
|
689
|
+
amount: bigint;
|
|
690
|
+
type: OutputScriptType;
|
|
691
|
+
/** Address for the configured network, if the script has a standard one. */
|
|
692
|
+
address?: string;
|
|
693
|
+
}
|
|
694
|
+
interface VerifyBitcoinPaymentResult {
|
|
695
|
+
/** `mined` AND every supplied `expect` constraint holds. */
|
|
696
|
+
verified: boolean;
|
|
697
|
+
/**
|
|
698
|
+
* On-chain proof that the tx is committed in a Bitcoin block. With
|
|
699
|
+
* `authenticateHeader` (default), this is the adapter's `was-tx-mined` —
|
|
700
|
+
* header authenticated against the chain AND merkle inclusion. With it off,
|
|
701
|
+
* it is merkle inclusion against the proof's own (unauthenticated) header.
|
|
702
|
+
*/
|
|
703
|
+
mined: boolean;
|
|
704
|
+
/** The decoded output at `vout`. */
|
|
705
|
+
output: BitcoinPaymentOutput;
|
|
706
|
+
/** The proof used (built or supplied). */
|
|
707
|
+
proof: SpvProof;
|
|
708
|
+
}
|
|
709
|
+
type VerifyBitcoinPaymentParams = ({
|
|
710
|
+
proof: SpvProof
|
|
711
|
+
} | {
|
|
712
|
+
txid: string
|
|
713
|
+
source: ProofSource
|
|
714
|
+
}) & {
|
|
715
|
+
/**
|
|
716
|
+
* Adapter contract principal, `"address.name"`. Optional once a reference
|
|
717
|
+
* adapter is published for `network` (see `SPV_ADAPTER_CONTRACTS`); until then
|
|
718
|
+
* it is required.
|
|
719
|
+
*/
|
|
720
|
+
contract?: string
|
|
721
|
+
/** Output index to decode and assert against. */
|
|
722
|
+
vout: number
|
|
723
|
+
/** Network for address formatting. Defaults to mainnet. */
|
|
724
|
+
network?: BitcoinNetwork
|
|
725
|
+
/** Optional expectations; each supplied field must match for `verified`. */
|
|
726
|
+
expect?: {
|
|
727
|
+
address?: string
|
|
728
|
+
amount?: bigint
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Confirm the proof's header is the canonical block at its height via
|
|
732
|
+
* `get-header-merkle-root` (default true). Turn off only when the caller has
|
|
733
|
+
* already authenticated the header.
|
|
734
|
+
*/
|
|
735
|
+
authenticateHeader?: boolean
|
|
736
|
+
sender?: string
|
|
737
|
+
};
|
|
738
|
+
/**
|
|
739
|
+
* Verify that a Bitcoin payment is committed on-chain and (optionally) matches
|
|
740
|
+
* an expected recipient/amount. Composes the whole SPV flow:
|
|
741
|
+
* 1. build the proof from a `ProofSource` (or accept a prepared `SpvProof`),
|
|
742
|
+
* 2. prove the tx is mined — `was-tx-mined` (header authenticated against the
|
|
743
|
+
* chain + merkle inclusion) by default, or membership-only when
|
|
744
|
+
* `authenticateHeader` is off,
|
|
745
|
+
* 3. decode the target output and assert any `expect` constraints.
|
|
746
|
+
*
|
|
747
|
+
* The output is decoded off-chain from the proof's raw tx, which is sound: the
|
|
748
|
+
* raw tx is pinned to the proven txid (`buildTxProof` checks it hashes to the
|
|
749
|
+
* leaf), so its bytes are committed.
|
|
750
|
+
*/
|
|
751
|
+
declare function verifyBitcoinPayment(client: Client, params: VerifyBitcoinPaymentParams): Promise<VerifyBitcoinPaymentResult>;
|
|
752
|
+
export { verifyBitcoinPayment, stripWitness, spvAdapterPrincipal, rootFromProof, reverseBytes, parseOutputScript, parseBlockHeader, parseBitcoinTx, merkleRoot2 as merkleRoot, isClarity6Active, getSpvAdapter, getBurnBlockHeight, formatBitcoinAddress, fallbackProofSource, esploraSource, encodeMerkleProofArgs, doubleSha256, decodeTxOutput, buildTxProof, buildMerkleProof, blockHash, bitcoinVerifier, bitcoinTxid, bitcoinRpcSource, VerifyBitcoinPaymentResult, VerifyBitcoinPaymentParams, SpvProof, SpvAdapterRef, SPV_ADAPTER_CONTRACTS, SPV_ADAPTER_ABI, ProofSource, ParsedOutputScript, ParsedBitcoinTx, OutputScriptType, MerkleProof, EsploraConfig, DecodedTxOutput, Clarity6Gate, BtcReader, BlockHeader, BlockForTx, BitcoinVerifierConfig, BitcoinVerifier, BitcoinTxOutput, BitcoinTxInput, BitcoinRpcConfig, BitcoinPaymentOutput, BitcoinNetwork };
|