@secondlayer/stacks 5.1.3 → 6.1.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 +1 -8
- package/dist/shared/{stacks-qt8ehebn.js → stacks-esm-0rdxcta7.js} +2 -2
- package/dist/shared/{stacks-qt8ehebn.js.map → stacks-esm-0rdxcta7.js.map} +1 -1
- package/dist/shared/{stacks-4hs8r4q2.js → stacks-esm-1jae0a3r.js} +2 -2
- package/dist/shared/{stacks-xjkn3en1.cjs.map → stacks-esm-1jae0a3r.js.map} +1 -1
- package/dist/shared/{stacks-4d7c3x27.js → stacks-esm-2mc7ebrm.js} +3 -3
- package/dist/shared/{stacks-4d7c3x27.js.map → stacks-esm-2mc7ebrm.js.map} +1 -1
- package/dist/shared/{stacks-8brgfvqx.js → stacks-esm-3g33bqsm.js} +4 -4
- package/dist/shared/{stacks-8brgfvqx.js.map → stacks-esm-3g33bqsm.js.map} +1 -1
- package/dist/shared/{stacks-gjz26k6b.js → stacks-esm-3pv5avgr.js} +59 -15
- package/dist/shared/stacks-esm-3pv5avgr.js.map +12 -0
- package/dist/shared/{stacks-qex691bh.js → stacks-esm-743jfh10.js} +6 -6
- package/dist/shared/{stacks-qex691bh.js.map → stacks-esm-743jfh10.js.map} +1 -1
- package/dist/shared/{stacks-wxfd6r4b.js → stacks-esm-7fpqgtyx.js} +3 -3
- package/dist/shared/{stacks-wxfd6r4b.js.map → stacks-esm-7fpqgtyx.js.map} +1 -1
- package/dist/shared/{stacks-71k0vmct.js → stacks-esm-9rnx0zxr.js} +5 -5
- package/dist/shared/{stacks-71k0vmct.js.map → stacks-esm-9rnx0zxr.js.map} +1 -1
- package/dist/shared/{stacks-fd4rx56e.js → stacks-esm-aw2mj3sj.js} +7 -7
- package/dist/shared/{stacks-fd4rx56e.js.map → stacks-esm-aw2mj3sj.js.map} +1 -1
- package/dist/shared/{stacks-qs5fzts5.js → stacks-esm-cvhazvhd.js} +2 -2
- package/dist/shared/{stacks-qs5fzts5.js.map → stacks-esm-cvhazvhd.js.map} +1 -1
- package/dist/shared/{stacks-69nq48jp.js → stacks-esm-fcfdn2zx.js} +5 -5
- package/dist/shared/{stacks-69nq48jp.js.map → stacks-esm-fcfdn2zx.js.map} +1 -1
- package/dist/shared/{stacks-x0qqwrev.js → stacks-esm-hr7fpxtj.js} +4 -4
- package/dist/shared/{stacks-x0qqwrev.js.map → stacks-esm-hr7fpxtj.js.map} +1 -1
- package/dist/shared/{stacks-mbt36bzt.js → stacks-esm-kds4n82p.js} +11 -11
- package/dist/shared/{stacks-mbt36bzt.js.map → stacks-esm-kds4n82p.js.map} +1 -1
- package/dist/shared/{stacks-kg1npstf.js → stacks-esm-n53kvwmt.js} +2 -2
- package/dist/shared/{stacks-kg1npstf.js.map → stacks-esm-n53kvwmt.js.map} +1 -1
- package/dist/shared/{stacks-v2rfr46a.js → stacks-esm-pxhfqfff.js} +4 -4
- package/dist/shared/{stacks-v2rfr46a.js.map → stacks-esm-pxhfqfff.js.map} +1 -1
- package/dist/shared/{stacks-yajyej2y.js → stacks-esm-q19a17fd.js} +3 -3
- package/dist/shared/{stacks-yajyej2y.js.map → stacks-esm-q19a17fd.js.map} +1 -1
- package/dist/shared/{stacks-v03zef3g.js → stacks-esm-qk0p9fr6.js} +9 -9
- package/dist/shared/{stacks-v03zef3g.js.map → stacks-esm-qk0p9fr6.js.map} +1 -1
- package/dist/shared/{stacks-qcae5852.js → stacks-esm-qkj67g80.js} +2 -2
- package/dist/shared/{stacks-qcae5852.js.map → stacks-esm-qkj67g80.js.map} +1 -1
- package/dist/shared/{stacks-mftyrc60.js → stacks-esm-r6dejrgr.js} +4 -4
- package/dist/shared/{stacks-mftyrc60.js.map → stacks-esm-r6dejrgr.js.map} +1 -1
- package/dist/shared/{stacks-9c1yam8b.js → stacks-esm-r9q4wnyw.js} +5 -5
- package/dist/shared/{stacks-9c1yam8b.js.map → stacks-esm-r9q4wnyw.js.map} +1 -1
- package/dist/shared/{stacks-51ybf135.js → stacks-esm-skmcf5n2.js} +5 -5
- package/dist/shared/{stacks-51ybf135.js.map → stacks-esm-skmcf5n2.js.map} +1 -1
- package/dist/shared/{stacks-yvb7rvwk.js → stacks-esm-v45990hx.js} +2 -2
- package/dist/shared/{stacks-8vzxax5e.cjs.map → stacks-esm-v45990hx.js.map} +1 -1
- package/dist/shared/{stacks-z90xdr1x.js → stacks-esm-wv67m4g6.js} +3 -3
- package/dist/shared/{stacks-z90xdr1x.js.map → stacks-esm-wv67m4g6.js.map} +1 -1
- package/dist/shared/{stacks-1m3m1agc.js → stacks-esm-x1r01353.js} +5 -5
- package/dist/shared/{stacks-1m3m1agc.js.map → stacks-esm-x1r01353.js.map} +1 -1
- package/dist/src/accounts/index.cjs +558 -4
- package/dist/src/accounts/index.cjs.map +10 -4
- package/dist/src/accounts/index.js +3 -3
- package/dist/src/actions/index.cjs +3815 -1
- package/dist/src/actions/index.cjs.map +65 -3
- package/dist/src/actions/index.js +14 -14
- package/dist/src/bitcoin/index.cjs +2477 -1
- package/dist/src/bitcoin/index.cjs.map +31 -3
- package/dist/src/bitcoin/index.js +11 -11
- package/dist/src/bns/index.cjs +4439 -17
- package/dist/src/bns/index.cjs.map +50 -3
- package/dist/src/bns/index.js +14 -14
- package/dist/src/chains/index.cjs +135 -1
- package/dist/src/chains/index.cjs.map +5 -3
- package/dist/src/chains/index.js +1 -1
- package/dist/src/clarity/index.cjs +2019 -1
- package/dist/src/clarity/index.cjs.map +24 -3
- package/dist/src/clarity/index.js +8 -8
- package/dist/src/filters/index.cjs +725 -1
- package/dist/src/filters/index.cjs.map +11 -3
- package/dist/src/filters/index.d.cts +76 -13
- package/dist/src/filters/index.d.ts +76 -13
- package/dist/src/filters/index.js +8 -4
- package/dist/src/filters/index.js.map +1 -1
- package/dist/src/index.cjs +3935 -62
- package/dist/src/index.cjs.map +69 -4
- package/dist/src/index.d.cts +62 -11
- package/dist/src/index.d.ts +62 -11
- package/dist/src/index.js +16 -16
- package/dist/src/postconditions/index.cjs +1726 -1
- package/dist/src/postconditions/index.cjs.map +20 -3
- package/dist/src/postconditions/index.js +6 -6
- package/dist/src/pox/index.cjs +3855 -1
- package/dist/src/pox/index.cjs.map +48 -3
- package/dist/src/pox/index.js +13 -13
- package/dist/src/pox5/index.cjs +4054 -29
- package/dist/src/pox5/index.cjs.map +54 -3
- package/dist/src/pox5/index.js +15 -15
- package/dist/src/sbtc/index.cjs +3705 -4
- package/dist/src/sbtc/index.cjs.map +47 -3
- package/dist/src/sbtc/index.js +12 -12
- package/dist/src/stackingdao/index.cjs +3751 -8
- package/dist/src/stackingdao/index.cjs.map +45 -3
- package/dist/src/stackingdao/index.js +12 -12
- package/dist/src/subscriptions/index.cjs +153 -1
- package/dist/src/subscriptions/index.cjs.map +6 -3
- package/dist/src/subscriptions/index.js +2 -2
- package/dist/src/transactions/index.cjs +2139 -1
- package/dist/src/transactions/index.cjs.map +23 -3
- package/dist/src/transactions/index.js +6 -6
- package/dist/src/utils/index.cjs +435 -3
- package/dist/src/utils/index.cjs.map +9 -3
- package/dist/src/utils/index.js +3 -3
- package/package.json +3 -23
- package/dist/shared/stacks-02actsrx.cjs +0 -17
- package/dist/shared/stacks-02actsrx.cjs.map +0 -10
- package/dist/shared/stacks-03vfm21t.cjs +0 -222
- package/dist/shared/stacks-03vfm21t.cjs.map +0 -12
- package/dist/shared/stacks-24h5k94q.cjs +0 -74
- package/dist/shared/stacks-24h5k94q.cjs.map +0 -11
- package/dist/shared/stacks-37kz5t9f.cjs +0 -764
- package/dist/shared/stacks-37kz5t9f.cjs.map +0 -23
- package/dist/shared/stacks-3mt2q2na.cjs +0 -65
- package/dist/shared/stacks-3mt2q2na.cjs.map +0 -9
- package/dist/shared/stacks-4hs8r4q2.js.map +0 -10
- package/dist/shared/stacks-8vzxax5e.cjs +0 -45
- package/dist/shared/stacks-agk106zs.cjs +0 -267
- package/dist/shared/stacks-agk106zs.cjs.map +0 -12
- package/dist/shared/stacks-bdgh9n83.cjs +0 -212
- package/dist/shared/stacks-bdgh9n83.cjs.map +0 -11
- package/dist/shared/stacks-dpxnxhqb.cjs +0 -51
- package/dist/shared/stacks-dpxnxhqb.cjs.map +0 -11
- package/dist/shared/stacks-ee53k9j2.cjs +0 -651
- package/dist/shared/stacks-ee53k9j2.cjs.map +0 -13
- package/dist/shared/stacks-enpj9krt.cjs +0 -38
- package/dist/shared/stacks-enpj9krt.cjs.map +0 -10
- package/dist/shared/stacks-f35mnrmg.cjs +0 -164
- package/dist/shared/stacks-f35mnrmg.cjs.map +0 -10
- package/dist/shared/stacks-fhzwsn5d.cjs +0 -261
- package/dist/shared/stacks-fhzwsn5d.cjs.map +0 -12
- package/dist/shared/stacks-gjz26k6b.js.map +0 -12
- package/dist/shared/stacks-gza2n1zs.cjs +0 -73
- package/dist/shared/stacks-gza2n1zs.cjs.map +0 -11
- package/dist/shared/stacks-kem68d80.cjs +0 -156
- package/dist/shared/stacks-kem68d80.cjs.map +0 -10
- package/dist/shared/stacks-n98w7g57.cjs +0 -234
- package/dist/shared/stacks-n98w7g57.cjs.map +0 -10
- package/dist/shared/stacks-p337hp6m.cjs +0 -336
- package/dist/shared/stacks-p337hp6m.cjs.map +0 -25
- package/dist/shared/stacks-q34k4vnc.cjs +0 -286
- package/dist/shared/stacks-q34k4vnc.cjs.map +0 -12
- package/dist/shared/stacks-rryme9bv.cjs +0 -569
- package/dist/shared/stacks-rryme9bv.cjs.map +0 -13
- package/dist/shared/stacks-s5vej5z6.cjs +0 -410
- package/dist/shared/stacks-s5vej5z6.cjs.map +0 -15
- package/dist/shared/stacks-sdqkapct.cjs +0 -67
- package/dist/shared/stacks-sdqkapct.cjs.map +0 -12
- package/dist/shared/stacks-vea7nde7.cjs +0 -731
- package/dist/shared/stacks-vea7nde7.cjs.map +0 -13
- package/dist/shared/stacks-wk8yp13j.cjs +0 -33
- package/dist/shared/stacks-wk8yp13j.cjs.map +0 -11
- package/dist/shared/stacks-xjkn3en1.cjs +0 -42
- package/dist/shared/stacks-xnrywjf1.cjs +0 -131
- package/dist/shared/stacks-xnrywjf1.cjs.map +0 -11
- package/dist/shared/stacks-yvb7rvwk.js.map +0 -10
- package/dist/src/tools/btc/index.cjs +0 -131
- package/dist/src/tools/btc/index.cjs.map +0 -10
- package/dist/src/tools/btc/index.d.cts +0 -10
- package/dist/src/tools/btc/index.d.ts +0 -10
- package/dist/src/tools/btc/index.js +0 -90
- package/dist/src/tools/btc/index.js.map +0 -10
- package/dist/src/tools/index.cjs +0 -3865
- package/dist/src/tools/index.cjs.map +0 -80
- package/dist/src/tools/index.d.cts +0 -206
- package/dist/src/tools/index.d.ts +0 -206
- package/dist/src/tools/index.js +0 -3827
- package/dist/src/tools/index.js.map +0 -80
package/dist/src/pox5/index.cjs
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
+
var import_node_module = require("node:module");
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
13
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
21
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
|
+
for (let key of __getOwnPropNames(mod))
|
|
24
|
+
if (!__hasOwnProp.call(to, key))
|
|
25
|
+
__defProp(to, key, {
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
27
|
+
enumerable: true
|
|
28
|
+
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
31
|
+
return to;
|
|
32
|
+
};
|
|
33
|
+
var __toCommonJS = (from) => {
|
|
34
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
35
|
+
if (entry)
|
|
36
|
+
return entry;
|
|
37
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
38
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
39
|
+
for (var key of __getOwnPropNames(from))
|
|
40
|
+
if (!__hasOwnProp.call(entry, key))
|
|
41
|
+
__defProp(entry, key, {
|
|
42
|
+
get: __accessProp.bind(from, key),
|
|
43
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
__moduleCache.set(from, entry);
|
|
47
|
+
return entry;
|
|
48
|
+
};
|
|
49
|
+
var __moduleCache;
|
|
50
|
+
var __returnValue = (v) => v;
|
|
51
|
+
function __exportSetter(name, newValue) {
|
|
52
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
53
|
+
}
|
|
54
|
+
var __export = (target, all) => {
|
|
55
|
+
for (var name in all)
|
|
56
|
+
__defProp(target, name, {
|
|
57
|
+
get: all[name],
|
|
58
|
+
enumerable: true,
|
|
59
|
+
configurable: true,
|
|
60
|
+
set: __exportSetter.bind(all, name)
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
1
64
|
// src/pox5/index.ts
|
|
2
65
|
var exports_pox5 = {};
|
|
3
66
|
__export(exports_pox5, {
|
|
@@ -70,6 +133,9 @@ __export(exports_pox5, {
|
|
|
70
133
|
});
|
|
71
134
|
module.exports = __toCommonJS(exports_pox5);
|
|
72
135
|
|
|
136
|
+
// src/epochs.ts
|
|
137
|
+
var EPOCH_4_ACTIVATION_BURN_HEIGHT_MAINNET = 960230;
|
|
138
|
+
|
|
73
139
|
// src/pox5/constants.ts
|
|
74
140
|
var POX5_CONTRACT_NAME = "pox-5";
|
|
75
141
|
var POX5_CONTRACT_ID_MAINNET = `SP000000000000000000002Q6VF78.${POX5_CONTRACT_NAME}`;
|
|
@@ -298,21 +364,3337 @@ var POX5_ERROR_DESCRIPTIONS = {
|
|
|
298
364
|
function parsePox5Error(result) {
|
|
299
365
|
if (result == null)
|
|
300
366
|
return;
|
|
301
|
-
if (typeof result === "string") {
|
|
302
|
-
const match = result.trim().match(/^\(err u(\d+)\)$/);
|
|
303
|
-
return match ? Number(match[1]) : undefined;
|
|
367
|
+
if (typeof result === "string") {
|
|
368
|
+
const match = result.trim().match(/^\(err u(\d+)\)$/);
|
|
369
|
+
return match ? Number(match[1]) : undefined;
|
|
370
|
+
}
|
|
371
|
+
if (result.type !== "err")
|
|
372
|
+
return;
|
|
373
|
+
return result.value.type === "uint" ? Number(result.value.value) : undefined;
|
|
374
|
+
}
|
|
375
|
+
function describePox5Error(code) {
|
|
376
|
+
const n = Number(code);
|
|
377
|
+
const name = POX5_ERROR_NAMES[n];
|
|
378
|
+
if (!name)
|
|
379
|
+
return;
|
|
380
|
+
return { code: n, name, description: POX5_ERROR_DESCRIPTIONS[n] };
|
|
381
|
+
}
|
|
382
|
+
// src/errors/base.ts
|
|
383
|
+
function codeFromName(name) {
|
|
384
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toUpperCase();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
class BaseError extends Error {
|
|
388
|
+
name = "StacksError";
|
|
389
|
+
#code;
|
|
390
|
+
shortMessage;
|
|
391
|
+
details;
|
|
392
|
+
constructor(shortMessage, options) {
|
|
393
|
+
const message = [
|
|
394
|
+
shortMessage,
|
|
395
|
+
options?.details ? `
|
|
396
|
+
${options.details}` : ""
|
|
397
|
+
].join("");
|
|
398
|
+
super(message, { cause: options?.cause });
|
|
399
|
+
if (options?.code !== undefined)
|
|
400
|
+
this.#code = options.code;
|
|
401
|
+
this.shortMessage = shortMessage;
|
|
402
|
+
this.details = options?.details;
|
|
403
|
+
}
|
|
404
|
+
get code() {
|
|
405
|
+
return this.#code ?? codeFromName(this.name);
|
|
406
|
+
}
|
|
407
|
+
set code(value) {
|
|
408
|
+
this.#code = value;
|
|
409
|
+
}
|
|
410
|
+
toJSON() {
|
|
411
|
+
return {
|
|
412
|
+
name: this.name,
|
|
413
|
+
code: this.code,
|
|
414
|
+
message: this.message,
|
|
415
|
+
shortMessage: this.shortMessage,
|
|
416
|
+
details: this.details,
|
|
417
|
+
cause: this.cause instanceof Error ? this.cause.message : undefined
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/errors/response.ts
|
|
423
|
+
class MalformedResponseError extends BaseError {
|
|
424
|
+
name = "MalformedResponseError";
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
class ReadContractError extends BaseError {
|
|
428
|
+
name = "ReadContractError";
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/actions/public/getBalance.ts
|
|
432
|
+
async function getBalance(client, params) {
|
|
433
|
+
const data = await client.request(`/v2/accounts/${encodeURIComponent(params.address)}`, {
|
|
434
|
+
method: "GET"
|
|
435
|
+
});
|
|
436
|
+
const balance = data?.balance;
|
|
437
|
+
if (typeof balance !== "string" && typeof balance !== "number") {
|
|
438
|
+
throw new MalformedResponseError(`getBalance: /v2/accounts/${params.address} response is missing "balance"`);
|
|
439
|
+
}
|
|
440
|
+
return BigInt(balance);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/errors/transaction.ts
|
|
444
|
+
class TransactionError extends BaseError {
|
|
445
|
+
name = "TransactionError";
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
class BroadcastError extends BaseError {
|
|
449
|
+
name = "BroadcastError";
|
|
450
|
+
txid;
|
|
451
|
+
reason;
|
|
452
|
+
reasonData;
|
|
453
|
+
constructor(message, options) {
|
|
454
|
+
super(message, options);
|
|
455
|
+
this.txid = options?.txid;
|
|
456
|
+
this.reason = options?.reason;
|
|
457
|
+
this.reasonData = options?.reasonData;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
class TransactionAbortedError extends BaseError {
|
|
462
|
+
name = "TransactionAbortedError";
|
|
463
|
+
receipt;
|
|
464
|
+
constructor(message, options) {
|
|
465
|
+
super(message, options);
|
|
466
|
+
this.receipt = options.receipt;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
class TransactionDroppedError extends BaseError {
|
|
471
|
+
name = "TransactionDroppedError";
|
|
472
|
+
txid;
|
|
473
|
+
constructor(message, options) {
|
|
474
|
+
super(message, options);
|
|
475
|
+
this.txid = options.txid;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
class WaitForTransactionTimeoutError extends BaseError {
|
|
480
|
+
name = "WaitForTransactionTimeoutError";
|
|
481
|
+
txid;
|
|
482
|
+
constructor(message, options) {
|
|
483
|
+
super(message, options);
|
|
484
|
+
this.txid = options.txid;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
class SerializationError extends BaseError {
|
|
489
|
+
name = "SerializationError";
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
class SigningError extends BaseError {
|
|
493
|
+
name = "SigningError";
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/utils/encoding.ts
|
|
497
|
+
var hexes = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
498
|
+
function bytesToHex(bytes) {
|
|
499
|
+
let hex = "";
|
|
500
|
+
for (const b of bytes) {
|
|
501
|
+
hex += hexes[b];
|
|
502
|
+
}
|
|
503
|
+
return hex;
|
|
504
|
+
}
|
|
505
|
+
function hexToBytes(hex) {
|
|
506
|
+
let h = without0x(hex);
|
|
507
|
+
if (h.length % 2)
|
|
508
|
+
h = `0${h}`;
|
|
509
|
+
const array = new Uint8Array(h.length / 2);
|
|
510
|
+
for (let i = 0;i < array.length; i++) {
|
|
511
|
+
const j = i * 2;
|
|
512
|
+
const byte = Number.parseInt(h.slice(j, j + 2), 16);
|
|
513
|
+
if (Number.isNaN(byte) || byte < 0)
|
|
514
|
+
throw new Error("Invalid byte sequence");
|
|
515
|
+
array[i] = byte;
|
|
516
|
+
}
|
|
517
|
+
return array;
|
|
518
|
+
}
|
|
519
|
+
function with0x(value) {
|
|
520
|
+
return /^0x/i.test(value) ? value : `0x${value}`;
|
|
521
|
+
}
|
|
522
|
+
function without0x(value) {
|
|
523
|
+
return /^0x/i.test(value) ? value.slice(2) : value;
|
|
524
|
+
}
|
|
525
|
+
function utf8ToBytes(str) {
|
|
526
|
+
return new TextEncoder().encode(str);
|
|
527
|
+
}
|
|
528
|
+
function bytesToUtf8(bytes) {
|
|
529
|
+
return new TextDecoder().decode(bytes);
|
|
530
|
+
}
|
|
531
|
+
function asciiToBytes(str) {
|
|
532
|
+
const bytes = new Uint8Array(str.length);
|
|
533
|
+
for (let i = 0;i < str.length; i++) {
|
|
534
|
+
bytes[i] = str.charCodeAt(i) & 255;
|
|
535
|
+
}
|
|
536
|
+
return bytes;
|
|
537
|
+
}
|
|
538
|
+
function bytesToAscii(bytes) {
|
|
539
|
+
return String.fromCharCode(...bytes);
|
|
540
|
+
}
|
|
541
|
+
function concatBytes(...arrays) {
|
|
542
|
+
if (arrays.length === 1)
|
|
543
|
+
return arrays[0];
|
|
544
|
+
const length = arrays.reduce((a, arr) => a + arr.length, 0);
|
|
545
|
+
const result = new Uint8Array(length);
|
|
546
|
+
let pad = 0;
|
|
547
|
+
for (const arr of arrays) {
|
|
548
|
+
result.set(arr, pad);
|
|
549
|
+
pad += arr.length;
|
|
550
|
+
}
|
|
551
|
+
return result;
|
|
552
|
+
}
|
|
553
|
+
function intToBigInt(value) {
|
|
554
|
+
if (typeof value === "bigint")
|
|
555
|
+
return value;
|
|
556
|
+
if (typeof value === "string")
|
|
557
|
+
return BigInt(value);
|
|
558
|
+
if (typeof value === "number") {
|
|
559
|
+
if (!Number.isInteger(value))
|
|
560
|
+
throw new RangeError("Values of type 'number' must be an integer.");
|
|
561
|
+
if (value > Number.MAX_SAFE_INTEGER)
|
|
562
|
+
throw new RangeError(`Values of type 'number' must be <= ${Number.MAX_SAFE_INTEGER}. Use BigInt instead.`);
|
|
563
|
+
return BigInt(value);
|
|
564
|
+
}
|
|
565
|
+
if (value instanceof Uint8Array)
|
|
566
|
+
return BigInt(`0x${bytesToHex(value)}`);
|
|
567
|
+
throw new TypeError("Must be a number, bigint, string, or Uint8Array.");
|
|
568
|
+
}
|
|
569
|
+
function intToBytes(value, byteLength) {
|
|
570
|
+
return bigIntToBytes(intToBigInt(value), byteLength);
|
|
571
|
+
}
|
|
572
|
+
function bigIntToBytes(value, length = 16) {
|
|
573
|
+
const hex = value.toString(16).padStart(length * 2, "0");
|
|
574
|
+
return hexToBytes(hex);
|
|
575
|
+
}
|
|
576
|
+
function intToHex(integer, byteLength = 8) {
|
|
577
|
+
const value = typeof integer === "bigint" ? integer : intToBigInt(integer);
|
|
578
|
+
return value.toString(16).padStart(byteLength * 2, "0");
|
|
579
|
+
}
|
|
580
|
+
function toTwos(value, width) {
|
|
581
|
+
const limit = BigInt(1) << width - BigInt(1);
|
|
582
|
+
if (value < -limit || value > limit - BigInt(1)) {
|
|
583
|
+
throw new Error(`Unable to represent integer in width: ${width}`);
|
|
584
|
+
}
|
|
585
|
+
if (value >= BigInt(0))
|
|
586
|
+
return value;
|
|
587
|
+
return value + (BigInt(1) << width);
|
|
588
|
+
}
|
|
589
|
+
function fromTwos(value, width) {
|
|
590
|
+
if (value & BigInt(1) << width - BigInt(1)) {
|
|
591
|
+
return value - (BigInt(1) << width);
|
|
592
|
+
}
|
|
593
|
+
return value;
|
|
594
|
+
}
|
|
595
|
+
function bytesToTwosBigInt(bytes) {
|
|
596
|
+
if (bytes.length === 0)
|
|
597
|
+
return 0n;
|
|
598
|
+
const hex = bytesToHex(bytes);
|
|
599
|
+
if (hex.length === 0)
|
|
600
|
+
return 0n;
|
|
601
|
+
return fromTwos(BigInt(`0x${hex}`), BigInt(bytes.byteLength * 8));
|
|
602
|
+
}
|
|
603
|
+
function writeUInt32BE(value) {
|
|
604
|
+
const buf = new Uint8Array(4);
|
|
605
|
+
buf[0] = value >>> 24 & 255;
|
|
606
|
+
buf[1] = value >>> 16 & 255;
|
|
607
|
+
buf[2] = value >>> 8 & 255;
|
|
608
|
+
buf[3] = value & 255;
|
|
609
|
+
return buf;
|
|
610
|
+
}
|
|
611
|
+
function readUInt32BE(bytes, offset = 0) {
|
|
612
|
+
return (bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]) >>> 0;
|
|
613
|
+
}
|
|
614
|
+
function writeUInt16BE(value) {
|
|
615
|
+
const buf = new Uint8Array(2);
|
|
616
|
+
buf[0] = value >>> 8 & 255;
|
|
617
|
+
buf[1] = value & 255;
|
|
618
|
+
return buf;
|
|
619
|
+
}
|
|
620
|
+
function readUInt16BE(bytes, offset = 0) {
|
|
621
|
+
return (bytes[offset] << 8 | bytes[offset + 1]) >>> 0;
|
|
622
|
+
}
|
|
623
|
+
function writeUInt8(value) {
|
|
624
|
+
return new Uint8Array([value & 255]);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// src/utils/bytes-reader.ts
|
|
628
|
+
class BytesReader {
|
|
629
|
+
data;
|
|
630
|
+
offset = 0;
|
|
631
|
+
constructor(data) {
|
|
632
|
+
this.data = data;
|
|
633
|
+
}
|
|
634
|
+
remaining() {
|
|
635
|
+
return this.data.length - this.offset;
|
|
636
|
+
}
|
|
637
|
+
ensure(length) {
|
|
638
|
+
if (this.offset + length > this.data.length) {
|
|
639
|
+
throw new SerializationError(`Buffer underflow: need ${length} bytes at offset ${this.offset}, have ${this.data.length}`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
readUInt8() {
|
|
643
|
+
this.ensure(1);
|
|
644
|
+
return this.data[this.offset++];
|
|
645
|
+
}
|
|
646
|
+
readUInt16BE() {
|
|
647
|
+
this.ensure(2);
|
|
648
|
+
const val = (this.data[this.offset] << 8 | this.data[this.offset + 1]) >>> 0;
|
|
649
|
+
this.offset += 2;
|
|
650
|
+
return val;
|
|
651
|
+
}
|
|
652
|
+
readUInt32BE() {
|
|
653
|
+
this.ensure(4);
|
|
654
|
+
const val = (this.data[this.offset] << 24 | this.data[this.offset + 1] << 16 | this.data[this.offset + 2] << 8 | this.data[this.offset + 3]) >>> 0;
|
|
655
|
+
this.offset += 4;
|
|
656
|
+
return val;
|
|
657
|
+
}
|
|
658
|
+
readBytes(length) {
|
|
659
|
+
this.ensure(length);
|
|
660
|
+
const slice = this.data.slice(this.offset, this.offset + length);
|
|
661
|
+
this.offset += length;
|
|
662
|
+
return slice;
|
|
663
|
+
}
|
|
664
|
+
readBigUInt64BE() {
|
|
665
|
+
const hex = bytesToHex(this.readBytes(8));
|
|
666
|
+
return hex.length > 0 ? BigInt(`0x${hex}`) : 0n;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// src/utils/c32.ts
|
|
671
|
+
var import_sha2 = require("@noble/hashes/sha2.js");
|
|
672
|
+
var C32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
673
|
+
var HEX = "0123456789abcdef";
|
|
674
|
+
function c32normalize(input) {
|
|
675
|
+
return input.toUpperCase().replace(/O/g, "0").replace(/L|I/g, "1");
|
|
676
|
+
}
|
|
677
|
+
function c32encode(inputHex) {
|
|
678
|
+
let hex = inputHex;
|
|
679
|
+
if (hex.length % 2 !== 0)
|
|
680
|
+
hex = `0${hex}`;
|
|
681
|
+
hex = hex.toLowerCase();
|
|
682
|
+
const res = [];
|
|
683
|
+
let carry = 0;
|
|
684
|
+
for (let i = hex.length - 1;i >= 0; i--) {
|
|
685
|
+
if (carry < 4) {
|
|
686
|
+
const currentCode = HEX.indexOf(hex[i]) >> carry;
|
|
687
|
+
const nextCode = i !== 0 ? HEX.indexOf(hex[i - 1]) : 0;
|
|
688
|
+
const nextBits = 1 + carry;
|
|
689
|
+
const nextLowBits = nextCode % (1 << nextBits) << 5 - nextBits;
|
|
690
|
+
res.unshift(C32[currentCode + nextLowBits]);
|
|
691
|
+
carry = nextBits;
|
|
692
|
+
} else {
|
|
693
|
+
carry = 0;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
let leadingZeros = 0;
|
|
697
|
+
while (leadingZeros < res.length && res[leadingZeros] === "0")
|
|
698
|
+
leadingZeros++;
|
|
699
|
+
const stripped = res.slice(leadingZeros);
|
|
700
|
+
const bytes = hexToBytes(hex);
|
|
701
|
+
let zeroBytesCount = 0;
|
|
702
|
+
while (zeroBytesCount < bytes.length && bytes[zeroBytesCount] === 0)
|
|
703
|
+
zeroBytesCount++;
|
|
704
|
+
const prefix = Array(zeroBytesCount).fill(C32[0]);
|
|
705
|
+
return [...prefix, ...stripped].join("");
|
|
706
|
+
}
|
|
707
|
+
function c32decode(c32input) {
|
|
708
|
+
const input = c32normalize(c32input);
|
|
709
|
+
if (!input.match(`^[${C32}]*$`))
|
|
710
|
+
throw new Error("Not a c32-encoded string");
|
|
711
|
+
const zeroPrefix = input.match(`^${C32[0]}*`);
|
|
712
|
+
const numLeadingZeroBytes = zeroPrefix ? zeroPrefix[0]?.length : 0;
|
|
713
|
+
const res = [];
|
|
714
|
+
let carry = 0;
|
|
715
|
+
let carryBits = 0;
|
|
716
|
+
for (let i = input.length - 1;i >= 0; i--) {
|
|
717
|
+
if (carryBits === 4) {
|
|
718
|
+
res.unshift(HEX[carry]);
|
|
719
|
+
carryBits = 0;
|
|
720
|
+
carry = 0;
|
|
721
|
+
}
|
|
722
|
+
const currentValue = (C32.indexOf(input[i]) << carryBits) + carry;
|
|
723
|
+
res.unshift(HEX[currentValue % 16]);
|
|
724
|
+
carryBits += 1;
|
|
725
|
+
carry = currentValue >> 4;
|
|
726
|
+
if (carry > 1 << carryBits)
|
|
727
|
+
throw new Error("Panic error in c32 decoding");
|
|
728
|
+
}
|
|
729
|
+
res.unshift(HEX[carry]);
|
|
730
|
+
if (res.length % 2 === 1)
|
|
731
|
+
res.unshift("0");
|
|
732
|
+
let hexLeadingZeros = 0;
|
|
733
|
+
while (hexLeadingZeros < res.length && res[hexLeadingZeros] === "0")
|
|
734
|
+
hexLeadingZeros++;
|
|
735
|
+
let hexStr = res.slice(hexLeadingZeros - hexLeadingZeros % 2).join("");
|
|
736
|
+
for (let i = 0;i < numLeadingZeroBytes; i++)
|
|
737
|
+
hexStr = `00${hexStr}`;
|
|
738
|
+
return hexStr;
|
|
739
|
+
}
|
|
740
|
+
function c32checksum(dataHex) {
|
|
741
|
+
return bytesToHex(import_sha2.sha256(import_sha2.sha256(hexToBytes(dataHex))).slice(0, 4));
|
|
742
|
+
}
|
|
743
|
+
function c32checkEncode(version, data) {
|
|
744
|
+
if (version < 0 || version >= 32)
|
|
745
|
+
throw new Error("Invalid version (must be between 0 and 31)");
|
|
746
|
+
if (!data.match(/^[0-9a-fA-F]*$/))
|
|
747
|
+
throw new Error("Invalid data (not a hex string)");
|
|
748
|
+
let d = data.toLowerCase();
|
|
749
|
+
if (d.length % 2 !== 0)
|
|
750
|
+
d = `0${d}`;
|
|
751
|
+
let versionHex = version.toString(16);
|
|
752
|
+
if (versionHex.length === 1)
|
|
753
|
+
versionHex = `0${versionHex}`;
|
|
754
|
+
const checksumHex = c32checksum(`${versionHex}${d}`);
|
|
755
|
+
return `${C32[version]}${c32encode(`${d}${checksumHex}`)}`;
|
|
756
|
+
}
|
|
757
|
+
function c32checkDecode(c32data) {
|
|
758
|
+
const normalized = c32normalize(c32data);
|
|
759
|
+
const dataHex = c32decode(normalized.slice(1));
|
|
760
|
+
const version = C32.indexOf(normalized[0]);
|
|
761
|
+
const checksum = dataHex.slice(-8);
|
|
762
|
+
let versionHex = version.toString(16);
|
|
763
|
+
if (versionHex.length === 1)
|
|
764
|
+
versionHex = `0${versionHex}`;
|
|
765
|
+
if (c32checksum(`${versionHex}${dataHex.substring(0, dataHex.length - 8)}`) !== checksum) {
|
|
766
|
+
throw new Error("Invalid c32check string: checksum mismatch");
|
|
767
|
+
}
|
|
768
|
+
return [version, dataHex.substring(0, dataHex.length - 8)];
|
|
769
|
+
}
|
|
770
|
+
function c32address(version, hash160hex) {
|
|
771
|
+
if (!hash160hex.match(/^[0-9a-fA-F]{40}$/)) {
|
|
772
|
+
throw new Error("Invalid argument: not a hash160 hex string");
|
|
773
|
+
}
|
|
774
|
+
return `S${c32checkEncode(version, hash160hex)}`;
|
|
775
|
+
}
|
|
776
|
+
function c32addressDecode(c32addr) {
|
|
777
|
+
if (c32addr.length <= 5)
|
|
778
|
+
throw new Error("Invalid c32 address: invalid length");
|
|
779
|
+
if (c32addr[0] !== "S")
|
|
780
|
+
throw new Error('Invalid c32 address: must start with "S"');
|
|
781
|
+
return c32checkDecode(c32addr.slice(1));
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/clarity/types.ts
|
|
785
|
+
var ClarityWireType = {
|
|
786
|
+
int: 0,
|
|
787
|
+
uint: 1,
|
|
788
|
+
buffer: 2,
|
|
789
|
+
true: 3,
|
|
790
|
+
false: 4,
|
|
791
|
+
address: 5,
|
|
792
|
+
contract: 6,
|
|
793
|
+
ok: 7,
|
|
794
|
+
err: 8,
|
|
795
|
+
none: 9,
|
|
796
|
+
some: 10,
|
|
797
|
+
list: 11,
|
|
798
|
+
tuple: 12,
|
|
799
|
+
ascii: 13,
|
|
800
|
+
utf8: 14
|
|
801
|
+
};
|
|
802
|
+
var wireTypeToClarity = Object.fromEntries(Object.entries(ClarityWireType).map(([k, v]) => [v, k]));
|
|
803
|
+
function clarityTypeFromByte(byte) {
|
|
804
|
+
const type = wireTypeToClarity[byte];
|
|
805
|
+
if (!type)
|
|
806
|
+
throw new Error(`Unknown clarity wire type: ${byte}`);
|
|
807
|
+
return type;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// src/utils/constants.ts
|
|
811
|
+
var MAX_U128 = (1n << 128n) - 1n;
|
|
812
|
+
var MAX_I128 = (1n << 127n) - 1n;
|
|
813
|
+
var MIN_I128 = -(1n << 127n);
|
|
814
|
+
var AddressVersion = {
|
|
815
|
+
MainnetSingleSig: 22,
|
|
816
|
+
MainnetMultiSig: 20,
|
|
817
|
+
TestnetSingleSig: 26,
|
|
818
|
+
TestnetMultiSig: 21
|
|
819
|
+
};
|
|
820
|
+
var ZERO_ADDRESS = "SP000000000000000000002Q6VF78";
|
|
821
|
+
var TESTNET_ZERO_ADDRESS = "ST000000000000000000002AMW42H";
|
|
822
|
+
var MICROSTX_PER_STX = 1000000n;
|
|
823
|
+
|
|
824
|
+
// src/utils/hash.ts
|
|
825
|
+
var import_legacy = require("@noble/hashes/legacy.js");
|
|
826
|
+
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
827
|
+
var import_sha23 = require("@noble/hashes/sha2.js");
|
|
828
|
+
function hash160(input) {
|
|
829
|
+
return import_legacy.ripemd160(import_sha22.sha256(input));
|
|
830
|
+
}
|
|
831
|
+
function txidFromBytes(data) {
|
|
832
|
+
return bytesToHex(import_sha23.sha512_256(data));
|
|
833
|
+
}
|
|
834
|
+
function hashP2PKH(input) {
|
|
835
|
+
return bytesToHex(hash160(input));
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/utils/address.ts
|
|
839
|
+
function publicKeyToAddress(publicKey, network = "mainnet") {
|
|
840
|
+
const bytes = typeof publicKey === "string" ? hexToBytes(without0x(publicKey)) : publicKey;
|
|
841
|
+
const version = network === "mainnet" ? AddressVersion.MainnetSingleSig : AddressVersion.TestnetSingleSig;
|
|
842
|
+
return c32address(version, bytesToHex(hash160(bytes)));
|
|
843
|
+
}
|
|
844
|
+
var CONTRACT_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/;
|
|
845
|
+
function validateStacksAddress(address) {
|
|
846
|
+
try {
|
|
847
|
+
c32addressDecode(address);
|
|
848
|
+
return true;
|
|
849
|
+
} catch {
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
var isValidAddress = validateStacksAddress;
|
|
854
|
+
function parsePrincipal(value) {
|
|
855
|
+
const parts = value.split(".");
|
|
856
|
+
if (parts.length > 2)
|
|
857
|
+
return null;
|
|
858
|
+
const [address, contractName] = parts;
|
|
859
|
+
if (!validateStacksAddress(address))
|
|
860
|
+
return null;
|
|
861
|
+
if (contractName === undefined)
|
|
862
|
+
return { address };
|
|
863
|
+
if (!CONTRACT_NAME_REGEX.test(contractName))
|
|
864
|
+
return null;
|
|
865
|
+
return { address, contractName };
|
|
866
|
+
}
|
|
867
|
+
function parseContractId(contractId) {
|
|
868
|
+
const parsed = parsePrincipal(contractId);
|
|
869
|
+
if (!parsed?.contractName)
|
|
870
|
+
throw new Error(`Invalid contract identifier: ${contractId}`);
|
|
871
|
+
return [parsed.address, parsed.contractName];
|
|
872
|
+
}
|
|
873
|
+
function isClarityName(name) {
|
|
874
|
+
const regex = /^[a-zA-Z]([a-zA-Z0-9]|[-_!?+<>=/*])*$|^[-+=/*]$|^[<>]=?$/;
|
|
875
|
+
return regex.test(name) && name.length < 128;
|
|
876
|
+
}
|
|
877
|
+
function isAddressEqual(a, b) {
|
|
878
|
+
const [versionA, hashA] = c32addressDecode(a);
|
|
879
|
+
const [versionB, hashB] = c32addressDecode(b);
|
|
880
|
+
return versionA === versionB && hashA.toLowerCase() === hashB.toLowerCase();
|
|
881
|
+
}
|
|
882
|
+
function addressToVersion(address) {
|
|
883
|
+
return c32addressDecode(address)[0];
|
|
884
|
+
}
|
|
885
|
+
function getContractAddress(deployer, contractName) {
|
|
886
|
+
if (!validateStacksAddress(deployer)) {
|
|
887
|
+
throw new Error(`Invalid deployer address: ${deployer}`);
|
|
888
|
+
}
|
|
889
|
+
if (!isClarityName(contractName)) {
|
|
890
|
+
throw new Error(`Invalid contract name: ${contractName}`);
|
|
891
|
+
}
|
|
892
|
+
return `${deployer}.${contractName}`;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/clarity/serialize.ts
|
|
896
|
+
var CLARITY_INT_SIZE = 128n;
|
|
897
|
+
var CLARITY_INT_BYTE_SIZE = 16;
|
|
898
|
+
function typeIdByte(type) {
|
|
899
|
+
return ClarityWireType[type];
|
|
900
|
+
}
|
|
901
|
+
function withTypeId(type, bytes) {
|
|
902
|
+
return concatBytes(new Uint8Array([typeIdByte(type)]), bytes);
|
|
903
|
+
}
|
|
904
|
+
function serializeAddress(c32Address) {
|
|
905
|
+
const [version, hash1602] = c32addressDecode(c32Address);
|
|
906
|
+
return concatBytes(new Uint8Array([version]), hexToBytes(hash1602));
|
|
907
|
+
}
|
|
908
|
+
function serializeLPString(str, prefixBytes = 1) {
|
|
909
|
+
const content = utf8ToBytes(str);
|
|
910
|
+
const lengthPrefix = hexToBytes(intToHex(content.byteLength, prefixBytes));
|
|
911
|
+
return concatBytes(lengthPrefix, content);
|
|
912
|
+
}
|
|
913
|
+
function serializeCVBytes(value) {
|
|
914
|
+
switch (value.type) {
|
|
915
|
+
case "true":
|
|
916
|
+
case "false":
|
|
917
|
+
return new Uint8Array([typeIdByte(value.type)]);
|
|
918
|
+
case "int": {
|
|
919
|
+
const bytes = bigIntToBytes(toTwos(BigInt(value.value), CLARITY_INT_SIZE), CLARITY_INT_BYTE_SIZE);
|
|
920
|
+
return withTypeId(value.type, bytes);
|
|
921
|
+
}
|
|
922
|
+
case "uint": {
|
|
923
|
+
const bytes = bigIntToBytes(BigInt(value.value), CLARITY_INT_BYTE_SIZE);
|
|
924
|
+
return withTypeId(value.type, bytes);
|
|
925
|
+
}
|
|
926
|
+
case "buffer": {
|
|
927
|
+
const bufBytes = hexToBytes(value.value);
|
|
928
|
+
return withTypeId(value.type, concatBytes(writeUInt32BE(bufBytes.length), bufBytes));
|
|
929
|
+
}
|
|
930
|
+
case "none":
|
|
931
|
+
return new Uint8Array([typeIdByte(value.type)]);
|
|
932
|
+
case "some":
|
|
933
|
+
return withTypeId(value.type, serializeCVBytes(value.value));
|
|
934
|
+
case "ok":
|
|
935
|
+
case "err":
|
|
936
|
+
return withTypeId(value.type, serializeCVBytes(value.value));
|
|
937
|
+
case "address":
|
|
938
|
+
return withTypeId(value.type, serializeAddress(value.value));
|
|
939
|
+
case "contract": {
|
|
940
|
+
const [addr, name] = value.value.split(".");
|
|
941
|
+
if (!addr || !name)
|
|
942
|
+
throw new Error(`Invalid contract principal: ${value.value}`);
|
|
943
|
+
return withTypeId(value.type, concatBytes(serializeAddress(addr), serializeLPString(name)));
|
|
944
|
+
}
|
|
945
|
+
case "list": {
|
|
946
|
+
const parts = [writeUInt32BE(value.value.length)];
|
|
947
|
+
for (const item of value.value) {
|
|
948
|
+
parts.push(serializeCVBytes(item));
|
|
949
|
+
}
|
|
950
|
+
return withTypeId(value.type, concatBytes(...parts));
|
|
951
|
+
}
|
|
952
|
+
case "tuple": {
|
|
953
|
+
const keys = Object.keys(value.value).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
954
|
+
const parts = [writeUInt32BE(keys.length)];
|
|
955
|
+
for (const key of keys) {
|
|
956
|
+
parts.push(serializeLPString(key));
|
|
957
|
+
parts.push(serializeCVBytes(value.value[key]));
|
|
958
|
+
}
|
|
959
|
+
return withTypeId(value.type, concatBytes(...parts));
|
|
960
|
+
}
|
|
961
|
+
case "ascii": {
|
|
962
|
+
const strBytes = asciiToBytes(value.value);
|
|
963
|
+
return withTypeId(value.type, concatBytes(writeUInt32BE(strBytes.length), strBytes));
|
|
964
|
+
}
|
|
965
|
+
case "utf8": {
|
|
966
|
+
const strBytes = utf8ToBytes(value.value);
|
|
967
|
+
return withTypeId(value.type, concatBytes(writeUInt32BE(strBytes.length), strBytes));
|
|
968
|
+
}
|
|
969
|
+
default:
|
|
970
|
+
throw new Error("Cannot serialize unknown clarity type");
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
function serializeCV(value) {
|
|
974
|
+
return bytesToHex(serializeCVBytes(value));
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// src/clarity/values.ts
|
|
978
|
+
var MAX_U1282 = BigInt("0xffffffffffffffffffffffffffffffff");
|
|
979
|
+
var MIN_I1282 = BigInt("-170141183460469231731687303715884105728");
|
|
980
|
+
var MAX_I1282 = BigInt("0x7fffffffffffffffffffffffffffffff");
|
|
981
|
+
function intCV(value) {
|
|
982
|
+
let v = value;
|
|
983
|
+
if (typeof v === "string" && v.toLowerCase().startsWith("0x")) {
|
|
984
|
+
v = bytesToTwosBigInt(hexToBytes(v));
|
|
985
|
+
}
|
|
986
|
+
if (v instanceof Uint8Array)
|
|
987
|
+
v = bytesToTwosBigInt(v);
|
|
988
|
+
const n = intToBigInt(v);
|
|
989
|
+
if (n > MAX_I1282)
|
|
990
|
+
throw new RangeError(`Int exceeds max i128: ${MAX_I1282}`);
|
|
991
|
+
if (n < MIN_I1282)
|
|
992
|
+
throw new RangeError(`Int below min i128: ${MIN_I1282}`);
|
|
993
|
+
return { type: "int", value: n };
|
|
994
|
+
}
|
|
995
|
+
function uintCV(value) {
|
|
996
|
+
const n = intToBigInt(value);
|
|
997
|
+
if (n < 0n)
|
|
998
|
+
throw new RangeError("Cannot construct unsigned int from negative value");
|
|
999
|
+
if (n > MAX_U1282)
|
|
1000
|
+
throw new RangeError(`UInt exceeds max u128: ${MAX_U1282}`);
|
|
1001
|
+
return { type: "uint", value: n };
|
|
1002
|
+
}
|
|
1003
|
+
var trueCV = () => ({ type: "true" });
|
|
1004
|
+
var falseCV = () => ({ type: "false" });
|
|
1005
|
+
var boolCV = (v) => v ? trueCV() : falseCV();
|
|
1006
|
+
function bufferCV(buffer) {
|
|
1007
|
+
if (buffer.byteLength > 1048576) {
|
|
1008
|
+
throw new Error("Buffer exceeds max size of 1MB");
|
|
1009
|
+
}
|
|
1010
|
+
return { type: "buffer", value: bytesToHex(buffer) };
|
|
1011
|
+
}
|
|
1012
|
+
function standardPrincipalCV(address) {
|
|
1013
|
+
const [version, hash1602] = c32addressDecode(address);
|
|
1014
|
+
const normalized = c32address(version, hash1602);
|
|
1015
|
+
return { type: "address", value: normalized };
|
|
1016
|
+
}
|
|
1017
|
+
function contractPrincipalCV(address, contractName) {
|
|
1018
|
+
const [version, hash1602] = c32addressDecode(address);
|
|
1019
|
+
const normalized = c32address(version, hash1602);
|
|
1020
|
+
if (utf8ToBytes(contractName).byteLength >= 128) {
|
|
1021
|
+
throw new Error("Contract name must be less than 128 bytes");
|
|
1022
|
+
}
|
|
1023
|
+
return { type: "contract", value: `${normalized}.${contractName}` };
|
|
1024
|
+
}
|
|
1025
|
+
function noneCV() {
|
|
1026
|
+
return { type: "none" };
|
|
1027
|
+
}
|
|
1028
|
+
function someCV(value) {
|
|
1029
|
+
return { type: "some", value };
|
|
1030
|
+
}
|
|
1031
|
+
function responseOkCV(value) {
|
|
1032
|
+
return { type: "ok", value };
|
|
1033
|
+
}
|
|
1034
|
+
function responseErrorCV(value) {
|
|
1035
|
+
return { type: "err", value };
|
|
1036
|
+
}
|
|
1037
|
+
function listCV(values) {
|
|
1038
|
+
return { type: "list", value: values };
|
|
1039
|
+
}
|
|
1040
|
+
function tupleCV(data) {
|
|
1041
|
+
for (const key in data) {
|
|
1042
|
+
if (!isClarityName(key)) {
|
|
1043
|
+
throw new Error(`"${key}" is not a valid Clarity name`);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return { type: "tuple", value: data };
|
|
1047
|
+
}
|
|
1048
|
+
function stringAsciiCV(value) {
|
|
1049
|
+
return { type: "ascii", value };
|
|
1050
|
+
}
|
|
1051
|
+
function stringUtf8CV(value) {
|
|
1052
|
+
return { type: "utf8", value };
|
|
1053
|
+
}
|
|
1054
|
+
var Cl = {
|
|
1055
|
+
int: intCV,
|
|
1056
|
+
uint: uintCV,
|
|
1057
|
+
bool: boolCV,
|
|
1058
|
+
principal(address) {
|
|
1059
|
+
const parsed = parsePrincipal(address);
|
|
1060
|
+
if (!parsed)
|
|
1061
|
+
throw new Error(`Invalid principal: ${address}`);
|
|
1062
|
+
return parsed.contractName ? contractPrincipalCV(parsed.address, parsed.contractName) : standardPrincipalCV(parsed.address);
|
|
1063
|
+
},
|
|
1064
|
+
address(address) {
|
|
1065
|
+
return Cl.principal(address);
|
|
1066
|
+
},
|
|
1067
|
+
contractPrincipal: contractPrincipalCV,
|
|
1068
|
+
standardPrincipal: standardPrincipalCV,
|
|
1069
|
+
buffer: bufferCV,
|
|
1070
|
+
bufferFromHex: (hex) => bufferCV(hexToBytes(hex)),
|
|
1071
|
+
bufferFromAscii: (ascii) => bufferCV(asciiToBytes(ascii)),
|
|
1072
|
+
bufferFromUtf8: (utf8) => bufferCV(utf8ToBytes(utf8)),
|
|
1073
|
+
none: noneCV,
|
|
1074
|
+
some: someCV,
|
|
1075
|
+
ok: responseOkCV,
|
|
1076
|
+
error: responseErrorCV,
|
|
1077
|
+
list: listCV,
|
|
1078
|
+
tuple: tupleCV,
|
|
1079
|
+
stringAscii: stringAsciiCV,
|
|
1080
|
+
stringUtf8: stringUtf8CV,
|
|
1081
|
+
serialize(value) {
|
|
1082
|
+
return bytesToHex(serializeCVBytes(value));
|
|
1083
|
+
},
|
|
1084
|
+
deserialize: deserializeCVBytes
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
// src/clarity/deserialize.ts
|
|
1088
|
+
function readAddress(reader) {
|
|
1089
|
+
const version = reader.readUInt8();
|
|
1090
|
+
const hash1602 = bytesToHex(reader.readBytes(20));
|
|
1091
|
+
return c32address(version, hash1602);
|
|
1092
|
+
}
|
|
1093
|
+
function readLPString(reader, prefixBytes = 1) {
|
|
1094
|
+
let length = 0;
|
|
1095
|
+
for (let i = 0;i < prefixBytes; i++) {
|
|
1096
|
+
length = length << 8 | reader.readUInt8();
|
|
1097
|
+
}
|
|
1098
|
+
return bytesToUtf8(reader.readBytes(length));
|
|
1099
|
+
}
|
|
1100
|
+
var MAX_CV_DEPTH = 64;
|
|
1101
|
+
var MIN_LIST_ITEM_BYTES = 1;
|
|
1102
|
+
var MIN_TUPLE_ENTRY_BYTES = 2;
|
|
1103
|
+
function checkCount(reader, count, minBytes) {
|
|
1104
|
+
if (count * minBytes > reader.remaining()) {
|
|
1105
|
+
throw new SerializationError(`Clarity value declares ${count} elements but only ${reader.remaining()} bytes remain`);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
function readCV(reader, depth = 0) {
|
|
1109
|
+
if (depth > MAX_CV_DEPTH) {
|
|
1110
|
+
throw new SerializationError(`Clarity value nests deeper than ${MAX_CV_DEPTH} levels`);
|
|
1111
|
+
}
|
|
1112
|
+
const typeByte = reader.readUInt8();
|
|
1113
|
+
const type = clarityTypeFromByte(typeByte);
|
|
1114
|
+
switch (type) {
|
|
1115
|
+
case "int":
|
|
1116
|
+
return intCV(bytesToTwosBigInt(reader.readBytes(16)));
|
|
1117
|
+
case "uint":
|
|
1118
|
+
return uintCV(reader.readBytes(16));
|
|
1119
|
+
case "true":
|
|
1120
|
+
return trueCV();
|
|
1121
|
+
case "false":
|
|
1122
|
+
return falseCV();
|
|
1123
|
+
case "buffer": {
|
|
1124
|
+
const len = reader.readUInt32BE();
|
|
1125
|
+
return bufferCV(reader.readBytes(len));
|
|
1126
|
+
}
|
|
1127
|
+
case "none":
|
|
1128
|
+
return noneCV();
|
|
1129
|
+
case "some":
|
|
1130
|
+
return someCV(readCV(reader, depth + 1));
|
|
1131
|
+
case "ok":
|
|
1132
|
+
return responseOkCV(readCV(reader, depth + 1));
|
|
1133
|
+
case "err":
|
|
1134
|
+
return responseErrorCV(readCV(reader, depth + 1));
|
|
1135
|
+
case "address":
|
|
1136
|
+
return standardPrincipalCV(readAddress(reader));
|
|
1137
|
+
case "contract": {
|
|
1138
|
+
const addr = readAddress(reader);
|
|
1139
|
+
const name = readLPString(reader);
|
|
1140
|
+
return contractPrincipalCV(addr, name);
|
|
1141
|
+
}
|
|
1142
|
+
case "list": {
|
|
1143
|
+
const len = reader.readUInt32BE();
|
|
1144
|
+
checkCount(reader, len, MIN_LIST_ITEM_BYTES);
|
|
1145
|
+
const items = [];
|
|
1146
|
+
for (let i = 0;i < len; i++) {
|
|
1147
|
+
items.push(readCV(reader, depth + 1));
|
|
1148
|
+
}
|
|
1149
|
+
return listCV(items);
|
|
1150
|
+
}
|
|
1151
|
+
case "tuple": {
|
|
1152
|
+
const len = reader.readUInt32BE();
|
|
1153
|
+
checkCount(reader, len, MIN_TUPLE_ENTRY_BYTES);
|
|
1154
|
+
const data = {};
|
|
1155
|
+
for (let i = 0;i < len; i++) {
|
|
1156
|
+
const key = readLPString(reader);
|
|
1157
|
+
data[key] = readCV(reader, depth + 1);
|
|
1158
|
+
}
|
|
1159
|
+
return tupleCV(data);
|
|
1160
|
+
}
|
|
1161
|
+
case "ascii": {
|
|
1162
|
+
const len = reader.readUInt32BE();
|
|
1163
|
+
return stringAsciiCV(bytesToAscii(reader.readBytes(len)));
|
|
1164
|
+
}
|
|
1165
|
+
case "utf8": {
|
|
1166
|
+
const len = reader.readUInt32BE();
|
|
1167
|
+
return stringUtf8CV(bytesToUtf8(reader.readBytes(len)));
|
|
1168
|
+
}
|
|
1169
|
+
default:
|
|
1170
|
+
throw new SerializationError(`Cannot deserialize unknown clarity type: ${type}`);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function deserializeCVBytes(input) {
|
|
1174
|
+
const bytes = typeof input === "string" ? hexToBytes(without0x(input)) : input;
|
|
1175
|
+
return readCV(new BytesReader(bytes));
|
|
1176
|
+
}
|
|
1177
|
+
function deserializeCV(input) {
|
|
1178
|
+
return deserializeCVBytes(input);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/actions/public/getDataVar.ts
|
|
1182
|
+
async function getDataVar(client, params) {
|
|
1183
|
+
const [address, name] = parseContractId(params.contract);
|
|
1184
|
+
const path = `/v2/data_var/${address}/${name}/${params.varName}?proof=0`;
|
|
1185
|
+
const data = await client.request(path, { method: "GET" });
|
|
1186
|
+
if (typeof data?.data !== "string") {
|
|
1187
|
+
throw new MalformedResponseError(`getDataVar: /v2/data_var/${address}/${name}/${params.varName} response is missing "data"`);
|
|
1188
|
+
}
|
|
1189
|
+
return deserializeCVBytes(data.data);
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// src/actions/public/getMapEntry.ts
|
|
1193
|
+
async function getMapEntry(client, params) {
|
|
1194
|
+
const [address, name] = parseContractId(params.contract);
|
|
1195
|
+
const serializedKey = with0x(bytesToHex(serializeCVBytes(params.key)));
|
|
1196
|
+
const data = await client.request(`/v2/map_entry/${address}/${name}/${params.mapName}`, {
|
|
1197
|
+
method: "POST",
|
|
1198
|
+
body: serializedKey
|
|
1199
|
+
});
|
|
1200
|
+
if (typeof data?.data !== "string") {
|
|
1201
|
+
throw new MalformedResponseError(`getMapEntry: /v2/map_entry/${address}/${name}/${params.mapName} response is missing "data"`);
|
|
1202
|
+
}
|
|
1203
|
+
return deserializeCVBytes(data.data);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// src/clarity/abi/utils.ts
|
|
1207
|
+
function toCamelCase(str) {
|
|
1208
|
+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()).replace(/-([A-Z])/g, (_, letter) => letter).replace(/-(\d)/g, (_, digit) => digit).replace(/-/g, "").replace(/^\d/, "_$&");
|
|
1209
|
+
}
|
|
1210
|
+
// src/clarity/abi/guards.ts
|
|
1211
|
+
function isUint128(value) {
|
|
1212
|
+
return typeof value === "bigint" && value >= 0n && value <= MAX_U128;
|
|
1213
|
+
}
|
|
1214
|
+
function isInt128(value) {
|
|
1215
|
+
return typeof value === "bigint" && value >= MIN_I128 && value <= MAX_I128;
|
|
1216
|
+
}
|
|
1217
|
+
function isBool(value) {
|
|
1218
|
+
return typeof value === "boolean";
|
|
1219
|
+
}
|
|
1220
|
+
function isPrincipal(value) {
|
|
1221
|
+
return typeof value === "string" && parsePrincipal(value) !== null;
|
|
1222
|
+
}
|
|
1223
|
+
function isStandardPrincipal(value) {
|
|
1224
|
+
if (typeof value !== "string")
|
|
1225
|
+
return false;
|
|
1226
|
+
const parsed = parsePrincipal(value);
|
|
1227
|
+
return parsed !== null && parsed.contractName === undefined;
|
|
1228
|
+
}
|
|
1229
|
+
function isContractPrincipal(value) {
|
|
1230
|
+
if (typeof value !== "string")
|
|
1231
|
+
return false;
|
|
1232
|
+
const parsed = parsePrincipal(value);
|
|
1233
|
+
return parsed !== null && parsed.contractName !== undefined;
|
|
1234
|
+
}
|
|
1235
|
+
function isTraitReference(value) {
|
|
1236
|
+
return isContractPrincipal(value);
|
|
1237
|
+
}
|
|
1238
|
+
function isString(value) {
|
|
1239
|
+
return typeof value === "string";
|
|
1240
|
+
}
|
|
1241
|
+
function isUint8Array(value) {
|
|
1242
|
+
return value instanceof Uint8Array;
|
|
1243
|
+
}
|
|
1244
|
+
function isArray(value, itemGuard) {
|
|
1245
|
+
return Array.isArray(value) && value.every(itemGuard);
|
|
1246
|
+
}
|
|
1247
|
+
function isOptional(value, guard) {
|
|
1248
|
+
return value === null || guard(value);
|
|
1249
|
+
}
|
|
1250
|
+
function isOkResponse(response) {
|
|
1251
|
+
return "ok" in response && !("err" in response);
|
|
1252
|
+
}
|
|
1253
|
+
function isErrResponse(response) {
|
|
1254
|
+
return "err" in response && !("ok" in response);
|
|
1255
|
+
}
|
|
1256
|
+
function isResponse(value, okGuard, errGuard) {
|
|
1257
|
+
if (typeof value !== "object" || value === null)
|
|
1258
|
+
return false;
|
|
1259
|
+
const obj = value;
|
|
1260
|
+
if ("ok" in value && !("err" in value))
|
|
1261
|
+
return okGuard(obj.ok);
|
|
1262
|
+
if ("err" in value && !("ok" in value))
|
|
1263
|
+
return errGuard(obj.err);
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1266
|
+
function isAbiList(type) {
|
|
1267
|
+
return typeof type === "object" && type !== null && "list" in type;
|
|
1268
|
+
}
|
|
1269
|
+
function isAbiTuple(type) {
|
|
1270
|
+
return typeof type === "object" && type !== null && "tuple" in type;
|
|
1271
|
+
}
|
|
1272
|
+
function isAbiOptional(type) {
|
|
1273
|
+
return typeof type === "object" && type !== null && "optional" in type;
|
|
1274
|
+
}
|
|
1275
|
+
function isAbiResponse(type) {
|
|
1276
|
+
return typeof type === "object" && type !== null && "response" in type;
|
|
1277
|
+
}
|
|
1278
|
+
function isAbiBuffer(type) {
|
|
1279
|
+
return typeof type === "object" && type !== null && "buff" in type;
|
|
1280
|
+
}
|
|
1281
|
+
function isAbiStringAscii(type) {
|
|
1282
|
+
return typeof type === "object" && type !== null && "string-ascii" in type;
|
|
1283
|
+
}
|
|
1284
|
+
function isAbiStringUtf8(type) {
|
|
1285
|
+
return typeof type === "object" && type !== null && "string-utf8" in type;
|
|
1286
|
+
}
|
|
1287
|
+
function isAbiTraitReference(type) {
|
|
1288
|
+
return type === "trait_reference";
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// src/clarity/bridge.ts
|
|
1292
|
+
var CV_TYPE_TAGS = new Set([
|
|
1293
|
+
"int",
|
|
1294
|
+
"uint",
|
|
1295
|
+
"true",
|
|
1296
|
+
"false",
|
|
1297
|
+
"address",
|
|
1298
|
+
"contract",
|
|
1299
|
+
"ascii",
|
|
1300
|
+
"utf8",
|
|
1301
|
+
"buffer",
|
|
1302
|
+
"list",
|
|
1303
|
+
"tuple",
|
|
1304
|
+
"none",
|
|
1305
|
+
"some",
|
|
1306
|
+
"ok",
|
|
1307
|
+
"err"
|
|
1308
|
+
]);
|
|
1309
|
+
function isClarityValue(value) {
|
|
1310
|
+
if (typeof value !== "object" || value === null)
|
|
1311
|
+
return false;
|
|
1312
|
+
const type = value.type;
|
|
1313
|
+
if (typeof type !== "string" || !CV_TYPE_TAGS.has(type))
|
|
1314
|
+
return false;
|
|
1315
|
+
if (type === "true" || type === "false" || type === "none")
|
|
1316
|
+
return true;
|
|
1317
|
+
return "value" in value;
|
|
1318
|
+
}
|
|
1319
|
+
var HEX_PAIRS_REGEX = /^(?:[0-9a-fA-F]{2})+$/;
|
|
1320
|
+
function coerceBuffer(value) {
|
|
1321
|
+
if (value instanceof Uint8Array)
|
|
1322
|
+
return bufferCV(value);
|
|
1323
|
+
if (typeof value === "object" && value !== null && "type" in value) {
|
|
1324
|
+
const tagged = value;
|
|
1325
|
+
if (tagged.type === "buffer" && isClarityValue(value))
|
|
1326
|
+
return value;
|
|
1327
|
+
if (typeof tagged.value === "string") {
|
|
1328
|
+
if (tagged.type === "ascii")
|
|
1329
|
+
return Cl.bufferFromAscii(tagged.value);
|
|
1330
|
+
if (tagged.type === "utf8")
|
|
1331
|
+
return Cl.bufferFromUtf8(tagged.value);
|
|
1332
|
+
if (tagged.type === "hex")
|
|
1333
|
+
return Cl.bufferFromHex(tagged.value);
|
|
1334
|
+
}
|
|
1335
|
+
throw new Error(`Unsupported buffer input: ${JSON.stringify(value)}`);
|
|
1336
|
+
}
|
|
1337
|
+
if (typeof value === "string") {
|
|
1338
|
+
const hex = value.startsWith("0x") ? value.slice(2) : value;
|
|
1339
|
+
if (HEX_PAIRS_REGEX.test(hex))
|
|
1340
|
+
return Cl.bufferFromHex(hex);
|
|
1341
|
+
return Cl.bufferFromUtf8(value);
|
|
1342
|
+
}
|
|
1343
|
+
throw new Error("buffer arg expects Uint8Array, hex string, { type, value }, or BufferCV");
|
|
1344
|
+
}
|
|
1345
|
+
function jsToClarityValue(abiType, value) {
|
|
1346
|
+
if (isAbiBuffer(abiType))
|
|
1347
|
+
return coerceBuffer(value);
|
|
1348
|
+
if (isClarityValue(value))
|
|
1349
|
+
return value;
|
|
1350
|
+
if (abiType === "uint128")
|
|
1351
|
+
return uintCV(value);
|
|
1352
|
+
if (abiType === "int128")
|
|
1353
|
+
return intCV(value);
|
|
1354
|
+
if (abiType === "bool")
|
|
1355
|
+
return boolCV(value);
|
|
1356
|
+
if (abiType === "principal" || abiType === "trait_reference")
|
|
1357
|
+
return Cl.principal(value);
|
|
1358
|
+
if (isAbiStringAscii(abiType))
|
|
1359
|
+
return stringAsciiCV(value);
|
|
1360
|
+
if (isAbiStringUtf8(abiType))
|
|
1361
|
+
return stringUtf8CV(value);
|
|
1362
|
+
if (isAbiList(abiType)) {
|
|
1363
|
+
const arr = value;
|
|
1364
|
+
return listCV(arr.map((item) => jsToClarityValue(abiType.list.type, item)));
|
|
1365
|
+
}
|
|
1366
|
+
if (isAbiTuple(abiType)) {
|
|
1367
|
+
const obj = value;
|
|
1368
|
+
const data = {};
|
|
1369
|
+
for (const field of abiType.tuple) {
|
|
1370
|
+
const camelKey = toCamelCase(field.name);
|
|
1371
|
+
const hasOriginal = field.name in obj;
|
|
1372
|
+
const hasCamel = camelKey in obj;
|
|
1373
|
+
const fieldValue = hasOriginal ? obj[field.name] : hasCamel ? obj[camelKey] : undefined;
|
|
1374
|
+
if (fieldValue === undefined && !isAbiOptional(field.type)) {
|
|
1375
|
+
throw new Error(`Missing tuple field: ${field.name}`);
|
|
1376
|
+
}
|
|
1377
|
+
data[field.name] = jsToClarityValue(field.type, fieldValue);
|
|
1378
|
+
}
|
|
1379
|
+
return tupleCV(data);
|
|
1380
|
+
}
|
|
1381
|
+
if (isAbiOptional(abiType)) {
|
|
1382
|
+
if (value === null || value === undefined)
|
|
1383
|
+
return noneCV();
|
|
1384
|
+
return someCV(jsToClarityValue(abiType.optional, value));
|
|
1385
|
+
}
|
|
1386
|
+
if (isAbiResponse(abiType)) {
|
|
1387
|
+
const obj = value;
|
|
1388
|
+
if ("ok" in obj && !("err" in obj))
|
|
1389
|
+
return responseOkCV(jsToClarityValue(abiType.response.ok, obj.ok));
|
|
1390
|
+
if ("err" in obj && !("ok" in obj))
|
|
1391
|
+
return responseErrorCV(jsToClarityValue(abiType.response.error, obj.err));
|
|
1392
|
+
throw new Error("Response must have exactly 'ok' or 'err' property");
|
|
1393
|
+
}
|
|
1394
|
+
throw new Error(`Unknown ABI type: ${JSON.stringify(abiType)}`);
|
|
1395
|
+
}
|
|
1396
|
+
function clarityValueToJS(abiType, cv) {
|
|
1397
|
+
return clarityValueToJSInner(abiType, cv);
|
|
1398
|
+
}
|
|
1399
|
+
function clarityValueToJSUntyped(abiType, cv) {
|
|
1400
|
+
return clarityValueToJSInner(abiType, cv);
|
|
1401
|
+
}
|
|
1402
|
+
function clarityValueToJSInner(abiType, cv) {
|
|
1403
|
+
if (abiType === "uint128" || abiType === "int128") {
|
|
1404
|
+
if (cv.type !== "int" && cv.type !== "uint")
|
|
1405
|
+
throw new Error(`Expected int/uint CV, got ${cv.type}`);
|
|
1406
|
+
return cv.value;
|
|
1407
|
+
}
|
|
1408
|
+
if (abiType === "bool") {
|
|
1409
|
+
if (cv.type === "true")
|
|
1410
|
+
return true;
|
|
1411
|
+
if (cv.type === "false")
|
|
1412
|
+
return false;
|
|
1413
|
+
throw new Error(`Expected bool CV, got ${cv.type}`);
|
|
1414
|
+
}
|
|
1415
|
+
if (abiType === "principal" || abiType === "trait_reference") {
|
|
1416
|
+
if (cv.type !== "address" && cv.type !== "contract")
|
|
1417
|
+
throw new Error(`Expected principal CV, got ${cv.type}`);
|
|
1418
|
+
return cv.value;
|
|
1419
|
+
}
|
|
1420
|
+
if (isAbiStringAscii(abiType) || isAbiStringUtf8(abiType)) {
|
|
1421
|
+
if (cv.type !== "ascii" && cv.type !== "utf8")
|
|
1422
|
+
throw new Error(`Expected string CV, got ${cv.type}`);
|
|
1423
|
+
return cv.value;
|
|
1424
|
+
}
|
|
1425
|
+
if (isAbiBuffer(abiType)) {
|
|
1426
|
+
if (cv.type !== "buffer")
|
|
1427
|
+
throw new Error(`Expected buffer CV, got ${cv.type}`);
|
|
1428
|
+
const hex = cv.value;
|
|
1429
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
1430
|
+
for (let i = 0;i < bytes.length; i++) {
|
|
1431
|
+
bytes[i] = Number.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
|
|
1432
|
+
}
|
|
1433
|
+
return bytes;
|
|
1434
|
+
}
|
|
1435
|
+
if (isAbiList(abiType)) {
|
|
1436
|
+
if (cv.type !== "list")
|
|
1437
|
+
throw new Error(`Expected list CV, got ${cv.type}`);
|
|
1438
|
+
return cv.value.map((item) => clarityValueToJSInner(abiType.list.type, item));
|
|
1439
|
+
}
|
|
1440
|
+
if (isAbiTuple(abiType)) {
|
|
1441
|
+
if (cv.type !== "tuple")
|
|
1442
|
+
throw new Error(`Expected tuple CV, got ${cv.type}`);
|
|
1443
|
+
const result = {};
|
|
1444
|
+
for (const field of abiType.tuple) {
|
|
1445
|
+
const fieldCV = cv.value[field.name];
|
|
1446
|
+
if (!fieldCV)
|
|
1447
|
+
throw new Error(`Missing tuple field in CV: ${field.name}`);
|
|
1448
|
+
const camelKey = toCamelCase(field.name);
|
|
1449
|
+
result[camelKey] = clarityValueToJSInner(field.type, fieldCV);
|
|
1450
|
+
}
|
|
1451
|
+
return result;
|
|
1452
|
+
}
|
|
1453
|
+
if (isAbiOptional(abiType)) {
|
|
1454
|
+
if (cv.type === "none")
|
|
1455
|
+
return null;
|
|
1456
|
+
if (cv.type === "some")
|
|
1457
|
+
return clarityValueToJSInner(abiType.optional, cv.value);
|
|
1458
|
+
throw new Error(`Expected optional CV, got ${cv.type}`);
|
|
1459
|
+
}
|
|
1460
|
+
if (isAbiResponse(abiType)) {
|
|
1461
|
+
if (cv.type === "ok")
|
|
1462
|
+
return { ok: clarityValueToJSInner(abiType.response.ok, cv.value) };
|
|
1463
|
+
if (cv.type === "err")
|
|
1464
|
+
return { err: clarityValueToJSInner(abiType.response.error, cv.value) };
|
|
1465
|
+
throw new Error(`Expected response CV, got ${cv.type}`);
|
|
1466
|
+
}
|
|
1467
|
+
throw new Error(`Unknown ABI type: ${JSON.stringify(abiType)}`);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// src/transactions/types.ts
|
|
1471
|
+
var AuthType = {
|
|
1472
|
+
Standard: 4,
|
|
1473
|
+
Sponsored: 5
|
|
1474
|
+
};
|
|
1475
|
+
var PayloadType = {
|
|
1476
|
+
TokenTransfer: 0,
|
|
1477
|
+
SmartContract: 1,
|
|
1478
|
+
ContractCall: 2,
|
|
1479
|
+
PoisonMicroblock: 3,
|
|
1480
|
+
Coinbase: 4,
|
|
1481
|
+
CoinbaseToAltRecipient: 5,
|
|
1482
|
+
VersionedSmartContract: 6,
|
|
1483
|
+
TenureChange: 7,
|
|
1484
|
+
NakamotoCoinbase: 8
|
|
1485
|
+
};
|
|
1486
|
+
var ClarityVersion = {
|
|
1487
|
+
Clarity1: 1,
|
|
1488
|
+
Clarity2: 2,
|
|
1489
|
+
Clarity3: 3,
|
|
1490
|
+
Clarity4: 4,
|
|
1491
|
+
Clarity5: 5,
|
|
1492
|
+
Clarity6: 6
|
|
1493
|
+
};
|
|
1494
|
+
var AnchorMode = {
|
|
1495
|
+
OnChainOnly: 1,
|
|
1496
|
+
OffChainOnly: 2,
|
|
1497
|
+
Any: 3
|
|
1498
|
+
};
|
|
1499
|
+
var PostConditionModeWire = {
|
|
1500
|
+
Allow: 1,
|
|
1501
|
+
Deny: 2,
|
|
1502
|
+
Originator: 3
|
|
1503
|
+
};
|
|
1504
|
+
var AddressHashMode = {
|
|
1505
|
+
P2PKH: 0,
|
|
1506
|
+
P2SH: 1,
|
|
1507
|
+
P2WPKH: 2,
|
|
1508
|
+
P2WSH: 3,
|
|
1509
|
+
P2SH_NonSequential: 5,
|
|
1510
|
+
P2WSH_P2SH_NonSequential: 7
|
|
1511
|
+
};
|
|
1512
|
+
var PubKeyEncoding = {
|
|
1513
|
+
Compressed: 0,
|
|
1514
|
+
Uncompressed: 1
|
|
1515
|
+
};
|
|
1516
|
+
var FungibleConditionCode = {
|
|
1517
|
+
Equal: 1,
|
|
1518
|
+
Greater: 2,
|
|
1519
|
+
GreaterEqual: 3,
|
|
1520
|
+
Less: 4,
|
|
1521
|
+
LessEqual: 5
|
|
1522
|
+
};
|
|
1523
|
+
var NonFungibleConditionCode = {
|
|
1524
|
+
Sends: 16,
|
|
1525
|
+
DoesNotSend: 17,
|
|
1526
|
+
MaybeSent: 18
|
|
1527
|
+
};
|
|
1528
|
+
var PoxConditionCode = {
|
|
1529
|
+
WillNotPerform: 48,
|
|
1530
|
+
MayPerform: 49,
|
|
1531
|
+
WillPerform: 50
|
|
1532
|
+
};
|
|
1533
|
+
var PostConditionPrincipalId = {
|
|
1534
|
+
Origin: 1,
|
|
1535
|
+
Standard: 2,
|
|
1536
|
+
Contract: 3
|
|
1537
|
+
};
|
|
1538
|
+
var AssetType = {
|
|
1539
|
+
STX: 0,
|
|
1540
|
+
Fungible: 1,
|
|
1541
|
+
NonFungible: 2,
|
|
1542
|
+
Staking: 3,
|
|
1543
|
+
Pox: 4
|
|
1544
|
+
};
|
|
1545
|
+
var AuthFieldType = {
|
|
1546
|
+
PublicKeyCompressed: 0,
|
|
1547
|
+
PublicKeyUncompressed: 1,
|
|
1548
|
+
SignatureCompressed: 2,
|
|
1549
|
+
SignatureUncompressed: 3
|
|
1550
|
+
};
|
|
1551
|
+
var RECOVERABLE_ECDSA_SIG_LENGTH_BYTES = 65;
|
|
1552
|
+
var MEMO_MAX_LENGTH_BYTES = 34;
|
|
1553
|
+
var MAX_STRING_LENGTH_BYTES = 128;
|
|
1554
|
+
var COINBASE_BYTES_LENGTH = 32;
|
|
1555
|
+
var VRF_PROOF_BYTES_LENGTH = 80;
|
|
1556
|
+
var MICROBLOCK_HEADER_BYTES_LENGTH = 132;
|
|
1557
|
+
var TenureChangeCause = {
|
|
1558
|
+
BlockFound: 0,
|
|
1559
|
+
Extended: 1,
|
|
1560
|
+
ExtendedRuntime: 2,
|
|
1561
|
+
ExtendedReadCount: 3,
|
|
1562
|
+
ExtendedReadLength: 4,
|
|
1563
|
+
ExtendedWriteCount: 5,
|
|
1564
|
+
ExtendedWriteLength: 6
|
|
1565
|
+
};
|
|
1566
|
+
|
|
1567
|
+
// src/transactions/wire/deserialize.ts
|
|
1568
|
+
function readSpendingCondition(r) {
|
|
1569
|
+
const hashMode = r.readUInt8();
|
|
1570
|
+
const signer = bytesToHex(r.readBytes(20));
|
|
1571
|
+
const nonce = r.readBigUInt64BE();
|
|
1572
|
+
const fee = r.readBigUInt64BE();
|
|
1573
|
+
if (hashMode === AddressHashMode.P2PKH || hashMode === AddressHashMode.P2WPKH) {
|
|
1574
|
+
const keyEncoding = r.readUInt8();
|
|
1575
|
+
const signature = bytesToHex(r.readBytes(RECOVERABLE_ECDSA_SIG_LENGTH_BYTES));
|
|
1576
|
+
return {
|
|
1577
|
+
hashMode,
|
|
1578
|
+
signer,
|
|
1579
|
+
nonce,
|
|
1580
|
+
fee,
|
|
1581
|
+
keyEncoding,
|
|
1582
|
+
signature
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
const fieldCount = r.readUInt32BE();
|
|
1586
|
+
const fields = [];
|
|
1587
|
+
for (let i = 0;i < fieldCount; i++) {
|
|
1588
|
+
const fieldType = r.readUInt8();
|
|
1589
|
+
if (fieldType <= 1) {
|
|
1590
|
+
const keyLen = fieldType === 0 ? 33 : 65;
|
|
1591
|
+
fields.push({
|
|
1592
|
+
type: "publicKey",
|
|
1593
|
+
pubKeyEncoding: fieldType,
|
|
1594
|
+
data: bytesToHex(r.readBytes(keyLen))
|
|
1595
|
+
});
|
|
1596
|
+
} else {
|
|
1597
|
+
fields.push({
|
|
1598
|
+
type: "signature",
|
|
1599
|
+
pubKeyEncoding: fieldType === 2 ? 0 : 1,
|
|
1600
|
+
data: bytesToHex(r.readBytes(RECOVERABLE_ECDSA_SIG_LENGTH_BYTES))
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
const signaturesRequired = r.readUInt16BE();
|
|
1605
|
+
return {
|
|
1606
|
+
hashMode,
|
|
1607
|
+
signer,
|
|
1608
|
+
nonce,
|
|
1609
|
+
fee,
|
|
1610
|
+
fields,
|
|
1611
|
+
signaturesRequired
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
function readAuthorization(r) {
|
|
1615
|
+
const authType = r.readUInt8();
|
|
1616
|
+
const spendingCondition = readSpendingCondition(r);
|
|
1617
|
+
if (authType === AuthType.Standard) {
|
|
1618
|
+
return { authType: AuthType.Standard, spendingCondition };
|
|
1619
|
+
}
|
|
1620
|
+
const sponsorSpendingCondition = readSpendingCondition(r);
|
|
1621
|
+
return {
|
|
1622
|
+
authType: AuthType.Sponsored,
|
|
1623
|
+
spendingCondition,
|
|
1624
|
+
sponsorSpendingCondition
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
function readAssetInfo(r) {
|
|
1628
|
+
return {
|
|
1629
|
+
address: readAddress(r),
|
|
1630
|
+
contractName: readLPString(r),
|
|
1631
|
+
assetName: readLPString(r)
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
function readPostConditionPrincipal(r) {
|
|
1635
|
+
const type = r.readUInt8();
|
|
1636
|
+
switch (type) {
|
|
1637
|
+
case PostConditionPrincipalId.Origin:
|
|
1638
|
+
return { type: "origin" };
|
|
1639
|
+
case PostConditionPrincipalId.Standard:
|
|
1640
|
+
return { type: "standard", address: readAddress(r) };
|
|
1641
|
+
case PostConditionPrincipalId.Contract:
|
|
1642
|
+
return {
|
|
1643
|
+
type: "contract",
|
|
1644
|
+
address: readAddress(r),
|
|
1645
|
+
contractName: readLPString(r)
|
|
1646
|
+
};
|
|
1647
|
+
default:
|
|
1648
|
+
return { type: "origin" };
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
function readPostCondition(r) {
|
|
1652
|
+
const assetType = r.readUInt8();
|
|
1653
|
+
const principal = readPostConditionPrincipal(r);
|
|
1654
|
+
switch (assetType) {
|
|
1655
|
+
case AssetType.STX:
|
|
1656
|
+
return {
|
|
1657
|
+
type: "stx",
|
|
1658
|
+
principal,
|
|
1659
|
+
conditionCode: r.readUInt8(),
|
|
1660
|
+
amount: r.readBigUInt64BE()
|
|
1661
|
+
};
|
|
1662
|
+
case AssetType.Fungible:
|
|
1663
|
+
return {
|
|
1664
|
+
type: "ft",
|
|
1665
|
+
principal,
|
|
1666
|
+
asset: readAssetInfo(r),
|
|
1667
|
+
conditionCode: r.readUInt8(),
|
|
1668
|
+
amount: r.readBigUInt64BE()
|
|
1669
|
+
};
|
|
1670
|
+
case AssetType.NonFungible:
|
|
1671
|
+
return {
|
|
1672
|
+
type: "nft",
|
|
1673
|
+
principal,
|
|
1674
|
+
asset: readAssetInfo(r),
|
|
1675
|
+
assetId: readCV(r),
|
|
1676
|
+
conditionCode: r.readUInt8()
|
|
1677
|
+
};
|
|
1678
|
+
case AssetType.Staking:
|
|
1679
|
+
return {
|
|
1680
|
+
type: "staking",
|
|
1681
|
+
principal,
|
|
1682
|
+
conditionCode: r.readUInt8(),
|
|
1683
|
+
amount: r.readBigUInt64BE()
|
|
1684
|
+
};
|
|
1685
|
+
case AssetType.Pox:
|
|
1686
|
+
return {
|
|
1687
|
+
type: "pox",
|
|
1688
|
+
principal,
|
|
1689
|
+
conditionCode: r.readUInt8()
|
|
1690
|
+
};
|
|
1691
|
+
default:
|
|
1692
|
+
throw new Error(`Unknown post-condition asset type: 0x${assetType.toString(16).padStart(2, "0")}`);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
function readPostConditions(r) {
|
|
1696
|
+
const count = r.readUInt32BE();
|
|
1697
|
+
const pcs = [];
|
|
1698
|
+
for (let i = 0;i < count; i++) {
|
|
1699
|
+
try {
|
|
1700
|
+
pcs.push(readPostCondition(r));
|
|
1701
|
+
} catch (e) {
|
|
1702
|
+
if (e instanceof Error && e.message.startsWith("Unknown post-condition asset type:")) {
|
|
1703
|
+
throw new Error(`${e.message} (post-condition ${i + 1}/${count})`);
|
|
1704
|
+
}
|
|
1705
|
+
throw e;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return pcs;
|
|
1709
|
+
}
|
|
1710
|
+
function readPayload(r) {
|
|
1711
|
+
const payloadType = r.readUInt8();
|
|
1712
|
+
switch (payloadType) {
|
|
1713
|
+
case PayloadType.TokenTransfer: {
|
|
1714
|
+
const recipient = readCV(r);
|
|
1715
|
+
const amount = r.readBigUInt64BE();
|
|
1716
|
+
const memoBytes = r.readBytes(MEMO_MAX_LENGTH_BYTES);
|
|
1717
|
+
const memo = bytesToAscii(memoBytes).replace(/\0+$/, "");
|
|
1718
|
+
return {
|
|
1719
|
+
payloadType: PayloadType.TokenTransfer,
|
|
1720
|
+
recipient,
|
|
1721
|
+
amount,
|
|
1722
|
+
memo
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
case PayloadType.ContractCall: {
|
|
1726
|
+
const contractAddress = readAddress(r);
|
|
1727
|
+
const contractName = readLPString(r);
|
|
1728
|
+
const functionName = readLPString(r);
|
|
1729
|
+
const numArgs = r.readUInt32BE();
|
|
1730
|
+
const functionArgs = [];
|
|
1731
|
+
for (let i = 0;i < numArgs; i++)
|
|
1732
|
+
functionArgs.push(readCV(r));
|
|
1733
|
+
return {
|
|
1734
|
+
payloadType: PayloadType.ContractCall,
|
|
1735
|
+
contractAddress,
|
|
1736
|
+
contractName,
|
|
1737
|
+
functionName,
|
|
1738
|
+
functionArgs
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
case PayloadType.SmartContract: {
|
|
1742
|
+
const contractName = readLPString(r);
|
|
1743
|
+
const codeBody = readLPString(r, 4);
|
|
1744
|
+
return { payloadType: PayloadType.SmartContract, contractName, codeBody };
|
|
1745
|
+
}
|
|
1746
|
+
case PayloadType.VersionedSmartContract: {
|
|
1747
|
+
const clarityVersion = r.readUInt8();
|
|
1748
|
+
const contractName = readLPString(r);
|
|
1749
|
+
const codeBody = readLPString(r, 4);
|
|
1750
|
+
return {
|
|
1751
|
+
payloadType: PayloadType.VersionedSmartContract,
|
|
1752
|
+
clarityVersion,
|
|
1753
|
+
contractName,
|
|
1754
|
+
codeBody
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
case PayloadType.Coinbase: {
|
|
1758
|
+
const coinbaseBuffer = bytesToHex(r.readBytes(COINBASE_BYTES_LENGTH));
|
|
1759
|
+
return { payloadType: PayloadType.Coinbase, coinbaseBuffer };
|
|
1760
|
+
}
|
|
1761
|
+
case PayloadType.CoinbaseToAltRecipient: {
|
|
1762
|
+
const coinbaseBuffer = bytesToHex(r.readBytes(COINBASE_BYTES_LENGTH));
|
|
1763
|
+
const recipient = readCV(r);
|
|
1764
|
+
return {
|
|
1765
|
+
payloadType: PayloadType.CoinbaseToAltRecipient,
|
|
1766
|
+
coinbaseBuffer,
|
|
1767
|
+
recipient
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
case PayloadType.PoisonMicroblock: {
|
|
1771
|
+
const header1 = bytesToHex(r.readBytes(MICROBLOCK_HEADER_BYTES_LENGTH));
|
|
1772
|
+
const header2 = bytesToHex(r.readBytes(MICROBLOCK_HEADER_BYTES_LENGTH));
|
|
1773
|
+
return { payloadType: PayloadType.PoisonMicroblock, header1, header2 };
|
|
1774
|
+
}
|
|
1775
|
+
case PayloadType.TenureChange: {
|
|
1776
|
+
const tenureConsensusHash = bytesToHex(r.readBytes(20));
|
|
1777
|
+
const prevTenureConsensusHash = bytesToHex(r.readBytes(20));
|
|
1778
|
+
const burnViewConsensusHash = bytesToHex(r.readBytes(20));
|
|
1779
|
+
const previousTenureEnd = bytesToHex(r.readBytes(32));
|
|
1780
|
+
const previousTenureBlocks = r.readUInt32BE();
|
|
1781
|
+
const cause = r.readUInt8();
|
|
1782
|
+
const pubkeyHash = bytesToHex(r.readBytes(20));
|
|
1783
|
+
return {
|
|
1784
|
+
payloadType: PayloadType.TenureChange,
|
|
1785
|
+
tenureConsensusHash,
|
|
1786
|
+
prevTenureConsensusHash,
|
|
1787
|
+
burnViewConsensusHash,
|
|
1788
|
+
previousTenureEnd,
|
|
1789
|
+
previousTenureBlocks,
|
|
1790
|
+
cause,
|
|
1791
|
+
pubkeyHash
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
case PayloadType.NakamotoCoinbase: {
|
|
1795
|
+
const coinbaseBuffer = bytesToHex(r.readBytes(COINBASE_BYTES_LENGTH));
|
|
1796
|
+
const optionalCV = readCV(r);
|
|
1797
|
+
const recipient = optionalCV.type === "none" ? null : optionalCV.type === "some" ? optionalCV.value : optionalCV;
|
|
1798
|
+
const vrfProof = bytesToHex(r.readBytes(VRF_PROOF_BYTES_LENGTH));
|
|
1799
|
+
return {
|
|
1800
|
+
payloadType: PayloadType.NakamotoCoinbase,
|
|
1801
|
+
coinbaseBuffer,
|
|
1802
|
+
recipient,
|
|
1803
|
+
vrfProof
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
default:
|
|
1807
|
+
throw new Error(`Unknown payload type: ${payloadType}`);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
function deserializePostConditionWire(input) {
|
|
1811
|
+
const bytes = typeof input === "string" ? hexToBytes(without0x(input)) : input;
|
|
1812
|
+
return readPostCondition(new BytesReader(bytes));
|
|
1813
|
+
}
|
|
1814
|
+
function deserializeTransaction(input, meta) {
|
|
1815
|
+
const bytes = typeof input === "string" ? hexToBytes(without0x(input)) : input;
|
|
1816
|
+
const r = new BytesReader(bytes);
|
|
1817
|
+
const tx = {
|
|
1818
|
+
version: r.readUInt8(),
|
|
1819
|
+
chainId: r.readUInt32BE(),
|
|
1820
|
+
auth: readAuthorization(r),
|
|
1821
|
+
anchorMode: r.readUInt8(),
|
|
1822
|
+
postConditionMode: r.readUInt8(),
|
|
1823
|
+
postConditions: readPostConditions(r),
|
|
1824
|
+
payload: readPayload(r)
|
|
1825
|
+
};
|
|
1826
|
+
if (meta?._multisig) {
|
|
1827
|
+
tx._multisig = meta._multisig;
|
|
1828
|
+
}
|
|
1829
|
+
return tx;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// src/transactions/wire/serialize.ts
|
|
1833
|
+
function serializeAddress2(c32Address) {
|
|
1834
|
+
const [version, hash1602] = c32addressDecode(c32Address);
|
|
1835
|
+
return concatBytes(new Uint8Array([version]), hexToBytes(hash1602));
|
|
1836
|
+
}
|
|
1837
|
+
function serializeLPString2(str, prefixBytes = 1) {
|
|
1838
|
+
const content = utf8ToBytes(str);
|
|
1839
|
+
const prefix = hexToBytes(intToHex(content.byteLength, prefixBytes));
|
|
1840
|
+
return concatBytes(prefix, content);
|
|
1841
|
+
}
|
|
1842
|
+
function serializeLPStringLong(str) {
|
|
1843
|
+
return serializeLPString2(str, 4);
|
|
1844
|
+
}
|
|
1845
|
+
function serializeMemo(memo) {
|
|
1846
|
+
const content = asciiToBytes(memo);
|
|
1847
|
+
const padded = new Uint8Array(MEMO_MAX_LENGTH_BYTES);
|
|
1848
|
+
padded.set(content.slice(0, MEMO_MAX_LENGTH_BYTES));
|
|
1849
|
+
return padded;
|
|
1850
|
+
}
|
|
1851
|
+
function serializeSpendingCondition(condition) {
|
|
1852
|
+
const parts = [
|
|
1853
|
+
writeUInt8(condition.hashMode),
|
|
1854
|
+
hexToBytes(condition.signer),
|
|
1855
|
+
intToBytes(condition.nonce, 8),
|
|
1856
|
+
intToBytes(condition.fee, 8)
|
|
1857
|
+
];
|
|
1858
|
+
if ("signature" in condition) {
|
|
1859
|
+
const sc = condition;
|
|
1860
|
+
parts.push(writeUInt8(sc.keyEncoding));
|
|
1861
|
+
parts.push(hexToBytes(sc.signature));
|
|
1862
|
+
} else {
|
|
1863
|
+
const mc = condition;
|
|
1864
|
+
parts.push(writeUInt32BE(mc.fields.length));
|
|
1865
|
+
for (const field of mc.fields) {
|
|
1866
|
+
parts.push(serializeAuthField(field));
|
|
1867
|
+
}
|
|
1868
|
+
parts.push(writeUInt16BE(mc.signaturesRequired));
|
|
1869
|
+
}
|
|
1870
|
+
return concatBytes(...parts);
|
|
1871
|
+
}
|
|
1872
|
+
function serializeAuthField(field) {
|
|
1873
|
+
if (field.type === "publicKey") {
|
|
1874
|
+
const typeId2 = field.pubKeyEncoding === 0 ? 0 : 1;
|
|
1875
|
+
return concatBytes(writeUInt8(typeId2), hexToBytes(field.data));
|
|
1876
|
+
}
|
|
1877
|
+
const typeId = field.pubKeyEncoding === 0 ? 2 : 3;
|
|
1878
|
+
return concatBytes(writeUInt8(typeId), hexToBytes(field.data));
|
|
1879
|
+
}
|
|
1880
|
+
function serializeAuthorization(auth) {
|
|
1881
|
+
const parts = [writeUInt8(auth.authType)];
|
|
1882
|
+
parts.push(serializeSpendingCondition(auth.spendingCondition));
|
|
1883
|
+
if (auth.authType === AuthType.Sponsored) {
|
|
1884
|
+
parts.push(serializeSpendingCondition(auth.sponsorSpendingCondition));
|
|
1885
|
+
}
|
|
1886
|
+
return concatBytes(...parts);
|
|
1887
|
+
}
|
|
1888
|
+
function serializePrincipal(principal) {
|
|
1889
|
+
switch (principal.type) {
|
|
1890
|
+
case "origin":
|
|
1891
|
+
return writeUInt8(PostConditionPrincipalId.Origin);
|
|
1892
|
+
case "standard":
|
|
1893
|
+
return concatBytes(writeUInt8(PostConditionPrincipalId.Standard), serializeAddress2(principal.address));
|
|
1894
|
+
case "contract":
|
|
1895
|
+
return concatBytes(writeUInt8(PostConditionPrincipalId.Contract), serializeAddress2(principal.address), serializeLPString2(principal.contractName));
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
function serializeAssetInfo(asset) {
|
|
1899
|
+
return concatBytes(serializeAddress2(asset.address), serializeLPString2(asset.contractName), serializeLPString2(asset.assetName));
|
|
1900
|
+
}
|
|
1901
|
+
function serializePostConditionWire(pc) {
|
|
1902
|
+
const parts = [];
|
|
1903
|
+
switch (pc.type) {
|
|
1904
|
+
case "stx":
|
|
1905
|
+
parts.push(writeUInt8(AssetType.STX));
|
|
1906
|
+
parts.push(serializePrincipal(pc.principal));
|
|
1907
|
+
parts.push(writeUInt8(pc.conditionCode));
|
|
1908
|
+
parts.push(intToBytes(pc.amount, 8));
|
|
1909
|
+
break;
|
|
1910
|
+
case "ft":
|
|
1911
|
+
parts.push(writeUInt8(AssetType.Fungible));
|
|
1912
|
+
parts.push(serializePrincipal(pc.principal));
|
|
1913
|
+
parts.push(serializeAssetInfo(pc.asset));
|
|
1914
|
+
parts.push(writeUInt8(pc.conditionCode));
|
|
1915
|
+
parts.push(intToBytes(pc.amount, 8));
|
|
1916
|
+
break;
|
|
1917
|
+
case "nft":
|
|
1918
|
+
parts.push(writeUInt8(AssetType.NonFungible));
|
|
1919
|
+
parts.push(serializePrincipal(pc.principal));
|
|
1920
|
+
parts.push(serializeAssetInfo(pc.asset));
|
|
1921
|
+
parts.push(serializeCVBytes(pc.assetId));
|
|
1922
|
+
parts.push(writeUInt8(pc.conditionCode));
|
|
1923
|
+
break;
|
|
1924
|
+
case "staking":
|
|
1925
|
+
parts.push(writeUInt8(AssetType.Staking));
|
|
1926
|
+
parts.push(serializePrincipal(pc.principal));
|
|
1927
|
+
parts.push(writeUInt8(pc.conditionCode));
|
|
1928
|
+
parts.push(intToBytes(pc.amount, 8));
|
|
1929
|
+
break;
|
|
1930
|
+
case "pox":
|
|
1931
|
+
parts.push(writeUInt8(AssetType.Pox));
|
|
1932
|
+
parts.push(serializePrincipal(pc.principal));
|
|
1933
|
+
parts.push(writeUInt8(pc.conditionCode));
|
|
1934
|
+
break;
|
|
1935
|
+
}
|
|
1936
|
+
return concatBytes(...parts);
|
|
1937
|
+
}
|
|
1938
|
+
function serializePostConditions(pcs) {
|
|
1939
|
+
const parts = [writeUInt32BE(pcs.length)];
|
|
1940
|
+
for (const pc of pcs)
|
|
1941
|
+
parts.push(serializePostConditionWire(pc));
|
|
1942
|
+
return concatBytes(...parts);
|
|
1943
|
+
}
|
|
1944
|
+
function serializePayload(payload) {
|
|
1945
|
+
const parts = [writeUInt8(payload.payloadType)];
|
|
1946
|
+
switch (payload.payloadType) {
|
|
1947
|
+
case PayloadType.TokenTransfer:
|
|
1948
|
+
parts.push(serializeCVBytes(payload.recipient));
|
|
1949
|
+
parts.push(intToBytes(payload.amount, 8));
|
|
1950
|
+
parts.push(serializeMemo(payload.memo));
|
|
1951
|
+
break;
|
|
1952
|
+
case PayloadType.ContractCall:
|
|
1953
|
+
parts.push(serializeAddress2(payload.contractAddress));
|
|
1954
|
+
parts.push(serializeLPString2(payload.contractName));
|
|
1955
|
+
parts.push(serializeLPString2(payload.functionName));
|
|
1956
|
+
parts.push(writeUInt32BE(payload.functionArgs.length));
|
|
1957
|
+
for (const arg of payload.functionArgs) {
|
|
1958
|
+
parts.push(serializeCVBytes(arg));
|
|
1959
|
+
}
|
|
1960
|
+
break;
|
|
1961
|
+
case PayloadType.SmartContract:
|
|
1962
|
+
parts.push(serializeLPString2(payload.contractName));
|
|
1963
|
+
parts.push(serializeLPStringLong(payload.codeBody));
|
|
1964
|
+
break;
|
|
1965
|
+
case PayloadType.VersionedSmartContract:
|
|
1966
|
+
parts.push(writeUInt8(payload.clarityVersion ?? 2));
|
|
1967
|
+
parts.push(serializeLPString2(payload.contractName));
|
|
1968
|
+
parts.push(serializeLPStringLong(payload.codeBody));
|
|
1969
|
+
break;
|
|
1970
|
+
case PayloadType.Coinbase:
|
|
1971
|
+
parts.push(hexToBytes(payload.coinbaseBuffer));
|
|
1972
|
+
break;
|
|
1973
|
+
case PayloadType.CoinbaseToAltRecipient: {
|
|
1974
|
+
const cbAlt = payload;
|
|
1975
|
+
parts.push(hexToBytes(cbAlt.coinbaseBuffer));
|
|
1976
|
+
parts.push(serializeCVBytes(cbAlt.recipient));
|
|
1977
|
+
break;
|
|
1978
|
+
}
|
|
1979
|
+
case PayloadType.PoisonMicroblock: {
|
|
1980
|
+
const pm = payload;
|
|
1981
|
+
parts.push(hexToBytes(pm.header1));
|
|
1982
|
+
parts.push(hexToBytes(pm.header2));
|
|
1983
|
+
break;
|
|
1984
|
+
}
|
|
1985
|
+
case PayloadType.TenureChange: {
|
|
1986
|
+
const tc = payload;
|
|
1987
|
+
parts.push(hexToBytes(tc.tenureConsensusHash));
|
|
1988
|
+
parts.push(hexToBytes(tc.prevTenureConsensusHash));
|
|
1989
|
+
parts.push(hexToBytes(tc.burnViewConsensusHash));
|
|
1990
|
+
parts.push(hexToBytes(tc.previousTenureEnd));
|
|
1991
|
+
parts.push(writeUInt32BE(tc.previousTenureBlocks));
|
|
1992
|
+
parts.push(writeUInt8(tc.cause));
|
|
1993
|
+
parts.push(hexToBytes(tc.pubkeyHash));
|
|
1994
|
+
break;
|
|
1995
|
+
}
|
|
1996
|
+
case PayloadType.NakamotoCoinbase: {
|
|
1997
|
+
const nc = payload;
|
|
1998
|
+
parts.push(hexToBytes(nc.coinbaseBuffer));
|
|
1999
|
+
if (nc.recipient) {
|
|
2000
|
+
parts.push(serializeCVBytes({ type: "some", value: nc.recipient }));
|
|
2001
|
+
} else {
|
|
2002
|
+
parts.push(serializeCVBytes({ type: "none" }));
|
|
2003
|
+
}
|
|
2004
|
+
parts.push(hexToBytes(nc.vrfProof));
|
|
2005
|
+
break;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
return concatBytes(...parts);
|
|
2009
|
+
}
|
|
2010
|
+
var _txCache = new WeakMap;
|
|
2011
|
+
function clearTxCache(tx) {
|
|
2012
|
+
_txCache.delete(tx);
|
|
2013
|
+
}
|
|
2014
|
+
function serializeTransaction(tx) {
|
|
2015
|
+
const cached = _txCache.get(tx);
|
|
2016
|
+
if (cached)
|
|
2017
|
+
return cached;
|
|
2018
|
+
const bytes = concatBytes(writeUInt8(tx.version), writeUInt32BE(tx.chainId), serializeAuthorization(tx.auth), writeUInt8(tx.anchorMode), writeUInt8(tx.postConditionMode), serializePostConditions(tx.postConditions), serializePayload(tx.payload));
|
|
2019
|
+
_txCache.set(tx, bytes);
|
|
2020
|
+
return bytes;
|
|
2021
|
+
}
|
|
2022
|
+
function serializeTransactionHex(tx) {
|
|
2023
|
+
return bytesToHex(serializeTransaction(tx));
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
// src/postconditions/convert.ts
|
|
2027
|
+
var MAX_U64 = 18446744073709551615n;
|
|
2028
|
+
function parsePostConditionAmount(amount) {
|
|
2029
|
+
const value = intToBigInt(amount);
|
|
2030
|
+
if (value < 0n || value > MAX_U64) {
|
|
2031
|
+
throw new RangeError(`Post-condition amount must be between 0 and ${MAX_U64} (u64 max), received: ${value}`);
|
|
2032
|
+
}
|
|
2033
|
+
return value;
|
|
2034
|
+
}
|
|
2035
|
+
var FUNGIBLE_CODE_MAP = {
|
|
2036
|
+
eq: FungibleConditionCode.Equal,
|
|
2037
|
+
gt: FungibleConditionCode.Greater,
|
|
2038
|
+
gte: FungibleConditionCode.GreaterEqual,
|
|
2039
|
+
lt: FungibleConditionCode.Less,
|
|
2040
|
+
lte: FungibleConditionCode.LessEqual
|
|
2041
|
+
};
|
|
2042
|
+
var FUNGIBLE_CODE_REVERSE = {
|
|
2043
|
+
[FungibleConditionCode.Equal]: "eq",
|
|
2044
|
+
[FungibleConditionCode.Greater]: "gt",
|
|
2045
|
+
[FungibleConditionCode.GreaterEqual]: "gte",
|
|
2046
|
+
[FungibleConditionCode.Less]: "lt",
|
|
2047
|
+
[FungibleConditionCode.LessEqual]: "lte"
|
|
2048
|
+
};
|
|
2049
|
+
var NFT_CODE_MAP = {
|
|
2050
|
+
sent: NonFungibleConditionCode.Sends,
|
|
2051
|
+
"not-sent": NonFungibleConditionCode.DoesNotSend,
|
|
2052
|
+
"maybe-sent": NonFungibleConditionCode.MaybeSent
|
|
2053
|
+
};
|
|
2054
|
+
var NFT_CODE_REVERSE = {
|
|
2055
|
+
[NonFungibleConditionCode.Sends]: "sent",
|
|
2056
|
+
[NonFungibleConditionCode.DoesNotSend]: "not-sent",
|
|
2057
|
+
[NonFungibleConditionCode.MaybeSent]: "maybe-sent"
|
|
2058
|
+
};
|
|
2059
|
+
var POX_CODE_MAP = {
|
|
2060
|
+
"will-not-perform": PoxConditionCode.WillNotPerform,
|
|
2061
|
+
"may-perform": PoxConditionCode.MayPerform,
|
|
2062
|
+
"will-perform": PoxConditionCode.WillPerform
|
|
2063
|
+
};
|
|
2064
|
+
var POX_CODE_REVERSE = {
|
|
2065
|
+
[PoxConditionCode.WillNotPerform]: "will-not-perform",
|
|
2066
|
+
[PoxConditionCode.MayPerform]: "may-perform",
|
|
2067
|
+
[PoxConditionCode.WillPerform]: "will-perform"
|
|
2068
|
+
};
|
|
2069
|
+
function resolvePrincipal(address) {
|
|
2070
|
+
if (address === "origin")
|
|
2071
|
+
return { type: "origin" };
|
|
2072
|
+
const [addr, name] = address.split(".");
|
|
2073
|
+
if (name) {
|
|
2074
|
+
return { type: "contract", address: addr, contractName: name };
|
|
2075
|
+
}
|
|
2076
|
+
return { type: "standard", address: addr };
|
|
2077
|
+
}
|
|
2078
|
+
function principalToString(principal) {
|
|
2079
|
+
if (principal.type === "origin")
|
|
2080
|
+
return "origin";
|
|
2081
|
+
if (principal.type === "contract") {
|
|
2082
|
+
return `${principal.address}.${principal.contractName}`;
|
|
2083
|
+
}
|
|
2084
|
+
return principal.address;
|
|
2085
|
+
}
|
|
2086
|
+
function assetToString(asset) {
|
|
2087
|
+
return `${asset.address}.${asset.contractName}::${asset.assetName}`;
|
|
2088
|
+
}
|
|
2089
|
+
function parseAsset(asset) {
|
|
2090
|
+
const [contractId, tokenName] = asset.split("::");
|
|
2091
|
+
const [addr, name] = parseContractId(contractId);
|
|
2092
|
+
return { address: addr, contractName: name, assetName: tokenName };
|
|
2093
|
+
}
|
|
2094
|
+
function convertPostCondition(pc) {
|
|
2095
|
+
switch (pc.type) {
|
|
2096
|
+
case "stx-postcondition":
|
|
2097
|
+
return {
|
|
2098
|
+
type: "stx",
|
|
2099
|
+
principal: resolvePrincipal(pc.address),
|
|
2100
|
+
conditionCode: FUNGIBLE_CODE_MAP[pc.condition],
|
|
2101
|
+
amount: parsePostConditionAmount(pc.amount)
|
|
2102
|
+
};
|
|
2103
|
+
case "ft-postcondition":
|
|
2104
|
+
return {
|
|
2105
|
+
type: "ft",
|
|
2106
|
+
principal: resolvePrincipal(pc.address),
|
|
2107
|
+
asset: parseAsset(pc.asset),
|
|
2108
|
+
conditionCode: FUNGIBLE_CODE_MAP[pc.condition],
|
|
2109
|
+
amount: parsePostConditionAmount(pc.amount)
|
|
2110
|
+
};
|
|
2111
|
+
case "nft-postcondition":
|
|
2112
|
+
return {
|
|
2113
|
+
type: "nft",
|
|
2114
|
+
principal: resolvePrincipal(pc.address),
|
|
2115
|
+
asset: parseAsset(pc.asset),
|
|
2116
|
+
conditionCode: NFT_CODE_MAP[pc.condition],
|
|
2117
|
+
assetId: pc.assetId
|
|
2118
|
+
};
|
|
2119
|
+
case "staking-postcondition":
|
|
2120
|
+
return {
|
|
2121
|
+
type: "staking",
|
|
2122
|
+
principal: resolvePrincipal(pc.address),
|
|
2123
|
+
conditionCode: FUNGIBLE_CODE_MAP[pc.condition],
|
|
2124
|
+
amount: parsePostConditionAmount(pc.amount)
|
|
2125
|
+
};
|
|
2126
|
+
case "pox-postcondition":
|
|
2127
|
+
return {
|
|
2128
|
+
type: "pox",
|
|
2129
|
+
principal: resolvePrincipal(pc.address),
|
|
2130
|
+
conditionCode: POX_CODE_MAP[pc.condition]
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
function wireToPostCondition(wire) {
|
|
2135
|
+
switch (wire.type) {
|
|
2136
|
+
case "stx":
|
|
2137
|
+
return {
|
|
2138
|
+
type: "stx-postcondition",
|
|
2139
|
+
address: principalToString(wire.principal),
|
|
2140
|
+
condition: fungibleName(wire.conditionCode),
|
|
2141
|
+
amount: wire.amount.toString()
|
|
2142
|
+
};
|
|
2143
|
+
case "ft":
|
|
2144
|
+
return {
|
|
2145
|
+
type: "ft-postcondition",
|
|
2146
|
+
address: principalToString(wire.principal),
|
|
2147
|
+
condition: fungibleName(wire.conditionCode),
|
|
2148
|
+
amount: wire.amount.toString(),
|
|
2149
|
+
asset: assetToString(wire.asset)
|
|
2150
|
+
};
|
|
2151
|
+
case "nft":
|
|
2152
|
+
return {
|
|
2153
|
+
type: "nft-postcondition",
|
|
2154
|
+
address: principalToString(wire.principal),
|
|
2155
|
+
condition: nftName(wire.conditionCode),
|
|
2156
|
+
asset: assetToString(wire.asset),
|
|
2157
|
+
assetId: wire.assetId
|
|
2158
|
+
};
|
|
2159
|
+
case "staking":
|
|
2160
|
+
return {
|
|
2161
|
+
type: "staking-postcondition",
|
|
2162
|
+
address: principalToString(wire.principal),
|
|
2163
|
+
condition: fungibleName(wire.conditionCode),
|
|
2164
|
+
amount: wire.amount.toString()
|
|
2165
|
+
};
|
|
2166
|
+
case "pox":
|
|
2167
|
+
return {
|
|
2168
|
+
type: "pox-postcondition",
|
|
2169
|
+
address: principalToString(wire.principal),
|
|
2170
|
+
condition: poxName(wire.conditionCode)
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
function fungibleName(code) {
|
|
2175
|
+
const name = FUNGIBLE_CODE_REVERSE[code];
|
|
2176
|
+
if (!name)
|
|
2177
|
+
throw new Error(`Unknown fungible post-condition code: ${code}`);
|
|
2178
|
+
return name;
|
|
2179
|
+
}
|
|
2180
|
+
function nftName(code) {
|
|
2181
|
+
const name = NFT_CODE_REVERSE[code];
|
|
2182
|
+
if (!name)
|
|
2183
|
+
throw new Error(`Unknown nft post-condition code: ${code}`);
|
|
2184
|
+
return name;
|
|
2185
|
+
}
|
|
2186
|
+
function poxName(code) {
|
|
2187
|
+
const name = POX_CODE_REVERSE[code];
|
|
2188
|
+
if (!name)
|
|
2189
|
+
throw new Error(`Unknown pox post-condition code: ${code}`);
|
|
2190
|
+
return name;
|
|
2191
|
+
}
|
|
2192
|
+
function fromHex(hex) {
|
|
2193
|
+
return wireToPostCondition(deserializePostConditionWire(hex));
|
|
2194
|
+
}
|
|
2195
|
+
function postConditionToHex(pc) {
|
|
2196
|
+
return bytesToHex(serializePostConditionWire(convertPostCondition(pc)));
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
// src/transactions/authorization.ts
|
|
2200
|
+
var import_hmac = require("@noble/hashes/hmac.js");
|
|
2201
|
+
var import_sha24 = require("@noble/hashes/sha2.js");
|
|
2202
|
+
var import_secp256k1 = require("@noble/secp256k1");
|
|
2203
|
+
import_secp256k1.etc.hmacSha256Sync = (key, ...msgs) => {
|
|
2204
|
+
const h = import_hmac.hmac.create(import_sha24.sha256, key);
|
|
2205
|
+
for (const msg of msgs)
|
|
2206
|
+
h.update(msg);
|
|
2207
|
+
return h.digest();
|
|
2208
|
+
};
|
|
2209
|
+
var EMPTY_SIG = bytesToHex(new Uint8Array(RECOVERABLE_ECDSA_SIG_LENGTH_BYTES));
|
|
2210
|
+
function createSingleSigSpendingCondition(publicKey, nonce, fee) {
|
|
2211
|
+
const pubKeyBytes = hexToBytes(publicKey);
|
|
2212
|
+
const signer = hashP2PKH(pubKeyBytes);
|
|
2213
|
+
const isCompressed = pubKeyBytes.length === 33;
|
|
2214
|
+
return {
|
|
2215
|
+
hashMode: AddressHashMode.P2PKH,
|
|
2216
|
+
signer,
|
|
2217
|
+
nonce,
|
|
2218
|
+
fee,
|
|
2219
|
+
keyEncoding: isCompressed ? PubKeyEncoding.Compressed : PubKeyEncoding.Uncompressed,
|
|
2220
|
+
signature: EMPTY_SIG
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
function createStandardAuth(spendingCondition) {
|
|
2224
|
+
return { authType: AuthType.Standard, spendingCondition };
|
|
2225
|
+
}
|
|
2226
|
+
function createSponsoredAuth(spendingCondition, sponsorSpendingCondition) {
|
|
2227
|
+
return {
|
|
2228
|
+
authType: AuthType.Sponsored,
|
|
2229
|
+
spendingCondition,
|
|
2230
|
+
sponsorSpendingCondition: sponsorSpendingCondition ?? createSingleSigSpendingCondition("0".repeat(66), 0n, 0n)
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
function clearCondition(condition) {
|
|
2234
|
+
if ("signature" in condition) {
|
|
2235
|
+
return {
|
|
2236
|
+
...condition,
|
|
2237
|
+
nonce: 0n,
|
|
2238
|
+
fee: 0n,
|
|
2239
|
+
signature: EMPTY_SIG
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
return {
|
|
2243
|
+
...condition,
|
|
2244
|
+
nonce: 0n,
|
|
2245
|
+
fee: 0n,
|
|
2246
|
+
fields: []
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
function sponsorSigningSentinel() {
|
|
2250
|
+
return {
|
|
2251
|
+
hashMode: AddressHashMode.P2PKH,
|
|
2252
|
+
signer: "00".repeat(20),
|
|
2253
|
+
nonce: 0n,
|
|
2254
|
+
fee: 0n,
|
|
2255
|
+
keyEncoding: PubKeyEncoding.Compressed,
|
|
2256
|
+
signature: EMPTY_SIG
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
function intoInitialSighashAuth(auth) {
|
|
2260
|
+
if (auth.authType === AuthType.Standard) {
|
|
2261
|
+
return createStandardAuth(clearCondition(auth.spendingCondition));
|
|
2262
|
+
}
|
|
2263
|
+
return createSponsoredAuth(clearCondition(auth.spendingCondition), sponsorSigningSentinel());
|
|
2264
|
+
}
|
|
2265
|
+
function sigHashPreSign(curSigHash, authType, fee, nonce) {
|
|
2266
|
+
const data = concatBytes(hexToBytes(curSigHash), new Uint8Array([authType]), intToBytes(fee, 8), intToBytes(nonce, 8));
|
|
2267
|
+
return txidFromBytes(data);
|
|
2268
|
+
}
|
|
2269
|
+
function sigHashPostSign(curSigHash, pubKeyEncoding, signature) {
|
|
2270
|
+
const data = concatBytes(hexToBytes(curSigHash), new Uint8Array([pubKeyEncoding]), hexToBytes(signature));
|
|
2271
|
+
return txidFromBytes(data);
|
|
2272
|
+
}
|
|
2273
|
+
function signWithKey(privateKey, messageHash) {
|
|
2274
|
+
const keyBytes = hexToBytes(privateKey).slice(0, 32);
|
|
2275
|
+
const sig = import_secp256k1.sign(messageHash, keyBytes, { lowS: true });
|
|
2276
|
+
const recoveryIdHex = intToHex(sig.recovery, 1);
|
|
2277
|
+
return recoveryIdHex + sig.toCompactHex();
|
|
2278
|
+
}
|
|
2279
|
+
function nextSignature(curSigHash, authType, fee, nonce, privateKey) {
|
|
2280
|
+
const sigHashPre = sigHashPreSign(curSigHash, authType, fee, nonce);
|
|
2281
|
+
const keyBytes = hexToBytes(privateKey).slice(0, 32);
|
|
2282
|
+
const pubKey = import_secp256k1.getPublicKey(keyBytes, true);
|
|
2283
|
+
const isCompressed = pubKey.length === 33;
|
|
2284
|
+
const pubKeyEncoding = isCompressed ? PubKeyEncoding.Compressed : PubKeyEncoding.Uncompressed;
|
|
2285
|
+
const nextSig = signWithKey(privateKey, sigHashPre);
|
|
2286
|
+
const nextSigHash = sigHashPostSign(sigHashPre, pubKeyEncoding, nextSig);
|
|
2287
|
+
return { nextSig, nextSigHash };
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
// src/transactions/multisig.ts
|
|
2291
|
+
var import_secp256k12 = require("@noble/secp256k1");
|
|
2292
|
+
var OP_CHECKMULTISIG = 174;
|
|
2293
|
+
function isNonSequential(hashMode) {
|
|
2294
|
+
return (hashMode & 4) !== 0;
|
|
2295
|
+
}
|
|
2296
|
+
function makeRedeemScript(publicKeys, signaturesRequired) {
|
|
2297
|
+
const parts = [];
|
|
2298
|
+
parts.push(new Uint8Array([80 + signaturesRequired]));
|
|
2299
|
+
for (const pk of publicKeys) {
|
|
2300
|
+
const pkBytes = hexToBytes(pk);
|
|
2301
|
+
parts.push(new Uint8Array([pkBytes.length]));
|
|
2302
|
+
parts.push(pkBytes);
|
|
2303
|
+
}
|
|
2304
|
+
parts.push(new Uint8Array([80 + publicKeys.length]));
|
|
2305
|
+
parts.push(new Uint8Array([OP_CHECKMULTISIG]));
|
|
2306
|
+
return concatBytes(...parts);
|
|
2307
|
+
}
|
|
2308
|
+
function makeMultiSigAddress(publicKeys, signaturesRequired, chain) {
|
|
2309
|
+
if (signaturesRequired < 1 || signaturesRequired > publicKeys.length) {
|
|
2310
|
+
throw new Error(`signaturesRequired must be between 1 and ${publicKeys.length}`);
|
|
2311
|
+
}
|
|
2312
|
+
const redeemScript = makeRedeemScript(publicKeys, signaturesRequired);
|
|
2313
|
+
const signer = hash160(redeemScript);
|
|
2314
|
+
const version = chain?.addressVersion?.multiSig ?? 20;
|
|
2315
|
+
return c32address(version, bytesToHex(signer));
|
|
2316
|
+
}
|
|
2317
|
+
function createMultiSigSpendingCondition(publicKeys, signaturesRequired, nonce, fee, hashMode = AddressHashMode.P2SH) {
|
|
2318
|
+
const redeemScript = makeRedeemScript(publicKeys, signaturesRequired);
|
|
2319
|
+
const signer = bytesToHex(hash160(redeemScript));
|
|
2320
|
+
return {
|
|
2321
|
+
hashMode,
|
|
2322
|
+
signer,
|
|
2323
|
+
nonce,
|
|
2324
|
+
fee,
|
|
2325
|
+
fields: [],
|
|
2326
|
+
signaturesRequired
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
function initialSigHash(tx) {
|
|
2330
|
+
const cleared = {
|
|
2331
|
+
...tx,
|
|
2332
|
+
auth: intoInitialSighashAuth(tx.auth)
|
|
2333
|
+
};
|
|
2334
|
+
return txidFromBytes(serializeTransaction(cleared));
|
|
2335
|
+
}
|
|
2336
|
+
function nonSequentialPreSignHash(tx) {
|
|
2337
|
+
const condition = tx.auth.spendingCondition;
|
|
2338
|
+
const initHash = initialSigHash(tx);
|
|
2339
|
+
return sigHashPreSign(initHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2340
|
+
}
|
|
2341
|
+
function replayMultiSigSigHash(tx) {
|
|
2342
|
+
const condition = tx.auth.spendingCondition;
|
|
2343
|
+
if (isNonSequential(condition.hashMode)) {
|
|
2344
|
+
const preSign = nonSequentialPreSignHash(tx);
|
|
2345
|
+
let curSigHash2 = preSign;
|
|
2346
|
+
for (const field of condition.fields) {
|
|
2347
|
+
curSigHash2 = sigHashPostSign(curSigHash2, field.pubKeyEncoding, field.data);
|
|
2348
|
+
}
|
|
2349
|
+
return curSigHash2;
|
|
2350
|
+
}
|
|
2351
|
+
let curSigHash = initialSigHash(tx);
|
|
2352
|
+
for (const field of condition.fields) {
|
|
2353
|
+
const preSign = sigHashPreSign(curSigHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2354
|
+
curSigHash = sigHashPostSign(preSign, field.pubKeyEncoding, field.data);
|
|
2355
|
+
}
|
|
2356
|
+
return curSigHash;
|
|
2357
|
+
}
|
|
2358
|
+
function signMultiSig(tx, privateKey, publicKeys) {
|
|
2359
|
+
const keyBytes = hexToBytes(privateKey).slice(0, 32);
|
|
2360
|
+
const signerPubKey = bytesToHex(import_secp256k12.getPublicKey(keyBytes, true));
|
|
2361
|
+
const signerIndex = publicKeys.findIndex((pk) => pk === signerPubKey);
|
|
2362
|
+
if (signerIndex === -1) {
|
|
2363
|
+
throw new Error("Private key does not correspond to any signer in the multi-sig");
|
|
2364
|
+
}
|
|
2365
|
+
const condition = {
|
|
2366
|
+
...tx.auth.spendingCondition
|
|
2367
|
+
};
|
|
2368
|
+
if (isNonSequential(condition.hashMode)) {
|
|
2369
|
+
return signNonSequential(tx, condition, privateKey);
|
|
2370
|
+
}
|
|
2371
|
+
return signSequential(tx, condition, privateKey, publicKeys, signerIndex);
|
|
2372
|
+
}
|
|
2373
|
+
function signNonSequential(tx, condition, privateKey) {
|
|
2374
|
+
const fields = [...condition.fields];
|
|
2375
|
+
const preSignHash = nonSequentialPreSignHash(tx);
|
|
2376
|
+
const keyBytes = hexToBytes(privateKey).slice(0, 32);
|
|
2377
|
+
const sig = import_secp256k12.sign(preSignHash, keyBytes, { lowS: true });
|
|
2378
|
+
const recoveryIdHex = intToHex(sig.recovery, 1);
|
|
2379
|
+
const nextSig = recoveryIdHex + sig.toCompactHex();
|
|
2380
|
+
fields.push({
|
|
2381
|
+
type: "signature",
|
|
2382
|
+
pubKeyEncoding: PubKeyEncoding.Compressed,
|
|
2383
|
+
data: nextSig
|
|
2384
|
+
});
|
|
2385
|
+
const newCondition = { ...condition, fields };
|
|
2386
|
+
const sigCount = fields.filter((f) => f.type === "signature").length;
|
|
2387
|
+
if (sigCount >= condition.signaturesRequired) {
|
|
2388
|
+
return {
|
|
2389
|
+
...tx,
|
|
2390
|
+
auth: { ...tx.auth, spendingCondition: newCondition }
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
return {
|
|
2394
|
+
...tx,
|
|
2395
|
+
auth: { ...tx.auth, spendingCondition: newCondition }
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
function signSequential(tx, condition, privateKey, publicKeys, signerIndex) {
|
|
2399
|
+
const fields = [...condition.fields];
|
|
2400
|
+
let curSigHash = initialSigHash(tx);
|
|
2401
|
+
for (const field of fields) {
|
|
2402
|
+
const preSign = sigHashPreSign(curSigHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2403
|
+
curSigHash = sigHashPostSign(preSign, field.pubKeyEncoding, field.data);
|
|
2404
|
+
}
|
|
2405
|
+
for (let i = fields.length;i < signerIndex; i++) {
|
|
2406
|
+
const pubKeyField = {
|
|
2407
|
+
type: "publicKey",
|
|
2408
|
+
pubKeyEncoding: PubKeyEncoding.Compressed,
|
|
2409
|
+
data: publicKeys[i]
|
|
2410
|
+
};
|
|
2411
|
+
fields.push(pubKeyField);
|
|
2412
|
+
const preSign = sigHashPreSign(curSigHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2413
|
+
curSigHash = sigHashPostSign(preSign, pubKeyField.pubKeyEncoding, pubKeyField.data);
|
|
2414
|
+
}
|
|
2415
|
+
const { nextSig, nextSigHash } = nextSignature(curSigHash, AuthType.Standard, condition.fee, condition.nonce, privateKey);
|
|
2416
|
+
fields.push({
|
|
2417
|
+
type: "signature",
|
|
2418
|
+
pubKeyEncoding: PubKeyEncoding.Compressed,
|
|
2419
|
+
data: nextSig
|
|
2420
|
+
});
|
|
2421
|
+
curSigHash = nextSigHash;
|
|
2422
|
+
const newCondition = { ...condition, fields };
|
|
2423
|
+
const sigCount = fields.filter((f) => f.type === "signature").length;
|
|
2424
|
+
let finalCondition = newCondition;
|
|
2425
|
+
if (sigCount >= condition.signaturesRequired) {
|
|
2426
|
+
finalCondition = finalizeMultiSigCondition(newCondition, publicKeys, curSigHash);
|
|
2427
|
+
}
|
|
2428
|
+
return {
|
|
2429
|
+
...tx,
|
|
2430
|
+
auth: {
|
|
2431
|
+
...tx.auth,
|
|
2432
|
+
spendingCondition: finalCondition
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2435
|
+
}
|
|
2436
|
+
async function signMultiSigWithAccount(tx, account, publicKeys) {
|
|
2437
|
+
const signerPubKey = account.publicKey;
|
|
2438
|
+
const signerIndex = publicKeys.findIndex((pk) => pk === signerPubKey);
|
|
2439
|
+
if (signerIndex === -1) {
|
|
2440
|
+
throw new Error("Account does not correspond to any signer in the multi-sig");
|
|
2441
|
+
}
|
|
2442
|
+
const condition = {
|
|
2443
|
+
...tx.auth.spendingCondition
|
|
2444
|
+
};
|
|
2445
|
+
if (isNonSequential(condition.hashMode)) {
|
|
2446
|
+
return signNonSequentialWithAccount(tx, condition, account);
|
|
2447
|
+
}
|
|
2448
|
+
return signSequentialWithAccount(tx, condition, account, publicKeys, signerIndex);
|
|
2449
|
+
}
|
|
2450
|
+
async function signNonSequentialWithAccount(tx, condition, account) {
|
|
2451
|
+
const fields = [...condition.fields];
|
|
2452
|
+
const preSignHash = nonSequentialPreSignHash(tx);
|
|
2453
|
+
const sigBytes = await account.sign(hexToBytes(preSignHash));
|
|
2454
|
+
const nextSig = bytesToHex(sigBytes);
|
|
2455
|
+
fields.push({
|
|
2456
|
+
type: "signature",
|
|
2457
|
+
pubKeyEncoding: PubKeyEncoding.Compressed,
|
|
2458
|
+
data: nextSig
|
|
2459
|
+
});
|
|
2460
|
+
const newCondition = { ...condition, fields };
|
|
2461
|
+
return {
|
|
2462
|
+
...tx,
|
|
2463
|
+
auth: { ...tx.auth, spendingCondition: newCondition }
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
async function signSequentialWithAccount(tx, condition, account, publicKeys, signerIndex) {
|
|
2467
|
+
const fields = [...condition.fields];
|
|
2468
|
+
let curSigHash = initialSigHash(tx);
|
|
2469
|
+
for (const field of fields) {
|
|
2470
|
+
const preSign2 = sigHashPreSign(curSigHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2471
|
+
curSigHash = sigHashPostSign(preSign2, field.pubKeyEncoding, field.data);
|
|
2472
|
+
}
|
|
2473
|
+
for (let i = fields.length;i < signerIndex; i++) {
|
|
2474
|
+
const pubKeyField = {
|
|
2475
|
+
type: "publicKey",
|
|
2476
|
+
pubKeyEncoding: PubKeyEncoding.Compressed,
|
|
2477
|
+
data: publicKeys[i]
|
|
2478
|
+
};
|
|
2479
|
+
fields.push(pubKeyField);
|
|
2480
|
+
const preSign2 = sigHashPreSign(curSigHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2481
|
+
curSigHash = sigHashPostSign(preSign2, pubKeyField.pubKeyEncoding, pubKeyField.data);
|
|
2482
|
+
}
|
|
2483
|
+
const preSign = sigHashPreSign(curSigHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2484
|
+
const sigBytes = await account.sign(hexToBytes(preSign));
|
|
2485
|
+
const nextSig = bytesToHex(sigBytes);
|
|
2486
|
+
const nextSigHash = sigHashPostSign(preSign, PubKeyEncoding.Compressed, nextSig);
|
|
2487
|
+
fields.push({
|
|
2488
|
+
type: "signature",
|
|
2489
|
+
pubKeyEncoding: PubKeyEncoding.Compressed,
|
|
2490
|
+
data: nextSig
|
|
2491
|
+
});
|
|
2492
|
+
curSigHash = nextSigHash;
|
|
2493
|
+
const newCondition = { ...condition, fields };
|
|
2494
|
+
const sigCount = fields.filter((f) => f.type === "signature").length;
|
|
2495
|
+
let finalCondition = newCondition;
|
|
2496
|
+
if (sigCount >= condition.signaturesRequired) {
|
|
2497
|
+
finalCondition = finalizeMultiSigCondition(newCondition, publicKeys, curSigHash);
|
|
2498
|
+
}
|
|
2499
|
+
return {
|
|
2500
|
+
...tx,
|
|
2501
|
+
auth: {
|
|
2502
|
+
...tx.auth,
|
|
2503
|
+
spendingCondition: finalCondition
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
function finalizeMultiSig(tx, publicKeys) {
|
|
2508
|
+
const condition = tx.auth.spendingCondition;
|
|
2509
|
+
if (isNonSequential(condition.hashMode)) {
|
|
2510
|
+
return tx;
|
|
2511
|
+
}
|
|
2512
|
+
const curSigHash = replayMultiSigSigHash(tx);
|
|
2513
|
+
const finalCondition = finalizeMultiSigCondition(condition, publicKeys, curSigHash);
|
|
2514
|
+
return {
|
|
2515
|
+
...tx,
|
|
2516
|
+
auth: {
|
|
2517
|
+
...tx.auth,
|
|
2518
|
+
spendingCondition: finalCondition
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
function finalizeMultiSigCondition(condition, publicKeys, curSigHash) {
|
|
2523
|
+
const fields = [...condition.fields];
|
|
2524
|
+
let sigHash = curSigHash;
|
|
2525
|
+
for (let i = fields.length;i < publicKeys.length; i++) {
|
|
2526
|
+
const pubKeyField = {
|
|
2527
|
+
type: "publicKey",
|
|
2528
|
+
pubKeyEncoding: PubKeyEncoding.Compressed,
|
|
2529
|
+
data: publicKeys[i]
|
|
2530
|
+
};
|
|
2531
|
+
fields.push(pubKeyField);
|
|
2532
|
+
const preSign = sigHashPreSign(sigHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2533
|
+
sigHash = sigHashPostSign(preSign, pubKeyField.pubKeyEncoding, pubKeyField.data);
|
|
2534
|
+
}
|
|
2535
|
+
return { ...condition, fields };
|
|
2536
|
+
}
|
|
2537
|
+
function combineMultiSigSignatures(baseTx, signedTxs) {
|
|
2538
|
+
const condition = baseTx.auth.spendingCondition;
|
|
2539
|
+
if (!isNonSequential(condition.hashMode)) {
|
|
2540
|
+
throw new Error("combineMultiSigSignatures only works with non-sequential hash modes (SIP-027)");
|
|
2541
|
+
}
|
|
2542
|
+
const seenSigs = new Set;
|
|
2543
|
+
const signatures = [];
|
|
2544
|
+
for (const stx of signedTxs) {
|
|
2545
|
+
const stxCondition = stx.auth.spendingCondition;
|
|
2546
|
+
for (const field of stxCondition.fields) {
|
|
2547
|
+
if (field.type === "signature" && !seenSigs.has(field.data)) {
|
|
2548
|
+
seenSigs.add(field.data);
|
|
2549
|
+
signatures.push(field);
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
if (signatures.length === 0) {
|
|
2554
|
+
throw new Error("No signatures found in the provided transactions");
|
|
2555
|
+
}
|
|
2556
|
+
const newCondition = {
|
|
2557
|
+
...condition,
|
|
2558
|
+
fields: signatures
|
|
2559
|
+
};
|
|
2560
|
+
return {
|
|
2561
|
+
...baseTx,
|
|
2562
|
+
auth: { ...baseTx.auth, spendingCondition: newCondition }
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// src/transactions/build.ts
|
|
2567
|
+
function resolvePcMode(mode) {
|
|
2568
|
+
if (mode === "allow")
|
|
2569
|
+
return PostConditionModeWire.Allow;
|
|
2570
|
+
if (mode === "originator")
|
|
2571
|
+
return PostConditionModeWire.Originator;
|
|
2572
|
+
return PostConditionModeWire.Deny;
|
|
2573
|
+
}
|
|
2574
|
+
function convertPostConditions(pcs) {
|
|
2575
|
+
if (!pcs || pcs.length === 0)
|
|
2576
|
+
return [];
|
|
2577
|
+
return pcs.map((pc) => convertPostCondition(typeof pc === "string" ? fromHex(pc) : pc));
|
|
2578
|
+
}
|
|
2579
|
+
function resolveSpendingCondition(opts, nonce, fee) {
|
|
2580
|
+
if (opts.publicKeys) {
|
|
2581
|
+
return createMultiSigSpendingCondition(opts.publicKeys, opts.signaturesRequired, nonce, fee, opts.hashMode);
|
|
2582
|
+
}
|
|
2583
|
+
return createSingleSigSpendingCondition(opts.publicKey, nonce, fee);
|
|
2584
|
+
}
|
|
2585
|
+
function resolveAuth(spendingCondition, sponsored) {
|
|
2586
|
+
return sponsored ? createSponsoredAuth(spendingCondition) : createStandardAuth(spendingCondition);
|
|
2587
|
+
}
|
|
2588
|
+
function resolveVersionAndChainId(chain) {
|
|
2589
|
+
if (!chain)
|
|
2590
|
+
return { version: 0, chainId: 1 };
|
|
2591
|
+
return { version: chain.transactionVersion, chainId: chain.id };
|
|
2592
|
+
}
|
|
2593
|
+
function buildTokenTransfer(options) {
|
|
2594
|
+
const { version, chainId } = resolveVersionAndChainId(options.chain);
|
|
2595
|
+
const fee = intToBigInt(options.fee);
|
|
2596
|
+
const nonce = intToBigInt(options.nonce);
|
|
2597
|
+
const spendingCondition = resolveSpendingCondition(options, nonce, fee);
|
|
2598
|
+
const recipient = Cl.principal(options.recipient);
|
|
2599
|
+
const tx = {
|
|
2600
|
+
version,
|
|
2601
|
+
chainId,
|
|
2602
|
+
auth: resolveAuth(spendingCondition, options.sponsored),
|
|
2603
|
+
anchorMode: AnchorMode.Any,
|
|
2604
|
+
postConditionMode: resolvePcMode(options.postConditionMode),
|
|
2605
|
+
postConditions: convertPostConditions(options.postConditions),
|
|
2606
|
+
payload: {
|
|
2607
|
+
payloadType: PayloadType.TokenTransfer,
|
|
2608
|
+
recipient,
|
|
2609
|
+
amount: intToBigInt(options.amount),
|
|
2610
|
+
memo: options.memo ?? ""
|
|
2611
|
+
}
|
|
2612
|
+
};
|
|
2613
|
+
if (options.publicKeys) {
|
|
2614
|
+
tx._multisig = { publicKeys: options.publicKeys };
|
|
2615
|
+
}
|
|
2616
|
+
return tx;
|
|
2617
|
+
}
|
|
2618
|
+
function buildContractCall(options) {
|
|
2619
|
+
const { version, chainId } = resolveVersionAndChainId(options.chain);
|
|
2620
|
+
const fee = intToBigInt(options.fee);
|
|
2621
|
+
const nonce = intToBigInt(options.nonce);
|
|
2622
|
+
const spendingCondition = resolveSpendingCondition(options, nonce, fee);
|
|
2623
|
+
const tx = {
|
|
2624
|
+
version,
|
|
2625
|
+
chainId,
|
|
2626
|
+
auth: resolveAuth(spendingCondition, options.sponsored),
|
|
2627
|
+
anchorMode: AnchorMode.Any,
|
|
2628
|
+
postConditionMode: resolvePcMode(options.postConditionMode),
|
|
2629
|
+
postConditions: convertPostConditions(options.postConditions),
|
|
2630
|
+
payload: {
|
|
2631
|
+
payloadType: PayloadType.ContractCall,
|
|
2632
|
+
contractAddress: options.contractAddress,
|
|
2633
|
+
contractName: options.contractName,
|
|
2634
|
+
functionName: options.functionName,
|
|
2635
|
+
functionArgs: options.functionArgs
|
|
2636
|
+
}
|
|
2637
|
+
};
|
|
2638
|
+
if (options.publicKeys) {
|
|
2639
|
+
tx._multisig = { publicKeys: options.publicKeys };
|
|
2640
|
+
}
|
|
2641
|
+
return tx;
|
|
2642
|
+
}
|
|
2643
|
+
function buildContractDeploy(options) {
|
|
2644
|
+
const { version, chainId } = resolveVersionAndChainId(options.chain);
|
|
2645
|
+
const fee = intToBigInt(options.fee);
|
|
2646
|
+
const nonce = intToBigInt(options.nonce);
|
|
2647
|
+
const spendingCondition = resolveSpendingCondition(options, nonce, fee);
|
|
2648
|
+
const clarityVersion = options.clarityVersion ?? ClarityVersion.Clarity4;
|
|
2649
|
+
const tx = {
|
|
2650
|
+
version,
|
|
2651
|
+
chainId,
|
|
2652
|
+
auth: resolveAuth(spendingCondition, options.sponsored),
|
|
2653
|
+
anchorMode: AnchorMode.Any,
|
|
2654
|
+
postConditionMode: resolvePcMode(options.postConditionMode),
|
|
2655
|
+
postConditions: convertPostConditions(options.postConditions),
|
|
2656
|
+
payload: {
|
|
2657
|
+
payloadType: PayloadType.VersionedSmartContract,
|
|
2658
|
+
clarityVersion,
|
|
2659
|
+
contractName: options.contractName,
|
|
2660
|
+
codeBody: options.codeBody
|
|
2661
|
+
}
|
|
2662
|
+
};
|
|
2663
|
+
if (options.publicKeys) {
|
|
2664
|
+
tx._multisig = { publicKeys: options.publicKeys };
|
|
2665
|
+
}
|
|
2666
|
+
return tx;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
// src/actions/public/getNonce.ts
|
|
2670
|
+
async function getNonce(client, params) {
|
|
2671
|
+
const data = await client.request(`/v2/accounts/${encodeURIComponent(params.address)}`, { method: "GET" });
|
|
2672
|
+
const nonce = data?.nonce;
|
|
2673
|
+
if (typeof nonce !== "number" && typeof nonce !== "string") {
|
|
2674
|
+
throw new MalformedResponseError(`getNonce: /v2/accounts/${params.address} response is missing "nonce"`);
|
|
2675
|
+
}
|
|
2676
|
+
return BigInt(nonce);
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
// src/actions/public/readContract.ts
|
|
2680
|
+
async function readContract(client, params) {
|
|
2681
|
+
const [address, name] = parseContractId(params.contract);
|
|
2682
|
+
const sender = params.sender ?? address;
|
|
2683
|
+
const serializedArgs = (params.args ?? []).map((arg) => with0x(bytesToHex(serializeCVBytes(arg))));
|
|
2684
|
+
const path = `/v2/contracts/call-read/${encodeURIComponent(address)}/${encodeURIComponent(name)}/${encodeURIComponent(params.functionName)}`;
|
|
2685
|
+
const data = await client.request(params.tip ? `${path}?tip=${encodeURIComponent(params.tip)}` : path, {
|
|
2686
|
+
method: "POST",
|
|
2687
|
+
body: {
|
|
2688
|
+
sender,
|
|
2689
|
+
arguments: serializedArgs
|
|
2690
|
+
}
|
|
2691
|
+
});
|
|
2692
|
+
if (data.okay) {
|
|
2693
|
+
if (typeof data.result !== "string") {
|
|
2694
|
+
throw new MalformedResponseError(`readContract: /v2/contracts/call-read response is missing "result"`);
|
|
2695
|
+
}
|
|
2696
|
+
return deserializeCVBytes(data.result);
|
|
2697
|
+
}
|
|
2698
|
+
throw new ReadContractError(data.cause ?? "Read-only call failed");
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
// src/transactions/signer.ts
|
|
2702
|
+
function txid(tx) {
|
|
2703
|
+
return txidFromBytes(serializeTransaction(tx));
|
|
2704
|
+
}
|
|
2705
|
+
function signBegin(tx) {
|
|
2706
|
+
const cleared = {
|
|
2707
|
+
...tx,
|
|
2708
|
+
auth: intoInitialSighashAuth(tx.auth)
|
|
2709
|
+
};
|
|
2710
|
+
return txid(cleared);
|
|
2711
|
+
}
|
|
2712
|
+
function signTransaction(tx, privateKey) {
|
|
2713
|
+
const sigHash = signBegin(tx);
|
|
2714
|
+
const condition = tx.auth.spendingCondition;
|
|
2715
|
+
const { nextSig } = nextSignature(sigHash, AuthType.Standard, condition.fee, condition.nonce, privateKey);
|
|
2716
|
+
return {
|
|
2717
|
+
...tx,
|
|
2718
|
+
auth: {
|
|
2719
|
+
...tx.auth,
|
|
2720
|
+
spendingCondition: {
|
|
2721
|
+
...condition,
|
|
2722
|
+
signature: nextSig
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
async function signTransactionWithAccount(tx, account) {
|
|
2728
|
+
const sigHash = signBegin(tx);
|
|
2729
|
+
const condition = tx.auth.spendingCondition;
|
|
2730
|
+
const sigHashPre = sigHashPreSign(sigHash, AuthType.Standard, condition.fee, condition.nonce);
|
|
2731
|
+
const sigBytes = await account.sign(hexToBytes(sigHashPre));
|
|
2732
|
+
const nextSig = bytesToHex(sigBytes);
|
|
2733
|
+
return {
|
|
2734
|
+
...tx,
|
|
2735
|
+
auth: {
|
|
2736
|
+
...tx.auth,
|
|
2737
|
+
spendingCondition: {
|
|
2738
|
+
...condition,
|
|
2739
|
+
signature: nextSig
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
function getTransactionId(tx) {
|
|
2745
|
+
return txid(tx);
|
|
2746
|
+
}
|
|
2747
|
+
function getOriginSigHash(tx) {
|
|
2748
|
+
const condition = tx.auth.spendingCondition;
|
|
2749
|
+
if ("fields" in condition) {
|
|
2750
|
+
return replayMultiSigSigHash(tx);
|
|
2751
|
+
}
|
|
2752
|
+
const initialSigHash2 = signBegin(tx);
|
|
2753
|
+
const sc = condition;
|
|
2754
|
+
const preSign = sigHashPreSign(initialSigHash2, AuthType.Standard, sc.fee, sc.nonce);
|
|
2755
|
+
return sigHashPostSign(preSign, sc.keyEncoding, sc.signature);
|
|
2756
|
+
}
|
|
2757
|
+
function signSponsor(tx, privateKey) {
|
|
2758
|
+
if (tx.auth.authType !== AuthType.Sponsored) {
|
|
2759
|
+
throw new Error("Transaction must be sponsored");
|
|
2760
|
+
}
|
|
2761
|
+
const auth = tx.auth;
|
|
2762
|
+
const sponsorCondition = auth.sponsorSpendingCondition;
|
|
2763
|
+
const originSigHash = getOriginSigHash(tx);
|
|
2764
|
+
const { nextSig } = nextSignature(originSigHash, AuthType.Sponsored, sponsorCondition.fee, sponsorCondition.nonce, privateKey);
|
|
2765
|
+
return {
|
|
2766
|
+
...tx,
|
|
2767
|
+
auth: {
|
|
2768
|
+
...auth,
|
|
2769
|
+
sponsorSpendingCondition: {
|
|
2770
|
+
...sponsorCondition,
|
|
2771
|
+
signature: nextSig
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
};
|
|
2775
|
+
}
|
|
2776
|
+
async function signSponsorWithAccount(tx, account) {
|
|
2777
|
+
if (tx.auth.authType !== AuthType.Sponsored) {
|
|
2778
|
+
throw new Error("Transaction must be sponsored");
|
|
2779
|
+
}
|
|
2780
|
+
const auth = tx.auth;
|
|
2781
|
+
const sponsorCondition = auth.sponsorSpendingCondition;
|
|
2782
|
+
const originSigHash = getOriginSigHash(tx);
|
|
2783
|
+
const sigHashPre = sigHashPreSign(originSigHash, AuthType.Sponsored, sponsorCondition.fee, sponsorCondition.nonce);
|
|
2784
|
+
const sigBytes = await account.sign(hexToBytes(sigHashPre));
|
|
2785
|
+
const nextSig = bytesToHex(sigBytes);
|
|
2786
|
+
return {
|
|
2787
|
+
...tx,
|
|
2788
|
+
auth: {
|
|
2789
|
+
...auth,
|
|
2790
|
+
sponsorSpendingCondition: {
|
|
2791
|
+
...sponsorCondition,
|
|
2792
|
+
signature: nextSig
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
};
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
// src/errors/http.ts
|
|
2799
|
+
class HttpRequestError extends BaseError {
|
|
2800
|
+
name = "HttpRequestError";
|
|
2801
|
+
status;
|
|
2802
|
+
url;
|
|
2803
|
+
method;
|
|
2804
|
+
constructor(status, options) {
|
|
2805
|
+
const where = options?.url !== undefined ? ` (${options.method ?? "GET"} ${options.url})` : "";
|
|
2806
|
+
super(`HTTP request failed with status ${status}${where}`, options);
|
|
2807
|
+
this.status = status;
|
|
2808
|
+
this.url = options?.url;
|
|
2809
|
+
this.method = options?.method;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
// src/errors/transport.ts
|
|
2814
|
+
class TimeoutError extends BaseError {
|
|
2815
|
+
name = "TimeoutError";
|
|
2816
|
+
method;
|
|
2817
|
+
url;
|
|
2818
|
+
timeout;
|
|
2819
|
+
attempt;
|
|
2820
|
+
constructor(params) {
|
|
2821
|
+
super(`${params.method} ${params.url} did not complete within ${params.timeout}ms (attempt ${params.attempt + 1})`);
|
|
2822
|
+
this.method = params.method;
|
|
2823
|
+
this.url = params.url;
|
|
2824
|
+
this.timeout = params.timeout;
|
|
2825
|
+
this.attempt = params.attempt;
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
// src/transports/createTransport.ts
|
|
2830
|
+
function createTransport(type, config) {
|
|
2831
|
+
const { request, apiKey: _apiKey, ...exposed } = config;
|
|
2832
|
+
return {
|
|
2833
|
+
type,
|
|
2834
|
+
request,
|
|
2835
|
+
config: exposed
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
var MAX_ERROR_DETAILS_BYTES = 4096;
|
|
2839
|
+
function capDetails(body) {
|
|
2840
|
+
if (body === undefined)
|
|
2841
|
+
return;
|
|
2842
|
+
const bytes = new TextEncoder().encode(body);
|
|
2843
|
+
if (bytes.length <= MAX_ERROR_DETAILS_BYTES)
|
|
2844
|
+
return body;
|
|
2845
|
+
const head = new TextDecoder().decode(bytes.subarray(0, MAX_ERROR_DETAILS_BYTES));
|
|
2846
|
+
return `${head}... [truncated ${bytes.length - MAX_ERROR_DETAILS_BYTES} bytes]`;
|
|
2847
|
+
}
|
|
2848
|
+
function isRetryableStatus(status) {
|
|
2849
|
+
return status === 429 || status >= 500;
|
|
2850
|
+
}
|
|
2851
|
+
var MAX_RETRY_AFTER_MS = 60000;
|
|
2852
|
+
function retryAfterMs(response) {
|
|
2853
|
+
const value = response.headers.get("Retry-After");
|
|
2854
|
+
if (!value)
|
|
2855
|
+
return;
|
|
2856
|
+
const seconds = Number(value);
|
|
2857
|
+
const ms = Number.isFinite(seconds) ? seconds * 1000 : Date.parse(value) - Date.now();
|
|
2858
|
+
if (!Number.isFinite(ms) || ms < 0 || ms > MAX_RETRY_AFTER_MS)
|
|
2859
|
+
return;
|
|
2860
|
+
return ms;
|
|
2861
|
+
}
|
|
2862
|
+
function abortReason(signal) {
|
|
2863
|
+
const reason = signal.reason;
|
|
2864
|
+
if (reason instanceof Error)
|
|
2865
|
+
return reason;
|
|
2866
|
+
return new DOMException(typeof reason === "string" ? reason : "The operation was aborted", "AbortError");
|
|
2867
|
+
}
|
|
2868
|
+
function sleep(ms, signal) {
|
|
2869
|
+
return new Promise((resolve, reject) => {
|
|
2870
|
+
if (signal?.aborted)
|
|
2871
|
+
return reject(abortReason(signal));
|
|
2872
|
+
const onAbort = () => {
|
|
2873
|
+
clearTimeout(id);
|
|
2874
|
+
reject(abortReason(signal));
|
|
2875
|
+
};
|
|
2876
|
+
const id = setTimeout(() => {
|
|
2877
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2878
|
+
resolve();
|
|
2879
|
+
}, ms);
|
|
2880
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
async function fetchWithRetry(url, options, retryCount, retryDelay, timeout, extra = {}) {
|
|
2884
|
+
const { signal } = extra;
|
|
2885
|
+
const read = extra.read ?? (async (response) => response);
|
|
2886
|
+
const method = options.method ?? "GET";
|
|
2887
|
+
let lastError;
|
|
2888
|
+
for (let attempt = 0;attempt <= retryCount; attempt++) {
|
|
2889
|
+
if (signal?.aborted)
|
|
2890
|
+
throw abortReason(signal);
|
|
2891
|
+
const controller = new AbortController;
|
|
2892
|
+
let timedOut = false;
|
|
2893
|
+
const timeoutId = setTimeout(() => {
|
|
2894
|
+
timedOut = true;
|
|
2895
|
+
controller.abort(new TimeoutError({ method, url, timeout, attempt }));
|
|
2896
|
+
}, timeout);
|
|
2897
|
+
const onCallerAbort = () => controller.abort(abortReason(signal));
|
|
2898
|
+
signal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
2899
|
+
const aborted = new Promise((_, reject) => {
|
|
2900
|
+
if (controller.signal.aborted)
|
|
2901
|
+
return reject(controller.signal.reason);
|
|
2902
|
+
controller.signal.addEventListener("abort", () => reject(controller.signal.reason), { once: true });
|
|
2903
|
+
});
|
|
2904
|
+
let response;
|
|
2905
|
+
try {
|
|
2906
|
+
try {
|
|
2907
|
+
response = await Promise.race([
|
|
2908
|
+
fetch(url, { ...options, signal: controller.signal }),
|
|
2909
|
+
aborted
|
|
2910
|
+
]);
|
|
2911
|
+
} catch (error) {
|
|
2912
|
+
if (signal?.aborted)
|
|
2913
|
+
throw abortReason(signal);
|
|
2914
|
+
lastError = timedOut ? new TimeoutError({ method, url, timeout, attempt }) : error instanceof Error ? error : new Error(String(error));
|
|
2915
|
+
if (attempt < retryCount) {
|
|
2916
|
+
clearTimeout(timeoutId);
|
|
2917
|
+
await sleep(retryDelay * (attempt + 1), signal);
|
|
2918
|
+
continue;
|
|
2919
|
+
}
|
|
2920
|
+
throw lastError;
|
|
2921
|
+
}
|
|
2922
|
+
if (response.ok) {
|
|
2923
|
+
try {
|
|
2924
|
+
return await Promise.race([read(response), aborted]);
|
|
2925
|
+
} catch (error) {
|
|
2926
|
+
response.body?.cancel().catch(() => {});
|
|
2927
|
+
if (signal?.aborted)
|
|
2928
|
+
throw abortReason(signal);
|
|
2929
|
+
if (!timedOut)
|
|
2930
|
+
throw error;
|
|
2931
|
+
lastError = new TimeoutError({ method, url, timeout, attempt });
|
|
2932
|
+
if (attempt < retryCount) {
|
|
2933
|
+
clearTimeout(timeoutId);
|
|
2934
|
+
await sleep(retryDelay * (attempt + 1), signal);
|
|
2935
|
+
continue;
|
|
2936
|
+
}
|
|
2937
|
+
throw lastError;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
const retryable = isRetryableStatus(response.status);
|
|
2941
|
+
if (!retryable || attempt === retryCount) {
|
|
2942
|
+
const body = await Promise.race([
|
|
2943
|
+
response.text().catch(() => {
|
|
2944
|
+
return;
|
|
2945
|
+
}),
|
|
2946
|
+
aborted.catch(() => {
|
|
2947
|
+
return;
|
|
2948
|
+
})
|
|
2949
|
+
]);
|
|
2950
|
+
throw new HttpRequestError(response.status, {
|
|
2951
|
+
details: capDetails(body),
|
|
2952
|
+
url,
|
|
2953
|
+
method
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
lastError = new HttpRequestError(response.status, { url, method });
|
|
2957
|
+
const delay = retryAfterMs(response) ?? retryDelay * (attempt + 1);
|
|
2958
|
+
response.body?.cancel().catch(() => {});
|
|
2959
|
+
clearTimeout(timeoutId);
|
|
2960
|
+
await sleep(delay, signal);
|
|
2961
|
+
} finally {
|
|
2962
|
+
clearTimeout(timeoutId);
|
|
2963
|
+
signal?.removeEventListener("abort", onCallerAbort);
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
throw lastError ?? new Error("Request failed");
|
|
2967
|
+
}
|
|
2968
|
+
async function readBody(response) {
|
|
2969
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
2970
|
+
if (contentType.includes("application/json")) {
|
|
2971
|
+
return response.json();
|
|
2972
|
+
}
|
|
2973
|
+
return response.text();
|
|
2974
|
+
}
|
|
2975
|
+
function buildRequestFn(baseUrl, config) {
|
|
2976
|
+
const {
|
|
2977
|
+
timeout = 30000,
|
|
2978
|
+
retryCount = 3,
|
|
2979
|
+
retryDelay = 150,
|
|
2980
|
+
fetchOptions = {},
|
|
2981
|
+
apiKey
|
|
2982
|
+
} = config;
|
|
2983
|
+
return async (path, options) => {
|
|
2984
|
+
const url = `${baseUrl.replace(/\/$/, "")}${path}`;
|
|
2985
|
+
const headers = {
|
|
2986
|
+
"Content-Type": "application/json",
|
|
2987
|
+
...fetchOptions.headers,
|
|
2988
|
+
...options?.headers
|
|
2989
|
+
};
|
|
2990
|
+
if (apiKey) {
|
|
2991
|
+
headers["x-api-key"] = apiKey;
|
|
2992
|
+
}
|
|
2993
|
+
const init = {
|
|
2994
|
+
...fetchOptions,
|
|
2995
|
+
method: options?.method ?? "GET",
|
|
2996
|
+
headers
|
|
2997
|
+
};
|
|
2998
|
+
if (options?.body !== undefined) {
|
|
2999
|
+
init.body = JSON.stringify(options.body);
|
|
3000
|
+
}
|
|
3001
|
+
return fetchWithRetry(url, init, options?.retryCount ?? retryCount, retryDelay, timeout, { signal: options?.signal, read: readBody });
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
|
|
3005
|
+
// src/actions/public/txSources.ts
|
|
3006
|
+
class IndexSourceConfigError extends Error {
|
|
3007
|
+
name = "IndexSourceConfigError";
|
|
3008
|
+
}
|
|
3009
|
+
function isHiroHost(url, client) {
|
|
3010
|
+
let hostname;
|
|
3011
|
+
try {
|
|
3012
|
+
hostname = new URL(url).hostname;
|
|
3013
|
+
} catch {
|
|
3014
|
+
return false;
|
|
3015
|
+
}
|
|
3016
|
+
if (hostname === "hiro.so" || hostname.endsWith(".hiro.so"))
|
|
3017
|
+
return true;
|
|
3018
|
+
const defaults = client.chain?.rpcUrls?.default?.http ?? [];
|
|
3019
|
+
return defaults.some((d) => {
|
|
3020
|
+
try {
|
|
3021
|
+
return new URL(d).hostname === hostname && hostname !== "localhost";
|
|
3022
|
+
} catch {
|
|
3023
|
+
return false;
|
|
3024
|
+
}
|
|
3025
|
+
});
|
|
3026
|
+
}
|
|
3027
|
+
function indexRequestFn(client, baseUrl, sourceName) {
|
|
3028
|
+
const transportUrl = client.transport.config.url?.replace(/\/$/, "");
|
|
3029
|
+
if (baseUrl === undefined) {
|
|
3030
|
+
if (!transportUrl) {
|
|
3031
|
+
throw new IndexSourceConfigError(`${sourceName} needs the URL of your Secondlayer instance: pass baseUrl, or use an http() transport pointed at the instance`);
|
|
3032
|
+
}
|
|
3033
|
+
if (isHiroHost(transportUrl, client)) {
|
|
3034
|
+
throw new IndexSourceConfigError(`${sourceName} reads /v1/index on a Secondlayer instance, and ${transportUrl} is a Hiro host: pass baseUrl with the URL of your instance`);
|
|
3035
|
+
}
|
|
3036
|
+
return client.request;
|
|
3037
|
+
}
|
|
3038
|
+
const target = baseUrl.replace(/\/$/, "");
|
|
3039
|
+
if (target === transportUrl)
|
|
3040
|
+
return client.request;
|
|
3041
|
+
const { timeout, retryCount, retryDelay } = client.transport.config;
|
|
3042
|
+
return buildRequestFn(target, { timeout, retryCount, retryDelay });
|
|
3043
|
+
}
|
|
3044
|
+
function indexNotFound(error, sourceName) {
|
|
3045
|
+
if (!(error instanceof HttpRequestError) || error.status !== 404) {
|
|
3046
|
+
return false;
|
|
3047
|
+
}
|
|
3048
|
+
const body = error.details;
|
|
3049
|
+
if (body !== undefined) {
|
|
3050
|
+
try {
|
|
3051
|
+
JSON.parse(body);
|
|
3052
|
+
} catch {
|
|
3053
|
+
throw new IndexSourceConfigError(`${sourceName} got a non-JSON 404 from ${error.url ?? "the transport host"}: it does not serve /v1/index, pass baseUrl with the URL of your Secondlayer instance`, { cause: error });
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
return true;
|
|
3057
|
+
}
|
|
3058
|
+
function normalizeTxid(txid2) {
|
|
3059
|
+
return txid2.startsWith("0x") ? txid2 : `0x${txid2}`;
|
|
3060
|
+
}
|
|
3061
|
+
function decodeResultHex(hex) {
|
|
3062
|
+
if (!hex)
|
|
3063
|
+
return {};
|
|
3064
|
+
try {
|
|
3065
|
+
return { result: deserializeCVBytes(hex), resultHex: hex };
|
|
3066
|
+
} catch {
|
|
3067
|
+
return { resultHex: hex };
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
function normalizeStatus(txStatus) {
|
|
3071
|
+
switch (txStatus) {
|
|
3072
|
+
case "pending":
|
|
3073
|
+
case "success":
|
|
3074
|
+
case "abort_by_response":
|
|
3075
|
+
case "abort_by_post_condition":
|
|
3076
|
+
return txStatus;
|
|
3077
|
+
default:
|
|
3078
|
+
return txStatus.startsWith("dropped") ? "dropped" : undefined;
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
function extendedApiSource() {
|
|
3082
|
+
return {
|
|
3083
|
+
async get({ client, txid: txid2 }) {
|
|
3084
|
+
let data;
|
|
3085
|
+
try {
|
|
3086
|
+
data = await client.request(`/extended/v1/tx/${encodeURIComponent(normalizeTxid(txid2))}`, { method: "GET" });
|
|
3087
|
+
} catch (error) {
|
|
3088
|
+
if (error instanceof HttpRequestError && error.status === 404) {
|
|
3089
|
+
return { receipt: null };
|
|
3090
|
+
}
|
|
3091
|
+
throw error;
|
|
3092
|
+
}
|
|
3093
|
+
const txStatus = typeof data?.tx_status === "string" ? data.tx_status : undefined;
|
|
3094
|
+
if (!txStatus)
|
|
3095
|
+
return { receipt: null };
|
|
3096
|
+
const status = normalizeStatus(txStatus);
|
|
3097
|
+
if (!status)
|
|
3098
|
+
return { receipt: null };
|
|
3099
|
+
return {
|
|
3100
|
+
receipt: {
|
|
3101
|
+
txid: normalizeTxid(txid2),
|
|
3102
|
+
status,
|
|
3103
|
+
blockHeight: typeof data.block_height === "number" && data.block_height > 0 ? data.block_height : undefined,
|
|
3104
|
+
blockHash: data.block_hash ?? undefined,
|
|
3105
|
+
...decodeResultHex(data.tx_result?.hex),
|
|
3106
|
+
events: Array.isArray(data.events) ? data.events : [],
|
|
3107
|
+
raw: data
|
|
3108
|
+
}
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
};
|
|
3112
|
+
}
|
|
3113
|
+
function indexTxSource(params = {}) {
|
|
3114
|
+
const headers = params.apiKey ? { authorization: `Bearer ${params.apiKey}` } : undefined;
|
|
3115
|
+
return {
|
|
3116
|
+
canonicalOnly: true,
|
|
3117
|
+
async get({ client, txid: txid2 }) {
|
|
3118
|
+
const request = indexRequestFn(client, params.baseUrl, "indexTxSource");
|
|
3119
|
+
let data;
|
|
3120
|
+
try {
|
|
3121
|
+
data = await request(`/v1/index/transactions/${normalizeTxid(txid2)}`, {
|
|
3122
|
+
method: "GET",
|
|
3123
|
+
headers
|
|
3124
|
+
});
|
|
3125
|
+
} catch (error) {
|
|
3126
|
+
if (indexNotFound(error, "indexTxSource"))
|
|
3127
|
+
return { receipt: null };
|
|
3128
|
+
throw error;
|
|
3129
|
+
}
|
|
3130
|
+
const tx = data.transaction;
|
|
3131
|
+
const tip = data.tip?.block_height;
|
|
3132
|
+
if (!tx)
|
|
3133
|
+
return { receipt: null, tip };
|
|
3134
|
+
const status = normalizeStatus(tx.status) ?? "success";
|
|
3135
|
+
return {
|
|
3136
|
+
receipt: {
|
|
3137
|
+
txid: normalizeTxid(tx.tx_id),
|
|
3138
|
+
status,
|
|
3139
|
+
blockHeight: tx.block_height,
|
|
3140
|
+
...decodeResultHex(tx.contract_call?.result_hex),
|
|
3141
|
+
events: [],
|
|
3142
|
+
raw: data
|
|
3143
|
+
},
|
|
3144
|
+
tip
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
// src/actions/public/getTransaction.ts
|
|
3151
|
+
async function getTransaction(client, params) {
|
|
3152
|
+
const source = params.source ?? extendedApiSource();
|
|
3153
|
+
const { receipt } = await source.get({ client, txid: params.txid });
|
|
3154
|
+
return receipt;
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
// src/actions/public/getBlockHeight.ts
|
|
3158
|
+
async function getBlockHeight(client) {
|
|
3159
|
+
const data = await client.request("/v2/info", { method: "GET" });
|
|
3160
|
+
if (typeof data?.stacks_tip_height !== "number") {
|
|
3161
|
+
throw new MalformedResponseError('getBlockHeight: /v2/info response is missing "stacks_tip_height"');
|
|
3162
|
+
}
|
|
3163
|
+
return data.stacks_tip_height;
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3166
|
+
// src/actions/public/waitForTransactionReceipt.ts
|
|
3167
|
+
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
3168
|
+
async function waitForTransactionReceipt(client, params) {
|
|
3169
|
+
const {
|
|
3170
|
+
txid: txid2,
|
|
3171
|
+
confirmations = 1,
|
|
3172
|
+
timeout = 180000,
|
|
3173
|
+
pollingInterval = 3000
|
|
3174
|
+
} = params;
|
|
3175
|
+
const source = params.source ?? extendedApiSource();
|
|
3176
|
+
const droppedGracePeriod = params.droppedGracePeriod ?? (source.canonicalOnly ? timeout : 30000);
|
|
3177
|
+
const startedAt = Date.now();
|
|
3178
|
+
let unknownSince = null;
|
|
3179
|
+
let everPending = false;
|
|
3180
|
+
while (true) {
|
|
3181
|
+
const snapshot = await source.get({ client, txid: txid2 });
|
|
3182
|
+
const receipt = snapshot.receipt;
|
|
3183
|
+
if (receipt === null) {
|
|
3184
|
+
unknownSince ??= Date.now();
|
|
3185
|
+
if (Date.now() - unknownSince >= droppedGracePeriod) {
|
|
3186
|
+
throw new TransactionDroppedError(everPending ? `Transaction ${txid2} left the mempool without being mined` : `Transaction ${txid2} was never observed by the status source`, { txid: txid2 });
|
|
3187
|
+
}
|
|
3188
|
+
} else {
|
|
3189
|
+
unknownSince = null;
|
|
3190
|
+
switch (receipt.status) {
|
|
3191
|
+
case "dropped":
|
|
3192
|
+
throw new TransactionDroppedError(`Transaction ${txid2} was dropped from the mempool`, { txid: txid2 });
|
|
3193
|
+
case "abort_by_response":
|
|
3194
|
+
case "abort_by_post_condition":
|
|
3195
|
+
throw new TransactionAbortedError(`Transaction ${txid2} aborted (${receipt.status})`, { receipt });
|
|
3196
|
+
case "pending":
|
|
3197
|
+
everPending = true;
|
|
3198
|
+
break;
|
|
3199
|
+
case "success": {
|
|
3200
|
+
if (confirmations <= 1)
|
|
3201
|
+
return receipt;
|
|
3202
|
+
if (receipt.blockHeight !== undefined) {
|
|
3203
|
+
const tip = snapshot.tip ?? await getBlockHeight(client);
|
|
3204
|
+
if (tip - receipt.blockHeight + 1 >= confirmations)
|
|
3205
|
+
return receipt;
|
|
3206
|
+
}
|
|
3207
|
+
break;
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
if (Date.now() - startedAt >= timeout) {
|
|
3212
|
+
throw new WaitForTransactionTimeoutError(`Timed out after ${timeout}ms waiting for transaction ${txid2}`, { txid: txid2 });
|
|
3213
|
+
}
|
|
3214
|
+
await sleep2(pollingInterval);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
// src/actions/wallet/sendTransaction.ts
|
|
3219
|
+
async function sendTransaction(client, params) {
|
|
3220
|
+
const hex = bytesToHex(serializeTransaction(params.transaction));
|
|
3221
|
+
const body = { tx: hex };
|
|
3222
|
+
if (params.attachment) {
|
|
3223
|
+
body.attachment = typeof params.attachment === "string" ? params.attachment : bytesToHex(params.attachment);
|
|
3224
|
+
}
|
|
3225
|
+
let data;
|
|
3226
|
+
try {
|
|
3227
|
+
data = await client.request("/v2/transactions", {
|
|
3228
|
+
method: "POST",
|
|
3229
|
+
body,
|
|
3230
|
+
retryCount: 0
|
|
3231
|
+
});
|
|
3232
|
+
} catch (error) {
|
|
3233
|
+
if (error instanceof HttpRequestError && error.details) {
|
|
3234
|
+
try {
|
|
3235
|
+
data = JSON.parse(error.details);
|
|
3236
|
+
} catch {
|
|
3237
|
+
throw error;
|
|
3238
|
+
}
|
|
3239
|
+
} else if (error instanceof TimeoutError) {
|
|
3240
|
+
const txid3 = getTransactionId(params.transaction);
|
|
3241
|
+
const receipt = await getTransaction(client, { txid: txid3 }).catch(() => null);
|
|
3242
|
+
if (receipt === null)
|
|
3243
|
+
throw error;
|
|
3244
|
+
data = { txid: txid3 };
|
|
3245
|
+
} else {
|
|
3246
|
+
throw error;
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
if (data.error) {
|
|
3250
|
+
throw new BroadcastError(data.reason ?? data.error, {
|
|
3251
|
+
reason: data.reason,
|
|
3252
|
+
reasonData: data.reason_data,
|
|
3253
|
+
txid: data.txid ?? getTransactionId(params.transaction)
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
const txid2 = typeof data === "string" ? data.replace(/"/g, "") : data.txid ?? getTransactionId(params.transaction);
|
|
3257
|
+
if (params.wait) {
|
|
3258
|
+
const confirmations = params.wait === true ? 1 : params.wait;
|
|
3259
|
+
const receipt = await waitForTransactionReceipt(client, {
|
|
3260
|
+
txid: txid2,
|
|
3261
|
+
confirmations
|
|
3262
|
+
});
|
|
3263
|
+
return { txid: txid2, receipt };
|
|
3264
|
+
}
|
|
3265
|
+
return { txid: txid2 };
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
// src/actions/wallet/nonceManager.ts
|
|
3269
|
+
function jsonRpcSource() {
|
|
3270
|
+
return {
|
|
3271
|
+
get: ({ client, address }) => getNonce(client, { address })
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3274
|
+
function memoryStore() {
|
|
3275
|
+
const next = new Map;
|
|
3276
|
+
const locks = new Map;
|
|
3277
|
+
function withLock(key, fn) {
|
|
3278
|
+
const prev = locks.get(key) ?? Promise.resolve();
|
|
3279
|
+
const run = prev.then(fn, fn);
|
|
3280
|
+
locks.set(key, run.catch(() => {}));
|
|
3281
|
+
return run;
|
|
3282
|
+
}
|
|
3283
|
+
return {
|
|
3284
|
+
reserve(key, getFloor) {
|
|
3285
|
+
return withLock(key, async () => {
|
|
3286
|
+
let current = next.get(key);
|
|
3287
|
+
if (current === undefined)
|
|
3288
|
+
current = await getFloor();
|
|
3289
|
+
next.set(key, current + 1n);
|
|
3290
|
+
return current;
|
|
3291
|
+
});
|
|
3292
|
+
},
|
|
3293
|
+
reset(key) {
|
|
3294
|
+
next.delete(key);
|
|
3295
|
+
},
|
|
3296
|
+
release(key, nonce) {
|
|
3297
|
+
return withLock(key, async () => {
|
|
3298
|
+
if (next.get(key) === nonce + 1n)
|
|
3299
|
+
next.set(key, nonce);
|
|
3300
|
+
});
|
|
3301
|
+
},
|
|
3302
|
+
peek(key) {
|
|
3303
|
+
return next.get(key);
|
|
3304
|
+
}
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
function nonceKey(client, address) {
|
|
3308
|
+
return `${client.chain?.id ?? "stacks"}:${address}`;
|
|
3309
|
+
}
|
|
3310
|
+
function createNonceManager(params = {}) {
|
|
3311
|
+
const source = params.source ?? jsonRpcSource();
|
|
3312
|
+
const store = params.store ?? memoryStore();
|
|
3313
|
+
return {
|
|
3314
|
+
consume({ client, address }) {
|
|
3315
|
+
return store.reserve(nonceKey(client, address), () => source.get({ client, address }));
|
|
3316
|
+
},
|
|
3317
|
+
reset({ client, address }) {
|
|
3318
|
+
return store.reset(nonceKey(client, address));
|
|
3319
|
+
},
|
|
3320
|
+
release({ client, address, nonce }) {
|
|
3321
|
+
return store.release?.(nonceKey(client, address), nonce);
|
|
3322
|
+
},
|
|
3323
|
+
async peek({ client, address }) {
|
|
3324
|
+
return store.peek?.(nonceKey(client, address));
|
|
3325
|
+
}
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
async function resolveNonce(client, address) {
|
|
3329
|
+
if (client.nonceManager)
|
|
3330
|
+
return client.nonceManager.consume({ client, address });
|
|
3331
|
+
return getNonce(client, { address });
|
|
3332
|
+
}
|
|
3333
|
+
async function releaseNonce(client, address, nonce) {
|
|
3334
|
+
if (!client.nonceManager)
|
|
3335
|
+
return;
|
|
3336
|
+
try {
|
|
3337
|
+
await client.nonceManager.release({ client, address, nonce });
|
|
3338
|
+
} catch {}
|
|
3339
|
+
}
|
|
3340
|
+
async function reconcileNonce(manager, params) {
|
|
3341
|
+
const { client, address, source } = params;
|
|
3342
|
+
const downward = params.downward ?? true;
|
|
3343
|
+
const authoritative = await source.get({ client, address });
|
|
3344
|
+
const tracked = await manager.peek({ client, address });
|
|
3345
|
+
if (tracked === undefined || authoritative === tracked) {
|
|
3346
|
+
return { reset: false, authoritative, tracked };
|
|
304
3347
|
}
|
|
305
|
-
if (
|
|
306
|
-
return;
|
|
307
|
-
|
|
3348
|
+
if (authoritative < tracked && !downward) {
|
|
3349
|
+
return { reset: false, authoritative, tracked };
|
|
3350
|
+
}
|
|
3351
|
+
await manager.reset({ client, address });
|
|
3352
|
+
return { reset: true, authoritative, tracked };
|
|
308
3353
|
}
|
|
309
|
-
function
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
3354
|
+
function startNonceReconciler(manager, params) {
|
|
3355
|
+
const { client, addresses, source, clock } = params;
|
|
3356
|
+
const intervalMs = params.intervalMs ?? 60000;
|
|
3357
|
+
let running = true;
|
|
3358
|
+
const tick = async () => {
|
|
3359
|
+
if (!running)
|
|
3360
|
+
return;
|
|
3361
|
+
for (const address of addresses) {
|
|
3362
|
+
try {
|
|
3363
|
+
const result = await reconcileNonce(manager, {
|
|
3364
|
+
client,
|
|
3365
|
+
address,
|
|
3366
|
+
source,
|
|
3367
|
+
downward: params.downward
|
|
3368
|
+
});
|
|
3369
|
+
params.onReconcile?.(address, result);
|
|
3370
|
+
} catch (error) {
|
|
3371
|
+
try {
|
|
3372
|
+
params.onError?.(address, error);
|
|
3373
|
+
} catch {}
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
if (running && clock) {
|
|
3377
|
+
await clock.advance(intervalMs);
|
|
3378
|
+
tick();
|
|
3379
|
+
}
|
|
3380
|
+
};
|
|
3381
|
+
if (clock) {
|
|
3382
|
+
tick();
|
|
3383
|
+
return {
|
|
3384
|
+
stop: () => {
|
|
3385
|
+
running = false;
|
|
3386
|
+
}
|
|
3387
|
+
};
|
|
3388
|
+
}
|
|
3389
|
+
const timer = setInterval(tick, intervalMs);
|
|
3390
|
+
timer.unref?.();
|
|
3391
|
+
return {
|
|
3392
|
+
stop: () => clearInterval(timer)
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
function isNonceConflictError(error) {
|
|
3396
|
+
if (!(error instanceof BroadcastError))
|
|
3397
|
+
return false;
|
|
3398
|
+
if (error.reason === "ConflictingNonceInMempool" || error.reason === "BadNonce") {
|
|
3399
|
+
return true;
|
|
3400
|
+
}
|
|
3401
|
+
if (error.reason !== undefined)
|
|
3402
|
+
return false;
|
|
3403
|
+
return error.message.toLowerCase().includes("nonce");
|
|
3404
|
+
}
|
|
3405
|
+
async function broadcastWithNonceReset(client, params) {
|
|
3406
|
+
try {
|
|
3407
|
+
const result = await sendTransaction(client, {
|
|
3408
|
+
transaction: params.transaction
|
|
3409
|
+
});
|
|
3410
|
+
return result.txid;
|
|
3411
|
+
} catch (error) {
|
|
3412
|
+
if (client.nonceManager && isNonceConflictError(error)) {
|
|
3413
|
+
await client.nonceManager.reset({ client, address: params.address });
|
|
3414
|
+
}
|
|
3415
|
+
throw error;
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
// src/actions/public/estimateFee.ts
|
|
3420
|
+
function isValidEstimation(e) {
|
|
3421
|
+
return typeof e === "object" && e !== null && "fee_rate" in e && "fee" in e && (typeof e.fee_rate === "number" || typeof e.fee_rate === "string") && (typeof e.fee === "number" || typeof e.fee === "string");
|
|
3422
|
+
}
|
|
3423
|
+
async function estimateFee(client, params) {
|
|
3424
|
+
const payloadHex = bytesToHex(serializePayload(params.transaction.payload));
|
|
3425
|
+
const txHex = bytesToHex(serializeTransaction(params.transaction));
|
|
3426
|
+
const data = await client.request("/v2/fees/transaction", {
|
|
3427
|
+
method: "POST",
|
|
3428
|
+
body: {
|
|
3429
|
+
estimated_len: Math.ceil(txHex.length / 2),
|
|
3430
|
+
transaction_payload: `0x${payloadHex}`
|
|
3431
|
+
}
|
|
3432
|
+
});
|
|
3433
|
+
const raw = data?.estimations ?? [];
|
|
3434
|
+
return raw.map((e) => {
|
|
3435
|
+
if (!isValidEstimation(e)) {
|
|
3436
|
+
throw new MalformedResponseError("estimateFee: node response contains invalid fee estimation entry");
|
|
3437
|
+
}
|
|
3438
|
+
return {
|
|
3439
|
+
feeRate: Number(e.fee_rate),
|
|
3440
|
+
fee: Number(e.fee)
|
|
3441
|
+
};
|
|
3442
|
+
});
|
|
3443
|
+
}
|
|
3444
|
+
|
|
3445
|
+
// src/actions/wallet/utils.ts
|
|
3446
|
+
function isProviderAccount(account) {
|
|
3447
|
+
return account.type === "provider";
|
|
3448
|
+
}
|
|
3449
|
+
var FEE_TIERS = ["min", "low", "mid", "high"];
|
|
3450
|
+
var TIER_INDEX = { low: 0, mid: 1, high: 2 };
|
|
3451
|
+
function isFeeTier(fee) {
|
|
3452
|
+
return typeof fee === "string" && FEE_TIERS.includes(fee);
|
|
3453
|
+
}
|
|
3454
|
+
function minimumFee(transaction) {
|
|
3455
|
+
return BigInt(serializeTransaction(transaction).length);
|
|
3456
|
+
}
|
|
3457
|
+
function isNoEstimateAvailable(error) {
|
|
3458
|
+
return error instanceof HttpRequestError && error.status === 400 && (error.details?.includes("NoEstimateAvailable") ?? false);
|
|
3459
|
+
}
|
|
3460
|
+
async function resolveFee(client, transaction, fee) {
|
|
3461
|
+
if (fee !== undefined && !isFeeTier(fee))
|
|
3462
|
+
return { fee: intToBigInt(fee) };
|
|
3463
|
+
const tier = fee ?? "mid";
|
|
3464
|
+
if (tier === "min")
|
|
3465
|
+
return { fee: minimumFee(transaction), tier };
|
|
3466
|
+
let estimates;
|
|
3467
|
+
try {
|
|
3468
|
+
estimates = await estimateFee(client, { transaction });
|
|
3469
|
+
} catch (error) {
|
|
3470
|
+
if (!isNoEstimateAvailable(error))
|
|
3471
|
+
throw error;
|
|
3472
|
+
return { fee: minimumFee(transaction), tier: "min" };
|
|
3473
|
+
}
|
|
3474
|
+
const pick = estimates[TIER_INDEX[tier]] ?? estimates[estimates.length - 1];
|
|
3475
|
+
if (pick)
|
|
3476
|
+
return { fee: BigInt(pick.fee), tier };
|
|
3477
|
+
return { fee: minimumFee(transaction), tier: "min" };
|
|
3478
|
+
}
|
|
3479
|
+
function setUnsignedFee(transaction, fee) {
|
|
3480
|
+
transaction.auth.spendingCondition.fee = fee;
|
|
3481
|
+
clearTxCache(transaction);
|
|
3482
|
+
}
|
|
3483
|
+
function assertNoFeeTierForProvider(fee) {
|
|
3484
|
+
if (isFeeTier(fee)) {
|
|
3485
|
+
throw new Error(`Fee tier '${fee}' requires local signing; provider (wallet) accounts set their own fee`);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
// src/actions/wallet/callContract.ts
|
|
3490
|
+
async function callContract(client, params) {
|
|
3491
|
+
const account = client.account;
|
|
3492
|
+
if (!account)
|
|
3493
|
+
throw new Error("Account required");
|
|
3494
|
+
const [contractAddress, contractName] = parseContractId(params.contract);
|
|
3495
|
+
if (isProviderAccount(account)) {
|
|
3496
|
+
assertNoFeeTierForProvider(params.fee);
|
|
3497
|
+
const result = await account.provider.request("stx_callContract", {
|
|
3498
|
+
contract: params.contract,
|
|
3499
|
+
functionName: params.functionName,
|
|
3500
|
+
functionArgs: params.functionArgs ?? []
|
|
3501
|
+
});
|
|
3502
|
+
return result.txid;
|
|
3503
|
+
}
|
|
3504
|
+
const managed = params.nonce === undefined && client.nonceManager !== undefined;
|
|
3505
|
+
const nonce = params.nonce ?? await resolveNonce(client, account.address);
|
|
3506
|
+
try {
|
|
3507
|
+
const needsFeeResolution = params.fee === undefined || isFeeTier(params.fee);
|
|
3508
|
+
const unsigned = buildContractCall({
|
|
3509
|
+
contractAddress,
|
|
3510
|
+
contractName,
|
|
3511
|
+
functionName: params.functionName,
|
|
3512
|
+
functionArgs: params.functionArgs ?? [],
|
|
3513
|
+
fee: needsFeeResolution ? 0n : params.fee,
|
|
3514
|
+
nonce,
|
|
3515
|
+
publicKey: account.publicKey,
|
|
3516
|
+
chain: client.chain,
|
|
3517
|
+
postConditionMode: params.postConditionMode,
|
|
3518
|
+
postConditions: params.postConditions
|
|
3519
|
+
});
|
|
3520
|
+
if (needsFeeResolution) {
|
|
3521
|
+
const { fee } = await resolveFee(client, unsigned, params.fee);
|
|
3522
|
+
setUnsignedFee(unsigned, fee);
|
|
3523
|
+
}
|
|
3524
|
+
const signed = await signTransactionWithAccount(unsigned, account);
|
|
3525
|
+
return await broadcastWithNonceReset(client, {
|
|
3526
|
+
transaction: signed,
|
|
3527
|
+
address: account.address
|
|
3528
|
+
});
|
|
3529
|
+
} catch (error) {
|
|
3530
|
+
if (managed)
|
|
3531
|
+
await releaseNonce(client, account.address, nonce);
|
|
3532
|
+
throw error;
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3536
|
+
// src/actions/getContract.ts
|
|
3537
|
+
function getContract(params) {
|
|
3538
|
+
const { client, address, name: contractName, abi } = params;
|
|
3539
|
+
const contractId = `${address}.${contractName}`;
|
|
3540
|
+
const fnByName = new Map;
|
|
3541
|
+
for (const fn of abi.functions) {
|
|
3542
|
+
fnByName.set(fn.name, fn);
|
|
3543
|
+
}
|
|
3544
|
+
const mapByName = new Map;
|
|
3545
|
+
if (abi.maps) {
|
|
3546
|
+
for (const m of abi.maps) {
|
|
3547
|
+
mapByName.set(m.name, m);
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
const camelToKebab = new Map;
|
|
3551
|
+
for (const fn of abi.functions) {
|
|
3552
|
+
camelToKebab.set(toCamelCase(fn.name), fn.name);
|
|
3553
|
+
}
|
|
3554
|
+
if (abi.maps) {
|
|
3555
|
+
for (const m of abi.maps) {
|
|
3556
|
+
camelToKebab.set(toCamelCase(m.name), m.name);
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
const read = new Proxy({}, {
|
|
3560
|
+
get(_target, prop) {
|
|
3561
|
+
const fnName = camelToKebab.get(prop) ?? prop;
|
|
3562
|
+
const fn = fnByName.get(fnName);
|
|
3563
|
+
if (!fn || fn.access !== "read-only")
|
|
3564
|
+
return;
|
|
3565
|
+
return async (args = {}) => {
|
|
3566
|
+
const clarityArgs = buildFunctionArgs(fn, args);
|
|
3567
|
+
const result = await readContract(client, {
|
|
3568
|
+
contract: contractId,
|
|
3569
|
+
functionName: fn.name,
|
|
3570
|
+
args: clarityArgs
|
|
3571
|
+
});
|
|
3572
|
+
const jsValue = clarityValueToJSUntyped(fn.outputs, result);
|
|
3573
|
+
if (isResponseOutput(fn.outputs)) {
|
|
3574
|
+
const resp = jsValue;
|
|
3575
|
+
if ("ok" in resp)
|
|
3576
|
+
return resp.ok;
|
|
3577
|
+
throw new ContractResponseError(`${contractId}::${fn.name} returned (err ${formatErrValue(resp.err)})`, resp.err);
|
|
3578
|
+
}
|
|
3579
|
+
return jsValue;
|
|
3580
|
+
};
|
|
3581
|
+
}
|
|
3582
|
+
});
|
|
3583
|
+
const call = new Proxy({}, {
|
|
3584
|
+
get(_target, prop) {
|
|
3585
|
+
const fnName = camelToKebab.get(prop) ?? prop;
|
|
3586
|
+
const fn = fnByName.get(fnName);
|
|
3587
|
+
if (!fn || fn.access !== "public")
|
|
3588
|
+
return;
|
|
3589
|
+
return async (args = {}, options) => {
|
|
3590
|
+
const clarityArgs = buildFunctionArgs(fn, args);
|
|
3591
|
+
return callContract(client, {
|
|
3592
|
+
contract: contractId,
|
|
3593
|
+
functionName: fn.name,
|
|
3594
|
+
functionArgs: clarityArgs,
|
|
3595
|
+
...options
|
|
3596
|
+
});
|
|
3597
|
+
};
|
|
3598
|
+
}
|
|
3599
|
+
});
|
|
3600
|
+
const buildCall = new Proxy({}, {
|
|
3601
|
+
get(_target, prop) {
|
|
3602
|
+
const fnName = camelToKebab.get(prop) ?? prop;
|
|
3603
|
+
const fn = fnByName.get(fnName);
|
|
3604
|
+
if (!fn || fn.access !== "public")
|
|
3605
|
+
return;
|
|
3606
|
+
return async (args = {}, options) => {
|
|
3607
|
+
const publicKey = options?.publicKey ?? client.account?.publicKey;
|
|
3608
|
+
if (!publicKey) {
|
|
3609
|
+
throw new Error("buildCall requires a publicKey (pass options.publicKey or configure a client account)");
|
|
3610
|
+
}
|
|
3611
|
+
const functionArgs = buildFunctionArgs(fn, args);
|
|
3612
|
+
const network = client.chain?.network ?? "mainnet";
|
|
3613
|
+
const nonce = options?.nonce ?? await getNonce(client, {
|
|
3614
|
+
address: publicKeyToAddress(publicKey, network)
|
|
3615
|
+
});
|
|
3616
|
+
const unsigned = buildContractCall({
|
|
3617
|
+
contractAddress: address,
|
|
3618
|
+
contractName,
|
|
3619
|
+
functionName: fn.name,
|
|
3620
|
+
functionArgs,
|
|
3621
|
+
fee: options?.fee ?? 0n,
|
|
3622
|
+
nonce,
|
|
3623
|
+
publicKey,
|
|
3624
|
+
chain: client.chain,
|
|
3625
|
+
postConditionMode: options?.postConditionMode,
|
|
3626
|
+
postConditions: options?.postConditions,
|
|
3627
|
+
sponsored: options?.sponsored
|
|
3628
|
+
});
|
|
3629
|
+
if (options?.fee === undefined) {
|
|
3630
|
+
const { fee } = await resolveFee(client, unsigned, undefined);
|
|
3631
|
+
setUnsignedFee(unsigned, fee);
|
|
3632
|
+
}
|
|
3633
|
+
return unsigned;
|
|
3634
|
+
};
|
|
3635
|
+
}
|
|
3636
|
+
});
|
|
3637
|
+
const maps = new Proxy({}, {
|
|
3638
|
+
get(_target, prop) {
|
|
3639
|
+
const mapName = camelToKebab.get(prop) ?? prop;
|
|
3640
|
+
const map = mapByName.get(mapName);
|
|
3641
|
+
if (!map)
|
|
3642
|
+
return;
|
|
3643
|
+
return async (key) => {
|
|
3644
|
+
const clarityKey = jsToClarityValue(map.key, key);
|
|
3645
|
+
const result = await getMapEntry(client, {
|
|
3646
|
+
contract: contractId,
|
|
3647
|
+
mapName: map.name,
|
|
3648
|
+
key: clarityKey
|
|
3649
|
+
});
|
|
3650
|
+
if (result.type === "none")
|
|
3651
|
+
return null;
|
|
3652
|
+
if (result.type === "some") {
|
|
3653
|
+
return clarityValueToJSUntyped(map.value, result.value);
|
|
3654
|
+
}
|
|
3655
|
+
return clarityValueToJSUntyped(map.value, result);
|
|
3656
|
+
};
|
|
3657
|
+
}
|
|
3658
|
+
});
|
|
3659
|
+
return { read, call, buildCall, maps };
|
|
3660
|
+
}
|
|
3661
|
+
function resolveNetworkContract(client, contracts) {
|
|
3662
|
+
if (!client.chain) {
|
|
3663
|
+
throw new Error("Client must have a chain configured");
|
|
3664
|
+
}
|
|
3665
|
+
return contracts[client.chain.network];
|
|
3666
|
+
}
|
|
3667
|
+
function buildFunctionArgs(fn, args = {}) {
|
|
3668
|
+
return fn.args.map((arg) => {
|
|
3669
|
+
const camelKey = toCamelCase(arg.name);
|
|
3670
|
+
const hasOriginal = arg.name in args;
|
|
3671
|
+
const hasCamel = camelKey in args;
|
|
3672
|
+
const value = hasOriginal ? args[arg.name] : hasCamel ? args[camelKey] : undefined;
|
|
3673
|
+
if (value === undefined)
|
|
3674
|
+
throw new Error(`Missing argument: ${arg.name}`);
|
|
3675
|
+
return jsToClarityValue(arg.type, value);
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
function isResponseOutput(type) {
|
|
3679
|
+
return typeof type === "object" && type !== null && "response" in type;
|
|
3680
|
+
}
|
|
3681
|
+
function formatErrValue(value) {
|
|
3682
|
+
if (typeof value === "bigint")
|
|
3683
|
+
return value.toString();
|
|
3684
|
+
if (typeof value === "string")
|
|
3685
|
+
return `"${value}"`;
|
|
3686
|
+
return String(value);
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
class ContractResponseError extends Error {
|
|
3690
|
+
errorValue;
|
|
3691
|
+
name = "ContractResponseError";
|
|
3692
|
+
constructor(message, errorValue) {
|
|
3693
|
+
super(message);
|
|
3694
|
+
this.errorValue = errorValue;
|
|
3695
|
+
}
|
|
315
3696
|
}
|
|
3697
|
+
|
|
316
3698
|
// src/pox5/abi.ts
|
|
317
3699
|
var POX5_ABI = {
|
|
318
3700
|
functions: [
|
|
@@ -1603,8 +4985,222 @@ async function eligiblePauseRewards(client, params) {
|
|
|
1603
4985
|
return { ok: true };
|
|
1604
4986
|
}
|
|
1605
4987
|
// src/pox5/script.ts
|
|
1606
|
-
var
|
|
1607
|
-
var
|
|
4988
|
+
var import_sha25 = require("@noble/hashes/sha2.js");
|
|
4989
|
+
var import_base6 = require("@scure/base");
|
|
4990
|
+
|
|
4991
|
+
// src/bitcoin/address.ts
|
|
4992
|
+
var import_secp256k13 = require("@noble/curves/secp256k1.js");
|
|
4993
|
+
var import_base5 = require("@scure/base");
|
|
4994
|
+
|
|
4995
|
+
// src/bitcoin/serialize.ts
|
|
4996
|
+
function doubleSha256(bytes) {
|
|
4997
|
+
return import_sha22.sha256(import_sha22.sha256(bytes));
|
|
4998
|
+
}
|
|
4999
|
+
function reverseBytes(bytes) {
|
|
5000
|
+
const out = new Uint8Array(bytes.length);
|
|
5001
|
+
for (let i = 0;i < bytes.length; i++) {
|
|
5002
|
+
out[i] = bytes[bytes.length - 1 - i];
|
|
5003
|
+
}
|
|
5004
|
+
return out;
|
|
5005
|
+
}
|
|
5006
|
+
|
|
5007
|
+
class BtcReader {
|
|
5008
|
+
data;
|
|
5009
|
+
view;
|
|
5010
|
+
offset = 0;
|
|
5011
|
+
constructor(data) {
|
|
5012
|
+
this.data = data;
|
|
5013
|
+
this.view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
5014
|
+
}
|
|
5015
|
+
get length() {
|
|
5016
|
+
return this.data.length;
|
|
5017
|
+
}
|
|
5018
|
+
readUInt8() {
|
|
5019
|
+
const v = this.view.getUint8(this.offset);
|
|
5020
|
+
this.offset += 1;
|
|
5021
|
+
return v;
|
|
5022
|
+
}
|
|
5023
|
+
peekUInt8() {
|
|
5024
|
+
return this.view.getUint8(this.offset);
|
|
5025
|
+
}
|
|
5026
|
+
readUInt32LE() {
|
|
5027
|
+
const v = this.view.getUint32(this.offset, true);
|
|
5028
|
+
this.offset += 4;
|
|
5029
|
+
return v;
|
|
5030
|
+
}
|
|
5031
|
+
readUInt64LE() {
|
|
5032
|
+
const v = this.view.getBigUint64(this.offset, true);
|
|
5033
|
+
this.offset += 8;
|
|
5034
|
+
return v;
|
|
5035
|
+
}
|
|
5036
|
+
readBytes(n) {
|
|
5037
|
+
if (this.offset + n > this.data.length) {
|
|
5038
|
+
throw new Error(`Bitcoin tx underflow: need ${n} bytes at offset ${this.offset}, have ${this.data.length}`);
|
|
5039
|
+
}
|
|
5040
|
+
const slice = this.data.slice(this.offset, this.offset + n);
|
|
5041
|
+
this.offset += n;
|
|
5042
|
+
return slice;
|
|
5043
|
+
}
|
|
5044
|
+
readVarInt() {
|
|
5045
|
+
const first = this.readUInt8();
|
|
5046
|
+
if (first < 253)
|
|
5047
|
+
return first;
|
|
5048
|
+
if (first === 253) {
|
|
5049
|
+
const v = this.view.getUint16(this.offset, true);
|
|
5050
|
+
this.offset += 2;
|
|
5051
|
+
return v;
|
|
5052
|
+
}
|
|
5053
|
+
if (first === 254) {
|
|
5054
|
+
return this.readUInt32LE();
|
|
5055
|
+
}
|
|
5056
|
+
const big = this.readUInt64LE();
|
|
5057
|
+
if (big > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5058
|
+
throw new Error("Bitcoin varint exceeds safe integer range");
|
|
5059
|
+
}
|
|
5060
|
+
return Number(big);
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
function scanTx(rawTx) {
|
|
5064
|
+
const r = new BtcReader(rawTx);
|
|
5065
|
+
const version = r.readUInt32LE();
|
|
5066
|
+
let hasWitness = false;
|
|
5067
|
+
if (r.peekUInt8() === 0) {
|
|
5068
|
+
r.readUInt8();
|
|
5069
|
+
const flag = r.readUInt8();
|
|
5070
|
+
if (flag !== 1) {
|
|
5071
|
+
throw new Error(`Unexpected SegWit flag 0x${flag.toString(16)}`);
|
|
5072
|
+
}
|
|
5073
|
+
hasWitness = true;
|
|
5074
|
+
}
|
|
5075
|
+
const bodyStart = r.offset;
|
|
5076
|
+
const inputCount = r.readVarInt();
|
|
5077
|
+
const inputs = [];
|
|
5078
|
+
for (let i = 0;i < inputCount; i++) {
|
|
5079
|
+
const prevTxid = r.readBytes(32);
|
|
5080
|
+
const vout = r.readUInt32LE();
|
|
5081
|
+
const scriptSig = r.readBytes(r.readVarInt());
|
|
5082
|
+
const sequence = r.readUInt32LE();
|
|
5083
|
+
inputs.push({ prevTxid, vout, scriptSig, sequence });
|
|
5084
|
+
}
|
|
5085
|
+
const outputCount = r.readVarInt();
|
|
5086
|
+
const outputs = [];
|
|
5087
|
+
for (let i = 0;i < outputCount; i++) {
|
|
5088
|
+
const value = r.readUInt64LE();
|
|
5089
|
+
const scriptPubKey = r.readBytes(r.readVarInt());
|
|
5090
|
+
outputs.push({ value, scriptPubKey });
|
|
5091
|
+
}
|
|
5092
|
+
const bodyEnd = r.offset;
|
|
5093
|
+
if (hasWitness) {
|
|
5094
|
+
for (let i = 0;i < inputCount; i++) {
|
|
5095
|
+
const itemCount = r.readVarInt();
|
|
5096
|
+
for (let j = 0;j < itemCount; j++) {
|
|
5097
|
+
r.readBytes(r.readVarInt());
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
const locktime = r.readUInt32LE();
|
|
5102
|
+
const legacy = concatBytes(rawTx.slice(0, 4), rawTx.slice(bodyStart, bodyEnd), rawTx.slice(r.offset - 4, r.offset));
|
|
5103
|
+
return { version, hasWitness, inputs, outputs, locktime, legacy };
|
|
5104
|
+
}
|
|
5105
|
+
function parseBitcoinTx(rawTx) {
|
|
5106
|
+
const s = scanTx(rawTx);
|
|
5107
|
+
return {
|
|
5108
|
+
version: s.version,
|
|
5109
|
+
hasWitness: s.hasWitness,
|
|
5110
|
+
inputs: s.inputs,
|
|
5111
|
+
outputs: s.outputs,
|
|
5112
|
+
locktime: s.locktime,
|
|
5113
|
+
txidInternal: doubleSha256(s.legacy)
|
|
5114
|
+
};
|
|
5115
|
+
}
|
|
5116
|
+
function stripWitness(rawTx) {
|
|
5117
|
+
return scanTx(rawTx).legacy;
|
|
5118
|
+
}
|
|
5119
|
+
function bitcoinTxid(rawTx, { display = false } = {}) {
|
|
5120
|
+
const txid2 = doubleSha256(scanTx(rawTx).legacy);
|
|
5121
|
+
return display ? reverseBytes(txid2) : txid2;
|
|
5122
|
+
}
|
|
5123
|
+
function parseBlockHeader(header) {
|
|
5124
|
+
if (header.length !== 80) {
|
|
5125
|
+
throw new Error(`Bitcoin block header must be 80 bytes, got ${header.length}`);
|
|
5126
|
+
}
|
|
5127
|
+
const r = new BtcReader(header);
|
|
5128
|
+
const version = r.readUInt32LE();
|
|
5129
|
+
const prevBlock = r.readBytes(32);
|
|
5130
|
+
const merkleRoot = r.readBytes(32);
|
|
5131
|
+
const timestamp = r.readUInt32LE();
|
|
5132
|
+
const bits = r.readUInt32LE();
|
|
5133
|
+
const nonce = r.readUInt32LE();
|
|
5134
|
+
return { version, prevBlock, merkleRoot, timestamp, bits, nonce };
|
|
5135
|
+
}
|
|
5136
|
+
function blockHash(header, { display = false } = {}) {
|
|
5137
|
+
const hash = doubleSha256(header);
|
|
5138
|
+
return display ? reverseBytes(hash) : hash;
|
|
5139
|
+
}
|
|
5140
|
+
|
|
5141
|
+
// src/bitcoin/address.ts
|
|
5142
|
+
var BITCOIN_NETWORK_PARAMS = {
|
|
5143
|
+
mainnet: { p2pkh: 0, p2sh: 5, hrp: "bc" },
|
|
5144
|
+
testnet: { p2pkh: 111, p2sh: 196, hrp: "tb" },
|
|
5145
|
+
regtest: { p2pkh: 111, p2sh: 196, hrp: "bcrt" }
|
|
5146
|
+
};
|
|
5147
|
+
var NETWORK_PARAMS = BITCOIN_NETWORK_PARAMS;
|
|
5148
|
+
function base58CheckEncode(version, payload) {
|
|
5149
|
+
const data = concatBytes(Uint8Array.of(version), payload);
|
|
5150
|
+
const checksum = doubleSha256(data).slice(0, 4);
|
|
5151
|
+
return import_base5.base58.encode(concatBytes(data, checksum));
|
|
5152
|
+
}
|
|
5153
|
+
function formatBitcoinAddress(parsed, network = "mainnet") {
|
|
5154
|
+
const params = NETWORK_PARAMS[network];
|
|
5155
|
+
const hash = parsed.hash;
|
|
5156
|
+
if (!hash)
|
|
5157
|
+
return;
|
|
5158
|
+
switch (parsed.type) {
|
|
5159
|
+
case "p2pkh":
|
|
5160
|
+
return base58CheckEncode(params.p2pkh, hash);
|
|
5161
|
+
case "p2sh":
|
|
5162
|
+
return base58CheckEncode(params.p2sh, hash);
|
|
5163
|
+
case "p2wpkh":
|
|
5164
|
+
case "p2wsh":
|
|
5165
|
+
return import_base5.bech32.encode(params.hrp, [0, ...import_base5.bech32.toWords(hash)]);
|
|
5166
|
+
case "p2tr":
|
|
5167
|
+
return import_base5.bech32m.encode(params.hrp, [1, ...import_base5.bech32m.toWords(hash)]);
|
|
5168
|
+
default:
|
|
5169
|
+
return;
|
|
5170
|
+
}
|
|
5171
|
+
}
|
|
5172
|
+
function toPubkeyBytes(publicKey) {
|
|
5173
|
+
return typeof publicKey === "string" ? hexToBytes(publicKey) : publicKey;
|
|
5174
|
+
}
|
|
5175
|
+
function taprootTweakPubkey(xonly) {
|
|
5176
|
+
if (xonly.length !== 32)
|
|
5177
|
+
throw new Error(`Expected 32-byte x-only pubkey, got ${xonly.length}`);
|
|
5178
|
+
const tweak = import_secp256k13.schnorr.utils.taggedHash("TapTweak", xonly);
|
|
5179
|
+
const t = BigInt(`0x${Array.from(tweak, (b) => b.toString(16).padStart(2, "0")).join("")}`);
|
|
5180
|
+
if (t >= import_secp256k13.secp256k1.Point.Fn.ORDER)
|
|
5181
|
+
throw new Error("Invalid tap tweak (exceeds curve order)");
|
|
5182
|
+
const internal = import_secp256k13.secp256k1.Point.fromBytes(concatBytes(Uint8Array.of(2), xonly));
|
|
5183
|
+
const output = internal.add(import_secp256k13.secp256k1.Point.BASE.multiply(t));
|
|
5184
|
+
return output.toBytes(true).slice(1);
|
|
5185
|
+
}
|
|
5186
|
+
function publicKeyToP2wpkhAddress(publicKey, network = "mainnet") {
|
|
5187
|
+
const pubkey = toPubkeyBytes(publicKey);
|
|
5188
|
+
if (pubkey.length !== 33)
|
|
5189
|
+
throw new Error(`Expected 33-byte compressed pubkey, got ${pubkey.length}`);
|
|
5190
|
+
const params = NETWORK_PARAMS[network];
|
|
5191
|
+
return import_base5.bech32.encode(params.hrp, [0, ...import_base5.bech32.toWords(hash160(pubkey))]);
|
|
5192
|
+
}
|
|
5193
|
+
function publicKeyToP2trAddress(publicKey, network = "mainnet") {
|
|
5194
|
+
const pubkey = toPubkeyBytes(publicKey);
|
|
5195
|
+
const xonly = pubkey.length === 33 ? pubkey.slice(1) : pubkey.length === 32 ? pubkey : undefined;
|
|
5196
|
+
if (!xonly)
|
|
5197
|
+
throw new Error(`Expected 33-byte compressed or 32-byte x-only pubkey, got ${pubkey.length}`);
|
|
5198
|
+
const params = NETWORK_PARAMS[network];
|
|
5199
|
+
const outputKey = taprootTweakPubkey(xonly);
|
|
5200
|
+
return import_base5.bech32m.encode(params.hrp, [1, ...import_base5.bech32m.toWords(outputKey)]);
|
|
5201
|
+
}
|
|
5202
|
+
|
|
5203
|
+
// src/pox5/script.ts
|
|
1608
5204
|
function serializeCScriptNum(n) {
|
|
1609
5205
|
const big = typeof n === "bigint" ? n : BigInt(n);
|
|
1610
5206
|
if (big < 0n)
|
|
@@ -1649,7 +5245,7 @@ function stakerConsensusBuff(stxAddress) {
|
|
|
1649
5245
|
return serializeCVBytes(Cl.principal(stxAddress));
|
|
1650
5246
|
}
|
|
1651
5247
|
function stakerPreimage(stxAddress) {
|
|
1652
|
-
return
|
|
5248
|
+
return import_sha25.sha256(stakerConsensusBuff(stxAddress));
|
|
1653
5249
|
}
|
|
1654
5250
|
function toBytes3(input) {
|
|
1655
5251
|
return typeof input === "string" ? hexToBytes(input) : input;
|
|
@@ -1662,17 +5258,17 @@ function buildLockupScript(opts) {
|
|
|
1662
5258
|
if (height >= BITCOIN_LOCKTIME_THRESHOLD) {
|
|
1663
5259
|
throw new Error("unlockBurnHeight >= 500,000,000 would be read by Bitcoin as a timestamp (contract rejects it)");
|
|
1664
5260
|
}
|
|
1665
|
-
const stakerHash =
|
|
5261
|
+
const stakerHash = import_sha25.sha256(stakerPreimage(opts.stxAddress));
|
|
1666
5262
|
return concatBytes(Uint8Array.of(99), pushCScriptNum(height), Uint8Array.of(177, 103), hexToBytes("82012088a820"), stakerHash, Uint8Array.of(136), toBytes3(opts.earlyUnlockBytes), Uint8Array.of(104, 105), toBytes3(opts.stakerUnlockBytes));
|
|
1667
5263
|
}
|
|
1668
5264
|
function buildLockupOutputScript(opts) {
|
|
1669
|
-
return concatBytes(Uint8Array.of(0, 32),
|
|
5265
|
+
return concatBytes(Uint8Array.of(0, 32), import_sha25.sha256(buildLockupScript(opts)));
|
|
1670
5266
|
}
|
|
1671
5267
|
function buildLockupAddress(opts, network = "mainnet") {
|
|
1672
|
-
const hash =
|
|
1673
|
-
return
|
|
5268
|
+
const hash = import_sha25.sha256(buildLockupScript(opts));
|
|
5269
|
+
return import_base6.bech32.encode(BITCOIN_NETWORK_PARAMS[network].hrp, [
|
|
1674
5270
|
0,
|
|
1675
|
-
...
|
|
5271
|
+
...import_base6.bech32.toWords(hash)
|
|
1676
5272
|
]);
|
|
1677
5273
|
}
|
|
1678
5274
|
function buildDefaultStakerUnlockBytes(publicKey) {
|
|
@@ -1703,7 +5299,45 @@ function buildRegisterMetadata(opts) {
|
|
|
1703
5299
|
};
|
|
1704
5300
|
}
|
|
1705
5301
|
// src/pox5/grants.ts
|
|
1706
|
-
var
|
|
5302
|
+
var import_secp256k14 = require("@noble/curves/secp256k1.js");
|
|
5303
|
+
|
|
5304
|
+
// src/clarity/structuredData.ts
|
|
5305
|
+
var import_sha26 = require("@noble/hashes/sha2.js");
|
|
5306
|
+
var SIP018_PREFIX = new Uint8Array([
|
|
5307
|
+
83,
|
|
5308
|
+
73,
|
|
5309
|
+
80,
|
|
5310
|
+
48,
|
|
5311
|
+
49,
|
|
5312
|
+
56
|
|
5313
|
+
]);
|
|
5314
|
+
function hashStructuredData(structuredData) {
|
|
5315
|
+
return import_sha26.sha256(serializeCVBytes(structuredData));
|
|
5316
|
+
}
|
|
5317
|
+
function isDomain(value) {
|
|
5318
|
+
if (value.type !== "tuple")
|
|
5319
|
+
return false;
|
|
5320
|
+
const { name, version, "chain-id": chainId } = value.value;
|
|
5321
|
+
return name?.type === "ascii" && version?.type === "ascii" && chainId?.type === "uint";
|
|
5322
|
+
}
|
|
5323
|
+
function encodeStructuredData(opts) {
|
|
5324
|
+
if (!isDomain(opts.domain)) {
|
|
5325
|
+
throw new Error("domain must be a tuple { name: ascii, version: ascii, chain-id: uint }");
|
|
5326
|
+
}
|
|
5327
|
+
return concatBytes(SIP018_PREFIX, hashStructuredData(opts.domain), hashStructuredData(opts.message));
|
|
5328
|
+
}
|
|
5329
|
+
function structuredDataHash(opts) {
|
|
5330
|
+
return import_sha26.sha256(encodeStructuredData(opts));
|
|
5331
|
+
}
|
|
5332
|
+
async function signStructuredData(account, opts) {
|
|
5333
|
+
const vrs = await account.sign(structuredDataHash(opts));
|
|
5334
|
+
if (vrs.length !== 65) {
|
|
5335
|
+
throw new Error(`Expected 65-byte recoverable signature, got ${vrs.length}`);
|
|
5336
|
+
}
|
|
5337
|
+
return bytesToHex(concatBytes(vrs.slice(1), vrs.slice(0, 1)));
|
|
5338
|
+
}
|
|
5339
|
+
|
|
5340
|
+
// src/pox5/grants.ts
|
|
1707
5341
|
function computeSignerGrantHash(opts) {
|
|
1708
5342
|
return structuredDataHash({
|
|
1709
5343
|
domain: Cl.tuple({
|
|
@@ -1731,17 +5365,236 @@ function verifySignerGrant(opts) {
|
|
|
1731
5365
|
return false;
|
|
1732
5366
|
const pubkey = typeof opts.publicKey === "string" ? hexToBytes(opts.publicKey) : opts.publicKey;
|
|
1733
5367
|
const hash = computeSignerGrantHash(opts);
|
|
1734
|
-
const recovered =
|
|
5368
|
+
const recovered = import_secp256k14.secp256k1.Signature.fromBytes(sig.slice(0, 64)).addRecoveryBit(sig[64]).recoverPublicKey(hash).toBytes(true);
|
|
1735
5369
|
return bytesToHex(recovered) === bytesToHex(pubkey);
|
|
1736
5370
|
} catch {
|
|
1737
5371
|
return false;
|
|
1738
5372
|
}
|
|
1739
5373
|
}
|
|
1740
5374
|
// src/pox5/lockProof.ts
|
|
1741
|
-
var
|
|
5375
|
+
var import_sha27 = require("@noble/hashes/sha2.js");
|
|
5376
|
+
|
|
5377
|
+
// src/bitcoin/merkle.ts
|
|
5378
|
+
function hashPair(left, right) {
|
|
5379
|
+
return doubleSha256(concatBytes(left, right));
|
|
5380
|
+
}
|
|
5381
|
+
function nextLevel(level) {
|
|
5382
|
+
const next = [];
|
|
5383
|
+
for (let i = 0;i < level.length; i += 2) {
|
|
5384
|
+
const left = level[i];
|
|
5385
|
+
if (left === undefined)
|
|
5386
|
+
throw new Error("merkle: missing left node");
|
|
5387
|
+
const right = level[i + 1] ?? left;
|
|
5388
|
+
next.push(hashPair(left, right));
|
|
5389
|
+
}
|
|
5390
|
+
return next;
|
|
5391
|
+
}
|
|
5392
|
+
function merkleRoot(txidsInternal) {
|
|
5393
|
+
if (txidsInternal.length === 0) {
|
|
5394
|
+
throw new Error("merkleRoot: empty tx list");
|
|
5395
|
+
}
|
|
5396
|
+
let level = txidsInternal.slice();
|
|
5397
|
+
while (level.length > 1) {
|
|
5398
|
+
level = nextLevel(level);
|
|
5399
|
+
}
|
|
5400
|
+
const root = level[0];
|
|
5401
|
+
if (root === undefined)
|
|
5402
|
+
throw new Error("merkleRoot: no root");
|
|
5403
|
+
return root;
|
|
5404
|
+
}
|
|
5405
|
+
function buildMerkleProof(txidsInternal, txIndex) {
|
|
5406
|
+
const txCount = txidsInternal.length;
|
|
5407
|
+
if (txCount === 0) {
|
|
5408
|
+
throw new Error("buildMerkleProof: empty tx list");
|
|
5409
|
+
}
|
|
5410
|
+
if (txIndex < 0 || txIndex >= txCount) {
|
|
5411
|
+
throw new Error(`buildMerkleProof: txIndex ${txIndex} out of range for ${txCount} txs`);
|
|
5412
|
+
}
|
|
5413
|
+
const siblings = [];
|
|
5414
|
+
let index = txIndex;
|
|
5415
|
+
let level = txidsInternal.slice();
|
|
5416
|
+
while (level.length > 1) {
|
|
5417
|
+
const isRight = index % 2 === 1;
|
|
5418
|
+
const siblingIndex = isRight ? index - 1 : index + 1;
|
|
5419
|
+
const self = level[index];
|
|
5420
|
+
if (self === undefined)
|
|
5421
|
+
throw new Error("buildMerkleProof: missing node");
|
|
5422
|
+
siblings.push(level[siblingIndex] ?? self);
|
|
5423
|
+
level = nextLevel(level);
|
|
5424
|
+
index = Math.floor(index / 2);
|
|
5425
|
+
}
|
|
5426
|
+
return { siblings, txIndex, txCount };
|
|
5427
|
+
}
|
|
5428
|
+
function rootFromProof(leafInternal, proof) {
|
|
5429
|
+
let hash = leafInternal;
|
|
5430
|
+
let index = proof.txIndex;
|
|
5431
|
+
for (const sibling of proof.siblings) {
|
|
5432
|
+
hash = index % 2 === 1 ? hashPair(sibling, hash) : hashPair(hash, sibling);
|
|
5433
|
+
index = Math.floor(index / 2);
|
|
5434
|
+
}
|
|
5435
|
+
return hash;
|
|
5436
|
+
}
|
|
5437
|
+
|
|
5438
|
+
// src/bitcoin/proof.ts
|
|
5439
|
+
function normalizeTxid2(txid2) {
|
|
5440
|
+
return without0x(txid2).toLowerCase();
|
|
5441
|
+
}
|
|
5442
|
+
function assembleBlockForTx(args) {
|
|
5443
|
+
const txIndex = args.txidsDisplay.indexOf(normalizeTxid2(args.txid));
|
|
5444
|
+
if (txIndex < 0) {
|
|
5445
|
+
throw new Error(`tx ${args.txid} not found in block ${args.blockId}`);
|
|
5446
|
+
}
|
|
5447
|
+
return {
|
|
5448
|
+
header: hexToBytes(args.headerHex),
|
|
5449
|
+
height: args.height,
|
|
5450
|
+
txidsInternal: args.txidsDisplay.map((t) => reverseBytes(hexToBytes(t))),
|
|
5451
|
+
txIndex
|
|
5452
|
+
};
|
|
5453
|
+
}
|
|
5454
|
+
async function buildTxProof(source, params) {
|
|
5455
|
+
const { txid: txid2, vout } = params;
|
|
5456
|
+
const want = normalizeTxid2(txid2);
|
|
5457
|
+
const [rawTx, block] = await Promise.all([
|
|
5458
|
+
source.getRawTx(txid2),
|
|
5459
|
+
source.getBlockForTx(txid2)
|
|
5460
|
+
]);
|
|
5461
|
+
const parsed = parseBitcoinTx(rawTx);
|
|
5462
|
+
const gotDisplay = bytesToHex(reverseBytes(parsed.txidInternal));
|
|
5463
|
+
if (gotDisplay !== want) {
|
|
5464
|
+
throw new Error(`source returned a tx whose id ${gotDisplay} does not match requested ${want}`);
|
|
5465
|
+
}
|
|
5466
|
+
const atIndex = block.txidsInternal[block.txIndex];
|
|
5467
|
+
if (!atIndex || bytesToHex(atIndex) !== bytesToHex(parsed.txidInternal)) {
|
|
5468
|
+
throw new Error(`source txIndex ${block.txIndex} does not point at tx ${want}`);
|
|
5469
|
+
}
|
|
5470
|
+
const merkle = buildMerkleProof(block.txidsInternal, block.txIndex);
|
|
5471
|
+
const headerRoot = parseBlockHeader(block.header).merkleRoot;
|
|
5472
|
+
const computed = rootFromProof(parsed.txidInternal, merkle);
|
|
5473
|
+
if (bytesToHex(computed) !== bytesToHex(headerRoot)) {
|
|
5474
|
+
throw new Error("constructed merkle proof does not reconcile with the block header merkle root");
|
|
5475
|
+
}
|
|
5476
|
+
return {
|
|
5477
|
+
rawTx,
|
|
5478
|
+
txidInternal: parsed.txidInternal,
|
|
5479
|
+
vout,
|
|
5480
|
+
merkle,
|
|
5481
|
+
header: block.header,
|
|
5482
|
+
height: block.height
|
|
5483
|
+
};
|
|
5484
|
+
}
|
|
5485
|
+
function fallbackProofSource(sources) {
|
|
5486
|
+
if (sources.length === 0) {
|
|
5487
|
+
throw new Error("fallbackProofSource: at least one source is required");
|
|
5488
|
+
}
|
|
5489
|
+
async function firstSuccess(fn) {
|
|
5490
|
+
let lastError;
|
|
5491
|
+
for (const source of sources) {
|
|
5492
|
+
try {
|
|
5493
|
+
return await fn(source);
|
|
5494
|
+
} catch (error) {
|
|
5495
|
+
lastError = error;
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
throw lastError;
|
|
5499
|
+
}
|
|
5500
|
+
return {
|
|
5501
|
+
getRawTx: (txid2) => firstSuccess((s) => s.getRawTx(txid2)),
|
|
5502
|
+
getBlockForTx: (txid2) => firstSuccess((s) => s.getBlockForTx(txid2))
|
|
5503
|
+
};
|
|
5504
|
+
}
|
|
5505
|
+
function bitcoinRpcSource(config) {
|
|
5506
|
+
const doFetch = config.fetch ?? fetch;
|
|
5507
|
+
async function rpc(method, rpcParams) {
|
|
5508
|
+
const headers = {
|
|
5509
|
+
"content-type": "application/json"
|
|
5510
|
+
};
|
|
5511
|
+
if (config.auth) {
|
|
5512
|
+
const basic = typeof config.auth === "string" ? config.auth : btoa(`${config.auth.username}:${config.auth.password}`);
|
|
5513
|
+
headers.authorization = `Basic ${basic}`;
|
|
5514
|
+
}
|
|
5515
|
+
const res = await doFetch(config.url, {
|
|
5516
|
+
method: "POST",
|
|
5517
|
+
headers,
|
|
5518
|
+
body: JSON.stringify({
|
|
5519
|
+
jsonrpc: "1.0",
|
|
5520
|
+
id: "secondlayer",
|
|
5521
|
+
method,
|
|
5522
|
+
params: rpcParams
|
|
5523
|
+
})
|
|
5524
|
+
});
|
|
5525
|
+
if (!res.ok) {
|
|
5526
|
+
throw new Error(`bitcoin rpc ${method} failed: HTTP ${res.status}`);
|
|
5527
|
+
}
|
|
5528
|
+
const json = await res.json();
|
|
5529
|
+
if (json.error) {
|
|
5530
|
+
throw new Error(`bitcoin rpc ${method} error: ${json.error.message}`);
|
|
5531
|
+
}
|
|
5532
|
+
return json.result;
|
|
5533
|
+
}
|
|
5534
|
+
return {
|
|
5535
|
+
async getRawTx(txid2) {
|
|
5536
|
+
return hexToBytes(await rpc("getrawtransaction", [txid2, false]));
|
|
5537
|
+
},
|
|
5538
|
+
async getBlockForTx(txid2) {
|
|
5539
|
+
const tx = await rpc("getrawtransaction", [
|
|
5540
|
+
txid2,
|
|
5541
|
+
true
|
|
5542
|
+
]);
|
|
5543
|
+
if (!tx.blockhash) {
|
|
5544
|
+
throw new Error(`tx ${txid2} is not in a block (node needs -txindex, or the tx is unconfirmed)`);
|
|
5545
|
+
}
|
|
5546
|
+
const block = await rpc("getblock", [
|
|
5547
|
+
tx.blockhash,
|
|
5548
|
+
1
|
|
5549
|
+
]);
|
|
5550
|
+
const header = await rpc("getblockheader", [tx.blockhash, false]);
|
|
5551
|
+
return assembleBlockForTx({
|
|
5552
|
+
txid: txid2,
|
|
5553
|
+
blockId: tx.blockhash,
|
|
5554
|
+
headerHex: header,
|
|
5555
|
+
height: block.height,
|
|
5556
|
+
txidsDisplay: block.tx
|
|
5557
|
+
});
|
|
5558
|
+
}
|
|
5559
|
+
};
|
|
5560
|
+
}
|
|
5561
|
+
function esploraSource(config) {
|
|
5562
|
+
const doFetch = config.fetch ?? fetch;
|
|
5563
|
+
const base = config.url.replace(/\/+$/, "");
|
|
5564
|
+
async function get(path) {
|
|
5565
|
+
const res = await doFetch(`${base}${path}`);
|
|
5566
|
+
if (!res.ok) {
|
|
5567
|
+
throw new Error(`esplora GET ${path} failed: HTTP ${res.status}`);
|
|
5568
|
+
}
|
|
5569
|
+
return res;
|
|
5570
|
+
}
|
|
5571
|
+
return {
|
|
5572
|
+
async getRawTx(txid2) {
|
|
5573
|
+
return hexToBytes((await (await get(`/tx/${txid2}/hex`)).text()).trim());
|
|
5574
|
+
},
|
|
5575
|
+
async getBlockForTx(txid2) {
|
|
5576
|
+
const status = await (await get(`/tx/${txid2}`)).json();
|
|
5577
|
+
if (!status.status.confirmed) {
|
|
5578
|
+
throw new Error(`tx ${txid2} is unconfirmed`);
|
|
5579
|
+
}
|
|
5580
|
+
const blockHashHex = status.status.block_hash;
|
|
5581
|
+
const header = (await (await get(`/block/${blockHashHex}/header`)).text()).trim();
|
|
5582
|
+
const txids = await (await get(`/block/${blockHashHex}/txids`)).json();
|
|
5583
|
+
return assembleBlockForTx({
|
|
5584
|
+
txid: txid2,
|
|
5585
|
+
blockId: blockHashHex,
|
|
5586
|
+
headerHex: header,
|
|
5587
|
+
height: status.status.block_height,
|
|
5588
|
+
txidsDisplay: txids
|
|
5589
|
+
});
|
|
5590
|
+
}
|
|
5591
|
+
};
|
|
5592
|
+
}
|
|
5593
|
+
|
|
5594
|
+
// src/pox5/lockProof.ts
|
|
1742
5595
|
var MAX_LEAF_HASHES2 = 14;
|
|
1743
5596
|
function lockupScriptPubKey(lockScript) {
|
|
1744
|
-
return concatBytes(Uint8Array.of(0, 32),
|
|
5597
|
+
return concatBytes(Uint8Array.of(0, 32), import_sha27.sha256(lockScript));
|
|
1745
5598
|
}
|
|
1746
5599
|
function scriptsEqual(a, b) {
|
|
1747
5600
|
return bytesToHex(a) === bytesToHex(b);
|
|
@@ -1800,9 +5653,9 @@ async function buildPox5LockProof(opts) {
|
|
|
1800
5653
|
});
|
|
1801
5654
|
}
|
|
1802
5655
|
// src/pox5/reclaim.ts
|
|
1803
|
-
var
|
|
5656
|
+
var import_sha28 = require("@noble/hashes/sha2.js");
|
|
1804
5657
|
var btc = __toESM(require("@scure/btc-signer"));
|
|
1805
|
-
var
|
|
5658
|
+
var import_utils5 = require("@scure/btc-signer/utils.js");
|
|
1806
5659
|
var SIGHASH_ALL = 1;
|
|
1807
5660
|
var DUST_LIMIT_SATS = 546n;
|
|
1808
5661
|
var ELSE_SELECTOR = new Uint8Array(0);
|
|
@@ -1830,7 +5683,7 @@ function bytesEqual(a, b) {
|
|
|
1830
5683
|
return bytesToHex(a) === bytesToHex(b);
|
|
1831
5684
|
}
|
|
1832
5685
|
function scriptToWshOutput(lockScript) {
|
|
1833
|
-
return concatBytes(Uint8Array.of(0, 32),
|
|
5686
|
+
return concatBytes(Uint8Array.of(0, 32), import_sha28.sha256(lockScript));
|
|
1834
5687
|
}
|
|
1835
5688
|
var SCAFFOLD_PREFIX_LEN = 10;
|
|
1836
5689
|
function decodeLockScript(script) {
|
|
@@ -1898,7 +5751,7 @@ function computeReclaimSighash(tx, opts) {
|
|
|
1898
5751
|
}
|
|
1899
5752
|
function signReclaim(sighash, privateKey, opts) {
|
|
1900
5753
|
const priv = toPrivBytes(privateKey);
|
|
1901
|
-
return concatBytes(
|
|
5754
|
+
return concatBytes(import_utils5.signECDSA(sighash, priv, opts?.lowR ?? false), Uint8Array.of(SIGHASH_ALL));
|
|
1902
5755
|
}
|
|
1903
5756
|
function finalizeReclaim(opts) {
|
|
1904
5757
|
const { tx } = opts;
|
|
@@ -1963,6 +5816,48 @@ function reclaim(opts) {
|
|
|
1963
5816
|
}
|
|
1964
5817
|
return finalizeReclaim({ path: "locktime", tx });
|
|
1965
5818
|
}
|
|
5819
|
+
// src/actions/public/multicall.ts
|
|
5820
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
5821
|
+
const results = new Array(items.length);
|
|
5822
|
+
let cursor = 0;
|
|
5823
|
+
const worker = async () => {
|
|
5824
|
+
while (cursor < items.length) {
|
|
5825
|
+
const index = cursor++;
|
|
5826
|
+
try {
|
|
5827
|
+
results[index] = {
|
|
5828
|
+
status: "fulfilled",
|
|
5829
|
+
value: await fn(items[index])
|
|
5830
|
+
};
|
|
5831
|
+
} catch (reason) {
|
|
5832
|
+
results[index] = { status: "rejected", reason };
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
5835
|
+
};
|
|
5836
|
+
const workers = Math.max(1, Math.min(limit, items.length));
|
|
5837
|
+
await Promise.all(Array.from({ length: workers }, worker));
|
|
5838
|
+
return results;
|
|
5839
|
+
}
|
|
5840
|
+
async function multicall(client, params) {
|
|
5841
|
+
const { calls, allowFailure = true, concurrency = 8 } = params;
|
|
5842
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
5843
|
+
throw new RangeError(`multicall concurrency must be a positive integer, got ${concurrency}`);
|
|
5844
|
+
}
|
|
5845
|
+
const settled = await mapWithConcurrency(calls, concurrency, (call) => readContract(client, call));
|
|
5846
|
+
if (allowFailure) {
|
|
5847
|
+
return settled.map((r) => r.status === "fulfilled" ? { status: "success", result: r.value } : {
|
|
5848
|
+
status: "failure",
|
|
5849
|
+
error: r.reason instanceof Error ? r.reason : new Error(String(r.reason))
|
|
5850
|
+
});
|
|
5851
|
+
}
|
|
5852
|
+
const results = [];
|
|
5853
|
+
for (const r of settled) {
|
|
5854
|
+
if (r.status === "rejected")
|
|
5855
|
+
throw r.reason;
|
|
5856
|
+
results.push(r.value);
|
|
5857
|
+
}
|
|
5858
|
+
return results;
|
|
5859
|
+
}
|
|
5860
|
+
|
|
1966
5861
|
// src/pox5/extension.ts
|
|
1967
5862
|
async function getStakerState(client, staker) {
|
|
1968
5863
|
const contract = pox5ContractId(client);
|
|
@@ -2047,6 +5942,136 @@ function pox5() {
|
|
|
2047
5942
|
}
|
|
2048
5943
|
});
|
|
2049
5944
|
}
|
|
5945
|
+
// src/pox5/btcAddress.ts
|
|
5946
|
+
var import_base7 = require("@scure/base");
|
|
5947
|
+
|
|
5948
|
+
// src/pox/constants.ts
|
|
5949
|
+
var POX_CONTRACTS = {
|
|
5950
|
+
mainnet: {
|
|
5951
|
+
address: "SP000000000000000000002Q6VF78",
|
|
5952
|
+
name: "pox-4"
|
|
5953
|
+
},
|
|
5954
|
+
testnet: {
|
|
5955
|
+
address: "ST000000000000000000002AMW42H",
|
|
5956
|
+
name: "pox-4"
|
|
5957
|
+
}
|
|
5958
|
+
};
|
|
5959
|
+
var MIN_LOCK_PERIOD = 1;
|
|
5960
|
+
var MAX_LOCK_PERIOD = 12;
|
|
5961
|
+
var POX_ADDRESS_VERSION = {
|
|
5962
|
+
p2pkh: 0,
|
|
5963
|
+
p2sh: 1,
|
|
5964
|
+
p2sh_p2wpkh: 2,
|
|
5965
|
+
p2sh_p2wsh: 3,
|
|
5966
|
+
p2wpkh: 4,
|
|
5967
|
+
p2wsh: 5,
|
|
5968
|
+
p2tr: 6
|
|
5969
|
+
};
|
|
5970
|
+
|
|
5971
|
+
// src/pox5/btcAddress.ts
|
|
5972
|
+
function expectedHashLen(version) {
|
|
5973
|
+
if (!Number.isInteger(version) || version < 0 || version > 6) {
|
|
5974
|
+
throw new Error(`Unknown PoX address version: 0x${version.toString(16)}`);
|
|
5975
|
+
}
|
|
5976
|
+
return version <= 4 ? 20 : 32;
|
|
5977
|
+
}
|
|
5978
|
+
function canonicalizeBtcAddress(repr) {
|
|
5979
|
+
const expected = expectedHashLen(repr.version);
|
|
5980
|
+
const { hashbytes } = repr;
|
|
5981
|
+
if (hashbytes.length === expected)
|
|
5982
|
+
return { version: repr.version, hashbytes };
|
|
5983
|
+
if (expected === 20 && hashbytes.length === 32 && hashbytes.slice(20).every((b) => b === 0)) {
|
|
5984
|
+
return { version: repr.version, hashbytes: hashbytes.slice(0, 20) };
|
|
5985
|
+
}
|
|
5986
|
+
throw new Error(`Invalid hashbytes length for PoX version 0x${repr.version.toString(16)}: ${hashbytes.length}`);
|
|
5987
|
+
}
|
|
5988
|
+
function parseBtcAddress(address) {
|
|
5989
|
+
if (address.startsWith("bc1") || address.startsWith("tb1") || address.startsWith("bcrt1")) {
|
|
5990
|
+
return parseSegwitAddress(address);
|
|
5991
|
+
}
|
|
5992
|
+
return parseLegacyAddress(address);
|
|
5993
|
+
}
|
|
5994
|
+
function parseSegwitAddress(address) {
|
|
5995
|
+
let decoded;
|
|
5996
|
+
try {
|
|
5997
|
+
decoded = import_base7.bech32.decode(address);
|
|
5998
|
+
} catch {
|
|
5999
|
+
decoded = import_base7.bech32m.decode(address);
|
|
6000
|
+
}
|
|
6001
|
+
const witnessVersion = decoded.words[0];
|
|
6002
|
+
if (witnessVersion === undefined) {
|
|
6003
|
+
throw new Error("Invalid segwit address: missing witness version");
|
|
6004
|
+
}
|
|
6005
|
+
const data = import_base7.bech32.fromWords(decoded.words.slice(1));
|
|
6006
|
+
const hashbytes = new Uint8Array(data);
|
|
6007
|
+
let version;
|
|
6008
|
+
if (witnessVersion === 0) {
|
|
6009
|
+
if (hashbytes.length === 20) {
|
|
6010
|
+
version = POX_ADDRESS_VERSION.p2wpkh;
|
|
6011
|
+
} else if (hashbytes.length === 32) {
|
|
6012
|
+
version = POX_ADDRESS_VERSION.p2wsh;
|
|
6013
|
+
} else {
|
|
6014
|
+
throw new Error(`Invalid witness v0 program length: ${hashbytes.length}`);
|
|
6015
|
+
}
|
|
6016
|
+
} else if (witnessVersion === 1) {
|
|
6017
|
+
if (hashbytes.length !== 32) {
|
|
6018
|
+
throw new Error(`Invalid witness v1 program length: ${hashbytes.length}`);
|
|
6019
|
+
}
|
|
6020
|
+
version = POX_ADDRESS_VERSION.p2tr;
|
|
6021
|
+
} else {
|
|
6022
|
+
throw new Error(`Unsupported witness version: ${witnessVersion}`);
|
|
6023
|
+
}
|
|
6024
|
+
return { version, hashbytes };
|
|
6025
|
+
}
|
|
6026
|
+
function parseLegacyAddress(address) {
|
|
6027
|
+
const decoded = import_base7.base58.decode(address);
|
|
6028
|
+
if (decoded.length !== 25) {
|
|
6029
|
+
throw new Error(`Invalid legacy address length: ${decoded.length}`);
|
|
6030
|
+
}
|
|
6031
|
+
const versionByte = decoded[0];
|
|
6032
|
+
if (versionByte === undefined) {
|
|
6033
|
+
throw new Error("Invalid legacy address: missing version byte");
|
|
6034
|
+
}
|
|
6035
|
+
const payload = decoded.slice(0, 21);
|
|
6036
|
+
const checksum = decoded.slice(21);
|
|
6037
|
+
const expected = doubleSha256(payload).slice(0, 4);
|
|
6038
|
+
if (checksum.length !== 4 || expected.some((b, i) => b !== checksum[i])) {
|
|
6039
|
+
throw new Error("Invalid legacy address checksum");
|
|
6040
|
+
}
|
|
6041
|
+
const hashbytes = decoded.slice(1, 21);
|
|
6042
|
+
let version;
|
|
6043
|
+
if (versionByte === 0 || versionByte === 111) {
|
|
6044
|
+
version = POX_ADDRESS_VERSION.p2pkh;
|
|
6045
|
+
} else if (versionByte === 5 || versionByte === 196) {
|
|
6046
|
+
version = POX_ADDRESS_VERSION.p2sh;
|
|
6047
|
+
} else {
|
|
6048
|
+
throw new Error(`Unknown legacy address version byte: 0x${versionByte.toString(16)}`);
|
|
6049
|
+
}
|
|
6050
|
+
return { version, hashbytes };
|
|
6051
|
+
}
|
|
6052
|
+
function stringifyBtcAddress(repr, network) {
|
|
6053
|
+
const { version, hashbytes } = canonicalizeBtcAddress(repr);
|
|
6054
|
+
const params = BITCOIN_NETWORK_PARAMS[network];
|
|
6055
|
+
switch (version) {
|
|
6056
|
+
case POX_ADDRESS_VERSION.p2pkh:
|
|
6057
|
+
return base58CheckEncode(params.p2pkh, hashbytes);
|
|
6058
|
+
case POX_ADDRESS_VERSION.p2sh:
|
|
6059
|
+
case POX_ADDRESS_VERSION.p2sh_p2wpkh:
|
|
6060
|
+
case POX_ADDRESS_VERSION.p2sh_p2wsh:
|
|
6061
|
+
return base58CheckEncode(params.p2sh, hashbytes);
|
|
6062
|
+
case POX_ADDRESS_VERSION.p2wpkh:
|
|
6063
|
+
case POX_ADDRESS_VERSION.p2wsh:
|
|
6064
|
+
return import_base7.bech32.encode(params.hrp, [0, ...import_base7.bech32.toWords(hashbytes)]);
|
|
6065
|
+
case POX_ADDRESS_VERSION.p2tr:
|
|
6066
|
+
return import_base7.bech32m.encode(params.hrp, [1, ...import_base7.bech32m.toWords(hashbytes)]);
|
|
6067
|
+
default:
|
|
6068
|
+
throw new Error(`Unknown PoX address version: 0x${version.toString(16)}`);
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
var BtcAddress = {
|
|
6072
|
+
parse: parseBtcAddress,
|
|
6073
|
+
stringify: stringifyBtcAddress
|
|
6074
|
+
};
|
|
2050
6075
|
// src/pox5/signerCalldata.ts
|
|
2051
6076
|
function buildSignerCalldata(opts) {
|
|
2052
6077
|
const repr = canonicalizeBtcAddress(typeof opts.poxAddress === "string" ? parseBtcAddress(opts.poxAddress) : opts.poxAddress);
|
|
@@ -2096,5 +6121,5 @@ function parseSignerCalldata(calldata) {
|
|
|
2096
6121
|
};
|
|
2097
6122
|
}
|
|
2098
6123
|
|
|
2099
|
-
//# debugId=
|
|
6124
|
+
//# debugId=9618E4E75187B7DD64756E2164756E21
|
|
2100
6125
|
//# sourceMappingURL=index.cjs.map
|