@secondlayer/stacks 4.1.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/shared/stacks-03vfm21t.cjs +222 -0
- package/dist/shared/stacks-03vfm21t.cjs.map +12 -0
- package/dist/shared/stacks-0eatsx6d.cjs +25 -0
- package/dist/shared/stacks-0eatsx6d.cjs.map +10 -0
- package/dist/shared/stacks-1arrcd11.js.map +2 -2
- package/dist/shared/{stacks-mrk6tkkh.cjs → stacks-3mt2q2na.cjs} +26 -2
- package/dist/shared/{stacks-mrk6tkkh.cjs.map → stacks-3mt2q2na.cjs.map} +1 -1
- package/dist/shared/{stacks-bw8qvqd1.js → stacks-7wkf2rph.js} +3 -15
- package/dist/shared/{stacks-bw8qvqd1.js.map → stacks-7wkf2rph.js.map} +3 -4
- package/dist/shared/stacks-8pfdscq6.js +52 -0
- package/dist/shared/stacks-8pfdscq6.js.map +12 -0
- package/dist/shared/stacks-e4xn4m7j.js +27 -0
- package/dist/shared/stacks-e4xn4m7j.js.map +10 -0
- package/dist/shared/stacks-enpj9krt.cjs.map +2 -2
- package/dist/shared/{stacks-9qcr921s.cjs → stacks-kem68d80.cjs} +3 -3
- package/dist/shared/stacks-kem68d80.cjs.map +10 -0
- package/dist/shared/stacks-wdp5yr8n.cjs +39 -0
- package/dist/shared/stacks-wdp5yr8n.cjs.map +12 -0
- package/dist/shared/{stacks-y4f7fm03.cjs → stacks-ycbq8pf7.cjs} +2 -14
- package/dist/shared/{stacks-y4f7fm03.cjs.map → stacks-ycbq8pf7.cjs.map} +3 -4
- package/dist/shared/{stacks-rpqtgpyj.js → stacks-zcpvzbr0.js} +3 -3
- package/dist/shared/stacks-zcpvzbr0.js.map +10 -0
- package/dist/shared/stacks-ztzygy90.js +237 -0
- package/dist/shared/stacks-ztzygy90.js.map +12 -0
- package/dist/src/actions/index.cjs +2 -1
- package/dist/src/actions/index.cjs.map +1 -1
- package/dist/src/actions/index.d.cts +6 -1
- package/dist/src/actions/index.d.ts +6 -1
- package/dist/src/actions/index.js +6 -4
- package/dist/src/actions/index.js.map +1 -1
- package/dist/src/bitcoin/index.cjs +1 -217
- package/dist/src/bitcoin/index.cjs.map +3 -5
- package/dist/src/bitcoin/index.js +11 -223
- package/dist/src/bitcoin/index.js.map +3 -5
- package/dist/src/bns/index.js +1 -1
- package/dist/src/clarity/index.d.cts +1 -1
- package/dist/src/clarity/index.d.ts +1 -1
- package/dist/src/index.cjs +2 -1
- package/dist/src/index.cjs.map +3 -3
- package/dist/src/index.d.cts +5 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +6 -4
- package/dist/src/index.js.map +3 -3
- package/dist/src/postconditions/index.d.cts +3 -2
- package/dist/src/postconditions/index.d.ts +3 -2
- package/dist/src/postconditions/index.js +1 -1
- package/dist/src/pox/index.cjs +1 -24
- package/dist/src/pox/index.cjs.map +3 -4
- package/dist/src/pox/index.js +8 -25
- package/dist/src/pox/index.js.map +3 -4
- package/dist/src/pox5/index.cjs +1118 -204
- package/dist/src/pox5/index.cjs.map +13 -8
- package/dist/src/pox5/index.d.cts +618 -162
- package/dist/src/pox5/index.d.ts +618 -162
- package/dist/src/pox5/index.js +1136 -208
- package/dist/src/pox5/index.js.map +13 -8
- package/dist/src/stackingdao/index.js +1 -1
- package/dist/src/tools/index.cjs +13 -1
- package/dist/src/tools/index.cjs.map +6 -5
- package/dist/src/tools/index.js +13 -1
- package/dist/src/tools/index.js.map +6 -5
- package/package.json +4 -3
- package/dist/shared/stacks-2evvn48s.js +0 -22
- package/dist/shared/stacks-2evvn48s.js.map +0 -10
- package/dist/shared/stacks-3kj51c6p.cjs +0 -16
- package/dist/shared/stacks-3kj51c6p.cjs.map +0 -10
- package/dist/shared/stacks-9qcr921s.cjs.map +0 -10
- package/dist/shared/stacks-rdgg7mj3.js +0 -7
- package/dist/shared/stacks-rdgg7mj3.js.map +0 -10
- package/dist/shared/stacks-rpqtgpyj.js.map +0 -10
- package/dist/shared/stacks-wscmfs7q.cjs +0 -5
- package/dist/shared/stacks-wscmfs7q.cjs.map +0 -10
package/dist/src/pox5/index.cjs
CHANGED
|
@@ -2,29 +2,53 @@
|
|
|
2
2
|
var exports_pox5 = {};
|
|
3
3
|
__export(exports_pox5, {
|
|
4
4
|
verifySignerGrant: () => verifySignerGrant,
|
|
5
|
+
stringifyBtcAddress: () => stringifyBtcAddress,
|
|
5
6
|
stakerPreimage: () => stakerPreimage,
|
|
6
7
|
stakerConsensusBuff: () => stakerConsensusBuff,
|
|
8
|
+
spvProofToL1LockupOutput: () => spvProofToL1LockupOutput,
|
|
7
9
|
signSignerGrant: () => signSignerGrant,
|
|
10
|
+
signReclaim: () => signReclaim,
|
|
8
11
|
serializeCScriptNum: () => serializeCScriptNum,
|
|
9
12
|
rewardCycleToBurnHeight: () => rewardCycleToBurnHeight,
|
|
13
|
+
reclaim: () => reclaim,
|
|
10
14
|
pushScriptBytes: () => pushScriptBytes,
|
|
11
15
|
pushCScriptNum: () => pushCScriptNum,
|
|
12
16
|
pox5ContractId: () => pox5ContractId,
|
|
13
17
|
pox5: () => pox5,
|
|
18
|
+
parseSignerCalldata: () => parseSignerCalldata,
|
|
14
19
|
parsePox5Error: () => parsePox5Error,
|
|
20
|
+
parseBtcAddress: () => parseBtcAddress,
|
|
15
21
|
isPox5Active: () => isPox5Active,
|
|
16
22
|
isInPreparePhase: () => isInPreparePhase,
|
|
17
23
|
getPoxInfo: () => getPoxInfo,
|
|
18
24
|
getPox5Activation: () => getPox5Activation,
|
|
25
|
+
finalizeReclaim: () => finalizeReclaim,
|
|
26
|
+
eligibleUnstakeSbtc: () => eligibleUnstakeSbtc,
|
|
27
|
+
eligibleUnstake: () => eligibleUnstake,
|
|
28
|
+
eligibleStake: () => eligibleStake,
|
|
29
|
+
eligibleSetBondAdmin: () => eligibleSetBondAdmin,
|
|
30
|
+
eligibleRegisterForBond: () => eligibleRegisterForBond,
|
|
31
|
+
eligiblePauseRewards: () => eligiblePauseRewards,
|
|
32
|
+
eligibleGrantSignerKey: () => eligibleGrantSignerKey,
|
|
33
|
+
eligibleClaimRewards: () => eligibleClaimRewards,
|
|
34
|
+
distributionCycleToBurnHeight: () => distributionCycleToBurnHeight,
|
|
19
35
|
describePox5Error: () => describePox5Error,
|
|
36
|
+
currentDistributionCycle: () => currentDistributionCycle,
|
|
20
37
|
computeSignerGrantHash: () => computeSignerGrantHash,
|
|
38
|
+
computeReclaimSighash: () => computeReclaimSighash,
|
|
39
|
+
computeBondUnlockHeight: () => computeBondUnlockHeight,
|
|
21
40
|
burnHeightToRewardCycle: () => burnHeightToRewardCycle,
|
|
22
41
|
burnHeightToDistributionIndex: () => burnHeightToDistributionIndex,
|
|
42
|
+
buildSignerCalldata: () => buildSignerCalldata,
|
|
43
|
+
buildRegisterMetadata: () => buildRegisterMetadata,
|
|
44
|
+
buildReclaim: () => buildReclaim,
|
|
45
|
+
buildPox5LockProof: () => buildPox5LockProof,
|
|
23
46
|
buildLockupScript: () => buildLockupScript,
|
|
24
47
|
buildLockupOutputScript: () => buildLockupOutputScript,
|
|
25
48
|
buildLockupAddress: () => buildLockupAddress,
|
|
26
49
|
buildDefaultStakerUnlockBytes: () => buildDefaultStakerUnlockBytes,
|
|
27
50
|
bondUnlockCycle: () => bondUnlockCycle,
|
|
51
|
+
bondStatusAtHeight: () => bondStatusAtHeight,
|
|
28
52
|
bondPhaseAtHeight: () => bondPhaseAtHeight,
|
|
29
53
|
bondPeriodToRewardCycle: () => bondPeriodToRewardCycle,
|
|
30
54
|
bondPeriodToBurnHeight: () => bondPeriodToBurnHeight,
|
|
@@ -39,6 +63,7 @@ __export(exports_pox5, {
|
|
|
39
63
|
POX5_ABI: () => POX5_ABI,
|
|
40
64
|
MAX_NUM_CYCLES: () => MAX_NUM_CYCLES,
|
|
41
65
|
C_SCRIPT_NUM_MAX: () => C_SCRIPT_NUM_MAX,
|
|
66
|
+
BtcAddress: () => BtcAddress,
|
|
42
67
|
BOND_LENGTH_CYCLES: () => BOND_LENGTH_CYCLES,
|
|
43
68
|
BOND_GAP_CYCLES: () => BOND_GAP_CYCLES,
|
|
44
69
|
BITCOIN_LOCKTIME_THRESHOLD: () => BITCOIN_LOCKTIME_THRESHOLD
|
|
@@ -288,154 +313,6 @@ function describePox5Error(code) {
|
|
|
288
313
|
return;
|
|
289
314
|
return { code: n, name, description: POX5_ERROR_DESCRIPTIONS[n] };
|
|
290
315
|
}
|
|
291
|
-
// src/pox5/cycles.ts
|
|
292
|
-
function burnHeightToRewardCycle(burnHeight, params) {
|
|
293
|
-
if (burnHeight < params.firstBurnchainBlockHeight) {
|
|
294
|
-
throw new Error(`burnHeight ${burnHeight} precedes first burnchain block ${params.firstBurnchainBlockHeight}`);
|
|
295
|
-
}
|
|
296
|
-
return Math.floor((burnHeight - params.firstBurnchainBlockHeight) / params.rewardCycleLength);
|
|
297
|
-
}
|
|
298
|
-
function rewardCycleToBurnHeight(cycle, params) {
|
|
299
|
-
return params.firstBurnchainBlockHeight + cycle * params.rewardCycleLength;
|
|
300
|
-
}
|
|
301
|
-
function bondPeriodToRewardCycle(bondIndex, params) {
|
|
302
|
-
return params.firstBondPeriodCycle + bondIndex * BOND_GAP_CYCLES;
|
|
303
|
-
}
|
|
304
|
-
function bondPeriodToBurnHeight(bondIndex, params) {
|
|
305
|
-
return rewardCycleToBurnHeight(bondPeriodToRewardCycle(bondIndex, params), params);
|
|
306
|
-
}
|
|
307
|
-
function bondUnlockCycle(bondIndex, params) {
|
|
308
|
-
return bondPeriodToRewardCycle(bondIndex, params) + BOND_LENGTH_CYCLES;
|
|
309
|
-
}
|
|
310
|
-
function burnHeightToDistributionIndex(burnHeight, params) {
|
|
311
|
-
return Math.floor((burnHeight - params.firstBurnchainBlockHeight) / Math.floor(params.rewardCycleLength / 2));
|
|
312
|
-
}
|
|
313
|
-
function isInPreparePhase(burnHeight, params) {
|
|
314
|
-
const offset = (burnHeight - params.firstBurnchainBlockHeight) % params.rewardCycleLength;
|
|
315
|
-
return offset >= params.rewardCycleLength - params.prepareCycleLength;
|
|
316
|
-
}
|
|
317
|
-
function bondPhaseAtHeight(bondIndex, burnHeight, params) {
|
|
318
|
-
const cycle = burnHeightToRewardCycle(burnHeight, params);
|
|
319
|
-
const start = bondPeriodToRewardCycle(bondIndex, params);
|
|
320
|
-
const unlock = bondUnlockCycle(bondIndex, params);
|
|
321
|
-
if (cycle < start - BOND_GAP_CYCLES)
|
|
322
|
-
return "too-early";
|
|
323
|
-
if (cycle < start)
|
|
324
|
-
return "open";
|
|
325
|
-
if (cycle < unlock)
|
|
326
|
-
return "locked";
|
|
327
|
-
return "unlocked";
|
|
328
|
-
}
|
|
329
|
-
// src/pox5/script.ts
|
|
330
|
-
var import_sha2 = require("@noble/hashes/sha2.js");
|
|
331
|
-
function serializeCScriptNum(n) {
|
|
332
|
-
const big = typeof n === "bigint" ? n : BigInt(n);
|
|
333
|
-
if (big < 0n)
|
|
334
|
-
throw new Error("serializeCScriptNum: negative value");
|
|
335
|
-
if (big >= C_SCRIPT_NUM_MAX) {
|
|
336
|
-
throw new Error("serializeCScriptNum: n >= 2^39 (contract rejects with ERR_INVALID_UNLOCK_HEIGHT)");
|
|
337
|
-
}
|
|
338
|
-
if (big === 0n)
|
|
339
|
-
return new Uint8Array(0);
|
|
340
|
-
const out = [];
|
|
341
|
-
let v = big;
|
|
342
|
-
while (v > 0n) {
|
|
343
|
-
out.push(Number(v & 0xffn));
|
|
344
|
-
v >>= 8n;
|
|
345
|
-
}
|
|
346
|
-
if (out[out.length - 1] & 128)
|
|
347
|
-
out.push(0);
|
|
348
|
-
return Uint8Array.from(out);
|
|
349
|
-
}
|
|
350
|
-
function pushScriptBytes(bytes) {
|
|
351
|
-
const len = bytes.length;
|
|
352
|
-
if (len > 65535)
|
|
353
|
-
throw new Error(`pushScriptBytes: payload too large (${len} > 65535)`);
|
|
354
|
-
let prefix;
|
|
355
|
-
if (len < 76)
|
|
356
|
-
prefix = Uint8Array.of(len);
|
|
357
|
-
else if (len < 256)
|
|
358
|
-
prefix = Uint8Array.of(76, len);
|
|
359
|
-
else
|
|
360
|
-
prefix = Uint8Array.of(77, len & 255, len >> 8);
|
|
361
|
-
return concatBytes(prefix, bytes);
|
|
362
|
-
}
|
|
363
|
-
function pushCScriptNum(n) {
|
|
364
|
-
const big = typeof n === "bigint" ? n : BigInt(n);
|
|
365
|
-
if (big === 0n)
|
|
366
|
-
return Uint8Array.of(0);
|
|
367
|
-
if (big >= 1n && big <= 16n)
|
|
368
|
-
return Uint8Array.of(80 + Number(big));
|
|
369
|
-
return pushScriptBytes(serializeCScriptNum(big));
|
|
370
|
-
}
|
|
371
|
-
function stakerConsensusBuff(stxAddress) {
|
|
372
|
-
return serializeCVBytes(Cl.principal(stxAddress));
|
|
373
|
-
}
|
|
374
|
-
function stakerPreimage(stxAddress) {
|
|
375
|
-
return import_sha2.sha256(stakerConsensusBuff(stxAddress));
|
|
376
|
-
}
|
|
377
|
-
function toBytes(input) {
|
|
378
|
-
return typeof input === "string" ? hexToBytes(input) : input;
|
|
379
|
-
}
|
|
380
|
-
function buildLockupScript(opts) {
|
|
381
|
-
const height = BigInt(opts.unlockBurnHeight);
|
|
382
|
-
if (height >= BITCOIN_LOCKTIME_THRESHOLD) {
|
|
383
|
-
throw new Error("unlockBurnHeight >= 500,000,000 would be read by Bitcoin as a timestamp (contract rejects it)");
|
|
384
|
-
}
|
|
385
|
-
const stakerHash = import_sha2.sha256(stakerPreimage(opts.stxAddress));
|
|
386
|
-
return concatBytes(Uint8Array.of(99), pushCScriptNum(height), Uint8Array.of(177, 103), hexToBytes("82012088a820"), stakerHash, Uint8Array.of(136), toBytes(opts.earlyUnlockBytes), Uint8Array.of(104, 105), toBytes(opts.stakerUnlockBytes));
|
|
387
|
-
}
|
|
388
|
-
function buildLockupOutputScript(opts) {
|
|
389
|
-
return concatBytes(Uint8Array.of(0, 32), import_sha2.sha256(buildLockupScript(opts)));
|
|
390
|
-
}
|
|
391
|
-
function buildLockupAddress(opts, network = "mainnet") {
|
|
392
|
-
const hash = import_sha2.sha256(buildLockupScript(opts));
|
|
393
|
-
return bech32.encode(BITCOIN_NETWORK_PARAMS[network].hrp, [
|
|
394
|
-
0,
|
|
395
|
-
...bech32.toWords(hash)
|
|
396
|
-
]);
|
|
397
|
-
}
|
|
398
|
-
function buildDefaultStakerUnlockBytes(publicKey) {
|
|
399
|
-
const pubkey = toBytes(publicKey);
|
|
400
|
-
if (pubkey.length !== 33)
|
|
401
|
-
throw new Error(`Expected 33-byte compressed pubkey, got ${pubkey.length}`);
|
|
402
|
-
return concatBytes(pushScriptBytes(pubkey), Uint8Array.of(172));
|
|
403
|
-
}
|
|
404
|
-
// src/pox5/grants.ts
|
|
405
|
-
var import_secp256k1 = require("@noble/curves/secp256k1.js");
|
|
406
|
-
function computeSignerGrantHash(opts) {
|
|
407
|
-
return structuredDataHash({
|
|
408
|
-
domain: Cl.tuple({
|
|
409
|
-
name: Cl.stringAscii(POX5_SIGNER_DOMAIN.name),
|
|
410
|
-
version: Cl.stringAscii(POX5_SIGNER_DOMAIN.version),
|
|
411
|
-
"chain-id": Cl.uint(opts.chainId)
|
|
412
|
-
}),
|
|
413
|
-
message: Cl.tuple({
|
|
414
|
-
topic: Cl.stringAscii("grant-authorization"),
|
|
415
|
-
"signer-manager": Cl.principal(opts.signerManager),
|
|
416
|
-
"auth-id": Cl.uint(opts.authId)
|
|
417
|
-
})
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
async function signSignerGrant(account, opts) {
|
|
421
|
-
const vrs = await account.sign(computeSignerGrantHash(opts));
|
|
422
|
-
if (vrs.length !== 65)
|
|
423
|
-
throw new Error(`Expected 65-byte recoverable signature, got ${vrs.length}`);
|
|
424
|
-
return bytesToHex(concatBytes(vrs.slice(1), vrs.slice(0, 1)));
|
|
425
|
-
}
|
|
426
|
-
function verifySignerGrant(opts) {
|
|
427
|
-
try {
|
|
428
|
-
const sig = typeof opts.signature === "string" ? hexToBytes(opts.signature) : opts.signature;
|
|
429
|
-
if (sig.length !== 65)
|
|
430
|
-
return false;
|
|
431
|
-
const pubkey = typeof opts.publicKey === "string" ? hexToBytes(opts.publicKey) : opts.publicKey;
|
|
432
|
-
const hash = computeSignerGrantHash(opts);
|
|
433
|
-
const recovered = import_secp256k1.secp256k1.Signature.fromBytes(sig.slice(0, 64)).addRecoveryBit(sig[64]).recoverPublicKey(hash).toBytes(true);
|
|
434
|
-
return bytesToHex(recovered) === bytesToHex(pubkey);
|
|
435
|
-
} catch {
|
|
436
|
-
return false;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
316
|
// src/pox5/abi.ts
|
|
440
317
|
var POX5_ABI = {
|
|
441
318
|
functions: [
|
|
@@ -548,6 +425,42 @@ var POX5_ABI = {
|
|
|
548
425
|
],
|
|
549
426
|
outputs: { response: { ok: "bool", error: "uint128" } }
|
|
550
427
|
},
|
|
428
|
+
{
|
|
429
|
+
name: "get-earned",
|
|
430
|
+
access: "read-only",
|
|
431
|
+
args: [
|
|
432
|
+
{ name: "signer", type: "principal" },
|
|
433
|
+
{ name: "reward-cycle", type: "uint128" },
|
|
434
|
+
{ name: "bond-index", type: { optional: "uint128" } }
|
|
435
|
+
],
|
|
436
|
+
outputs: "uint128"
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
name: "get-earned-staker-rewards",
|
|
440
|
+
access: "read-only",
|
|
441
|
+
args: [
|
|
442
|
+
{ name: "signer", type: "principal" },
|
|
443
|
+
{ name: "reward-cycle", type: "uint128" },
|
|
444
|
+
{ name: "bond-index", type: { optional: "uint128" } },
|
|
445
|
+
{ name: "staker", type: "principal" }
|
|
446
|
+
],
|
|
447
|
+
outputs: "uint128"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: "get-last-reward-compute-height",
|
|
451
|
+
access: "read-only",
|
|
452
|
+
args: [],
|
|
453
|
+
outputs: "uint128"
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: "get-total-shares-staked-for-cycle",
|
|
457
|
+
access: "read-only",
|
|
458
|
+
args: [
|
|
459
|
+
{ name: "reward-cycle", type: "uint128" },
|
|
460
|
+
{ name: "bond-index", type: { optional: "uint128" } }
|
|
461
|
+
],
|
|
462
|
+
outputs: "uint128"
|
|
463
|
+
},
|
|
551
464
|
{
|
|
552
465
|
name: "announce-l1-early-exit",
|
|
553
466
|
access: "public",
|
|
@@ -1001,6 +914,49 @@ var POX5_ABI = {
|
|
|
1001
914
|
error: "uint128"
|
|
1002
915
|
}
|
|
1003
916
|
}
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
name: "set-bond-admin",
|
|
920
|
+
access: "public",
|
|
921
|
+
args: [{ name: "new-admin", type: "principal" }],
|
|
922
|
+
outputs: {
|
|
923
|
+
response: {
|
|
924
|
+
ok: {
|
|
925
|
+
tuple: [
|
|
926
|
+
{ name: "new-admin", type: "principal" },
|
|
927
|
+
{ name: "old-admin", type: "principal" }
|
|
928
|
+
]
|
|
929
|
+
},
|
|
930
|
+
error: "uint128"
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
name: "set-pause-admin",
|
|
936
|
+
access: "public",
|
|
937
|
+
args: [{ name: "new-admin", type: "principal" }],
|
|
938
|
+
outputs: {
|
|
939
|
+
response: {
|
|
940
|
+
ok: {
|
|
941
|
+
tuple: [
|
|
942
|
+
{ name: "new-admin", type: "principal" },
|
|
943
|
+
{ name: "old-admin", type: "principal" }
|
|
944
|
+
]
|
|
945
|
+
},
|
|
946
|
+
error: "uint128"
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
name: "pause-rewards",
|
|
952
|
+
access: "public",
|
|
953
|
+
args: [],
|
|
954
|
+
outputs: {
|
|
955
|
+
response: {
|
|
956
|
+
ok: "bool",
|
|
957
|
+
error: "uint128"
|
|
958
|
+
}
|
|
959
|
+
}
|
|
1004
960
|
}
|
|
1005
961
|
]
|
|
1006
962
|
};
|
|
@@ -1016,11 +972,11 @@ function getPox5Contract(client) {
|
|
|
1016
972
|
const [address, name] = pox5ContractId(client).split(".");
|
|
1017
973
|
return getContract({ client, address, name, abi: POX5_ABI });
|
|
1018
974
|
}
|
|
1019
|
-
function
|
|
975
|
+
function toBytes(input) {
|
|
1020
976
|
return typeof input === "string" ? hexToBytes(input) : input;
|
|
1021
977
|
}
|
|
1022
978
|
function optionalBytes(input) {
|
|
1023
|
-
return input === undefined ? null :
|
|
979
|
+
return input === undefined ? null : toBytes(input);
|
|
1024
980
|
}
|
|
1025
981
|
var MAX_LOCKUP_OUTPUTS = 10;
|
|
1026
982
|
var MAX_LEAF_HASHES = 14;
|
|
@@ -1043,16 +999,16 @@ function btcLockupArg(lockup) {
|
|
|
1043
999
|
ok: {
|
|
1044
1000
|
outputs: lockup.l1Outputs.map((o) => ({
|
|
1045
1001
|
height: intToBigInt(o.height),
|
|
1046
|
-
tx:
|
|
1002
|
+
tx: toBytes(o.tx),
|
|
1047
1003
|
outputIndex: intToBigInt(o.outputIndex),
|
|
1048
|
-
header:
|
|
1049
|
-
leafHashes: o.leafHashes.map(
|
|
1004
|
+
header: toBytes(o.header),
|
|
1005
|
+
leafHashes: o.leafHashes.map(toBytes),
|
|
1050
1006
|
txCount: intToBigInt(o.txCount),
|
|
1051
1007
|
txIndex: intToBigInt(o.txIndex),
|
|
1052
1008
|
amount: intToBigInt(o.amount),
|
|
1053
1009
|
unlockBurnHeight: intToBigInt(o.unlockBurnHeight)
|
|
1054
1010
|
})),
|
|
1055
|
-
stakerUnlockBytes:
|
|
1011
|
+
stakerUnlockBytes: toBytes(lockup.stakerUnlockBytes)
|
|
1056
1012
|
}
|
|
1057
1013
|
};
|
|
1058
1014
|
}
|
|
@@ -1071,7 +1027,7 @@ function setupBond(client, params) {
|
|
|
1071
1027
|
targetRate: intToBigInt(targetRate),
|
|
1072
1028
|
stxValueRatio: intToBigInt(stxValueRatio),
|
|
1073
1029
|
minUstxRatio: intToBigInt(minUstxRatio),
|
|
1074
|
-
earlyUnlockBytes:
|
|
1030
|
+
earlyUnlockBytes: toBytes(earlyUnlockBytes),
|
|
1075
1031
|
allowlist: allowlist.map((a) => ({
|
|
1076
1032
|
staker: a.staker,
|
|
1077
1033
|
maxSats: intToBigInt(a.maxSats)
|
|
@@ -1171,15 +1127,26 @@ function claimStakerRewardsForSigner(client, params) {
|
|
|
1171
1127
|
function grantSignerKey(client, params) {
|
|
1172
1128
|
const { signerKey, signerManager, authId, signerSig, ...tx } = params;
|
|
1173
1129
|
return getPox5Contract(client).call.grantSignerKey({
|
|
1174
|
-
signerKey:
|
|
1130
|
+
signerKey: toBytes(signerKey),
|
|
1175
1131
|
signerManager,
|
|
1176
1132
|
authId: intToBigInt(authId),
|
|
1177
|
-
signerSig:
|
|
1133
|
+
signerSig: toBytes(signerSig)
|
|
1178
1134
|
}, tx);
|
|
1179
1135
|
}
|
|
1180
1136
|
function revokeSignerGrant(client, params) {
|
|
1181
1137
|
const { signerManager, signerKey, ...tx } = params;
|
|
1182
|
-
return getPox5Contract(client).call.revokeSignerGrant({ signerManager, signerKey:
|
|
1138
|
+
return getPox5Contract(client).call.revokeSignerGrant({ signerManager, signerKey: toBytes(signerKey) }, tx);
|
|
1139
|
+
}
|
|
1140
|
+
function setBondAdmin(client, params) {
|
|
1141
|
+
const { newAdmin, ...tx } = params;
|
|
1142
|
+
return getPox5Contract(client).call.setBondAdmin({ newAdmin }, tx);
|
|
1143
|
+
}
|
|
1144
|
+
function setPauseAdmin(client, params) {
|
|
1145
|
+
const { newAdmin, ...tx } = params;
|
|
1146
|
+
return getPox5Contract(client).call.setPauseAdmin({ newAdmin }, tx);
|
|
1147
|
+
}
|
|
1148
|
+
function pauseRewards(client, params) {
|
|
1149
|
+
return getPox5Contract(client).call.pauseRewards({}, params ?? {});
|
|
1183
1150
|
}
|
|
1184
1151
|
async function getStakerInfo(client, staker) {
|
|
1185
1152
|
return await getPox5Contract(client).read.getStakerInfo({
|
|
@@ -1229,7 +1196,7 @@ async function getSignerInfo(client, signer) {
|
|
|
1229
1196
|
function verifySignerKeyGrantOnChain(client, signerManager, signerKey) {
|
|
1230
1197
|
return getPox5Contract(client).read.verifySignerKeyGrant({
|
|
1231
1198
|
signerManager,
|
|
1232
|
-
signerKey:
|
|
1199
|
+
signerKey: toBytes(signerKey)
|
|
1233
1200
|
});
|
|
1234
1201
|
}
|
|
1235
1202
|
function getCurrentRewardCycle(client) {
|
|
@@ -1238,51 +1205,808 @@ function getCurrentRewardCycle(client) {
|
|
|
1238
1205
|
function getFirstPox5RewardCycle(client) {
|
|
1239
1206
|
return getPox5Contract(client).read.getFirstPox5RewardCycle({});
|
|
1240
1207
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
calls: [
|
|
1247
|
-
{
|
|
1248
|
-
contract,
|
|
1249
|
-
functionName: "get-staker-info",
|
|
1250
|
-
args: [Cl.principal(staker)]
|
|
1251
|
-
},
|
|
1252
|
-
{
|
|
1253
|
-
contract,
|
|
1254
|
-
functionName: "get-bond-membership",
|
|
1255
|
-
args: [Cl.principal(staker)]
|
|
1256
|
-
},
|
|
1257
|
-
{
|
|
1258
|
-
contract,
|
|
1259
|
-
functionName: "get-staker-custodied-sbtc",
|
|
1260
|
-
args: [Cl.principal(staker)]
|
|
1261
|
-
},
|
|
1262
|
-
{ contract, functionName: "current-pox-reward-cycle", args: [] }
|
|
1263
|
-
]
|
|
1208
|
+
function getEarned(client, params) {
|
|
1209
|
+
return getPox5Contract(client).read.getEarned({
|
|
1210
|
+
signer: params.signer,
|
|
1211
|
+
rewardCycle: intToBigInt(params.rewardCycle),
|
|
1212
|
+
bondIndex: params.bondIndex === undefined ? null : intToBigInt(params.bondIndex)
|
|
1264
1213
|
});
|
|
1265
|
-
const [stakerInfo, bondMembership, custodiedSbtc, currentCycle] = results;
|
|
1266
|
-
return {
|
|
1267
|
-
stakerInfo,
|
|
1268
|
-
bondMembership,
|
|
1269
|
-
custodiedSbtc,
|
|
1270
|
-
currentCycle
|
|
1271
|
-
};
|
|
1272
1214
|
}
|
|
1273
|
-
function
|
|
1274
|
-
return (client)
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1215
|
+
function getEarnedStakerRewards(client, params) {
|
|
1216
|
+
return getPox5Contract(client).read.getEarnedStakerRewards({
|
|
1217
|
+
signer: params.signer,
|
|
1218
|
+
rewardCycle: intToBigInt(params.rewardCycle),
|
|
1219
|
+
bondIndex: params.bondIndex === undefined ? null : intToBigInt(params.bondIndex),
|
|
1220
|
+
staker: params.staker
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
function getLastRewardComputeHeight(client) {
|
|
1224
|
+
return getPox5Contract(client).read.getLastRewardComputeHeight({});
|
|
1225
|
+
}
|
|
1226
|
+
function getTotalSharesStakedForCycle(client, rewardCycle, bondIndex) {
|
|
1227
|
+
return getPox5Contract(client).read.getTotalSharesStakedForCycle({
|
|
1228
|
+
rewardCycle: intToBigInt(rewardCycle),
|
|
1229
|
+
bondIndex: bondIndex === undefined ? null : intToBigInt(bondIndex)
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// src/pox5/cycles.ts
|
|
1234
|
+
function burnHeightToRewardCycle(burnHeight, params) {
|
|
1235
|
+
if (burnHeight < params.firstBurnchainBlockHeight) {
|
|
1236
|
+
throw new Error(`burnHeight ${burnHeight} precedes first burnchain block ${params.firstBurnchainBlockHeight}`);
|
|
1237
|
+
}
|
|
1238
|
+
return Math.floor((burnHeight - params.firstBurnchainBlockHeight) / params.rewardCycleLength);
|
|
1239
|
+
}
|
|
1240
|
+
function rewardCycleToBurnHeight(cycle, params) {
|
|
1241
|
+
return params.firstBurnchainBlockHeight + cycle * params.rewardCycleLength;
|
|
1242
|
+
}
|
|
1243
|
+
function bondPeriodToRewardCycle(bondIndex, params) {
|
|
1244
|
+
return params.firstBondPeriodCycle + bondIndex * BOND_GAP_CYCLES;
|
|
1245
|
+
}
|
|
1246
|
+
function bondPeriodToBurnHeight(bondIndex, params) {
|
|
1247
|
+
return rewardCycleToBurnHeight(bondPeriodToRewardCycle(bondIndex, params), params);
|
|
1248
|
+
}
|
|
1249
|
+
function bondUnlockCycle(bondIndex, params) {
|
|
1250
|
+
return bondPeriodToRewardCycle(bondIndex, params) + BOND_LENGTH_CYCLES;
|
|
1251
|
+
}
|
|
1252
|
+
function computeBondUnlockHeight(bondIndex, params) {
|
|
1253
|
+
const endCycle = bondPeriodToRewardCycle(bondIndex + BOND_LENGTH_CYCLES / BOND_GAP_CYCLES, params);
|
|
1254
|
+
return rewardCycleToBurnHeight(endCycle, params) - Math.floor(params.rewardCycleLength / 2);
|
|
1255
|
+
}
|
|
1256
|
+
function burnHeightToDistributionIndex(burnHeight, params) {
|
|
1257
|
+
return Math.floor((burnHeight - params.firstBurnchainBlockHeight) / Math.floor(params.rewardCycleLength / 2));
|
|
1258
|
+
}
|
|
1259
|
+
function distributionCycleToBurnHeight(distIndex, params) {
|
|
1260
|
+
return params.firstBurnchainBlockHeight + distIndex * Math.floor(params.rewardCycleLength / 2);
|
|
1261
|
+
}
|
|
1262
|
+
function currentDistributionCycle(burnHeight, params) {
|
|
1263
|
+
return burnHeightToDistributionIndex(burnHeight, params);
|
|
1264
|
+
}
|
|
1265
|
+
function isInPreparePhase(burnHeight, params) {
|
|
1266
|
+
const offset = (burnHeight - params.firstBurnchainBlockHeight) % params.rewardCycleLength;
|
|
1267
|
+
return offset >= params.rewardCycleLength - params.prepareCycleLength;
|
|
1268
|
+
}
|
|
1269
|
+
function bondPhaseAtHeight(bondIndex, burnHeight, params) {
|
|
1270
|
+
const cycle = burnHeightToRewardCycle(burnHeight, params);
|
|
1271
|
+
const start = bondPeriodToRewardCycle(bondIndex, params);
|
|
1272
|
+
const unlock = bondUnlockCycle(bondIndex, params);
|
|
1273
|
+
if (cycle < start - BOND_GAP_CYCLES)
|
|
1274
|
+
return "too-early";
|
|
1275
|
+
if (cycle < start)
|
|
1276
|
+
return "open";
|
|
1277
|
+
if (cycle < unlock)
|
|
1278
|
+
return "locked";
|
|
1279
|
+
return "unlocked";
|
|
1280
|
+
}
|
|
1281
|
+
function bondStatusAtHeight(bondIndex, burnHeight, params) {
|
|
1282
|
+
const cycle = burnHeightToRewardCycle(burnHeight, params);
|
|
1283
|
+
const start = bondPeriodToRewardCycle(bondIndex, params);
|
|
1284
|
+
const unlock = bondUnlockCycle(bondIndex, params);
|
|
1285
|
+
if (cycle < start - BOND_GAP_CYCLES)
|
|
1286
|
+
return "too-early";
|
|
1287
|
+
if (cycle < start) {
|
|
1288
|
+
if (cycle === start - 1 && isInPreparePhase(burnHeight, params)) {
|
|
1289
|
+
return "locked";
|
|
1290
|
+
}
|
|
1291
|
+
return "open";
|
|
1292
|
+
}
|
|
1293
|
+
if (cycle < unlock)
|
|
1294
|
+
return "locked";
|
|
1295
|
+
return "unlocked";
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// src/pox5/eligibility.ts
|
|
1299
|
+
function finish(reasons) {
|
|
1300
|
+
const unique = [];
|
|
1301
|
+
const seen = new Set;
|
|
1302
|
+
for (const code of reasons) {
|
|
1303
|
+
if (!seen.has(code)) {
|
|
1304
|
+
seen.add(code);
|
|
1305
|
+
unique.push(code);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
if (unique.length === 0)
|
|
1309
|
+
return { ok: true };
|
|
1310
|
+
return { ok: false, reasons: unique };
|
|
1311
|
+
}
|
|
1312
|
+
function toBytes2(input) {
|
|
1313
|
+
return typeof input === "string" ? hexToBytes(input) : input;
|
|
1314
|
+
}
|
|
1315
|
+
function outpointKey(tx, outputIndex) {
|
|
1316
|
+
const hex = typeof tx === "string" ? tx.toLowerCase() : bytesToHex(tx);
|
|
1317
|
+
return `${hex}:${intToBigInt(outputIndex)}`;
|
|
1318
|
+
}
|
|
1319
|
+
async function readCycleClock(client) {
|
|
1320
|
+
const info = await getPoxInfo(client);
|
|
1321
|
+
const burnHeight = info.currentBurnchainBlockHeight;
|
|
1322
|
+
const first = info.firstBurnchainBlockHeight;
|
|
1323
|
+
const raw = await client.request("/v2/pox", { method: "GET" });
|
|
1324
|
+
const pox5 = info.contractVersions.find((v) => v.contractId.endsWith(`.${POX5_CONTRACT_NAME}`));
|
|
1325
|
+
if (typeof burnHeight !== "number" || typeof first !== "number" || typeof raw.reward_cycle_length !== "number" || typeof raw.prepare_cycle_length !== "number" || pox5 == null) {
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
return {
|
|
1329
|
+
burnHeight,
|
|
1330
|
+
params: {
|
|
1331
|
+
firstBurnchainBlockHeight: first,
|
|
1332
|
+
rewardCycleLength: raw.reward_cycle_length,
|
|
1333
|
+
prepareCycleLength: raw.prepare_cycle_length
|
|
1334
|
+
},
|
|
1335
|
+
firstBondPeriodCycle: pox5.firstRewardCycleId
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
function inPrepare(clock) {
|
|
1339
|
+
if (!clock)
|
|
1340
|
+
return true;
|
|
1341
|
+
return isInPreparePhase(clock.burnHeight, clock.params);
|
|
1342
|
+
}
|
|
1343
|
+
async function signerReasons(client, signerManager) {
|
|
1344
|
+
const info = await getSignerInfo(client, signerManager);
|
|
1345
|
+
if (info == null)
|
|
1346
|
+
return [Pox5ErrorCode.SignerNotFound];
|
|
1347
|
+
try {
|
|
1348
|
+
const granted = await verifySignerKeyGrantOnChain(client, signerManager, info);
|
|
1349
|
+
if (!granted)
|
|
1350
|
+
return [Pox5ErrorCode.SignerKeyGrantNotFound];
|
|
1351
|
+
} catch {
|
|
1352
|
+
return [Pox5ErrorCode.SignerKeyGrantNotFound];
|
|
1353
|
+
}
|
|
1354
|
+
return [];
|
|
1355
|
+
}
|
|
1356
|
+
function bondOverlaps(membership, newFirstCycle, clock) {
|
|
1357
|
+
if (!membership)
|
|
1358
|
+
return false;
|
|
1359
|
+
const start = bondPeriodToRewardCycle(Number(membership.bondIndex), {
|
|
1360
|
+
...clock.params,
|
|
1361
|
+
firstBondPeriodCycle: clock.firstBondPeriodCycle
|
|
1362
|
+
});
|
|
1363
|
+
return start + BOND_LENGTH_CYCLES > newFirstCycle;
|
|
1364
|
+
}
|
|
1365
|
+
async function stxFloor(client, staker, amountUstx) {
|
|
1366
|
+
const balance = await getBalance(client, { address: staker });
|
|
1367
|
+
if (balance < amountUstx)
|
|
1368
|
+
return Pox5ErrorCode.InsufficientStx;
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
function principalValue(cv) {
|
|
1372
|
+
if (cv.type === "address" || cv.type === "contract")
|
|
1373
|
+
return cv.value;
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
function minUstxForSats(sats, stxValueRatio, minUstxRatio) {
|
|
1377
|
+
return stxValueRatio * sats / 100n * minUstxRatio / 10000n;
|
|
1378
|
+
}
|
|
1379
|
+
async function eligibleStake(client, params) {
|
|
1380
|
+
const reasons = [];
|
|
1381
|
+
const amountUstx = intToBigInt(params.amountUstx);
|
|
1382
|
+
const numCycles = intToBigInt(params.numCycles);
|
|
1383
|
+
const startBurnHeight = intToBigInt(params.startBurnHeight);
|
|
1384
|
+
const [clock, stakerInfo, membership, signerErrs, floor] = await Promise.all([
|
|
1385
|
+
readCycleClock(client),
|
|
1386
|
+
getStakerInfo(client, params.staker),
|
|
1387
|
+
getBondMembership(client, params.staker),
|
|
1388
|
+
signerReasons(client, params.signerManager),
|
|
1389
|
+
stxFloor(client, params.staker, amountUstx)
|
|
1390
|
+
]);
|
|
1391
|
+
if (inPrepare(clock))
|
|
1392
|
+
reasons.push(Pox5ErrorCode.StakeInPreparePhase);
|
|
1393
|
+
reasons.push(...signerErrs);
|
|
1394
|
+
if (floor !== undefined)
|
|
1395
|
+
reasons.push(floor);
|
|
1396
|
+
if (numCycles < 1n || numCycles > BigInt(MAX_NUM_CYCLES)) {
|
|
1397
|
+
reasons.push(Pox5ErrorCode.InvalidNumCycles);
|
|
1398
|
+
}
|
|
1399
|
+
if (clock) {
|
|
1400
|
+
const currentCycle = burnHeightToRewardCycle(clock.burnHeight, clock.params);
|
|
1401
|
+
const firstRewardCycle = currentCycle + 1;
|
|
1402
|
+
const specified = burnHeightToRewardCycle(Number(startBurnHeight), clock.params) + 1;
|
|
1403
|
+
if (firstRewardCycle !== specified) {
|
|
1404
|
+
reasons.push(Pox5ErrorCode.InvalidStartBurnHeight);
|
|
1405
|
+
}
|
|
1406
|
+
if (stakerInfo)
|
|
1407
|
+
reasons.push(Pox5ErrorCode.AlreadyStaked);
|
|
1408
|
+
if (bondOverlaps(membership, firstRewardCycle, clock)) {
|
|
1409
|
+
reasons.push(Pox5ErrorCode.AlreadyStaked);
|
|
1410
|
+
}
|
|
1411
|
+
if (membership) {
|
|
1412
|
+
const unlock = await getBondL1UnlockHeight(client, membership.bondIndex);
|
|
1413
|
+
if (BigInt(clock.burnHeight) < unlock) {
|
|
1414
|
+
reasons.push(Pox5ErrorCode.RolloverTooEarly);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
} else if (stakerInfo) {
|
|
1418
|
+
reasons.push(Pox5ErrorCode.AlreadyStaked);
|
|
1419
|
+
}
|
|
1420
|
+
return finish(reasons);
|
|
1421
|
+
}
|
|
1422
|
+
async function eligibleRegisterForBond(client, params) {
|
|
1423
|
+
const reasons = [];
|
|
1424
|
+
const amountUstx = intToBigInt(params.amountUstx);
|
|
1425
|
+
const lockup = params.btcLockup;
|
|
1426
|
+
let sats = 0n;
|
|
1427
|
+
if ("sbtcSats" in lockup) {
|
|
1428
|
+
sats = intToBigInt(lockup.sbtcSats);
|
|
1429
|
+
} else {
|
|
1430
|
+
if (lockup.l1Outputs.length === 0) {
|
|
1431
|
+
reasons.push(Pox5ErrorCode.InvalidLockupAmount);
|
|
1432
|
+
}
|
|
1433
|
+
const seen = new Set;
|
|
1434
|
+
for (const output of lockup.l1Outputs) {
|
|
1435
|
+
sats += intToBigInt(output.amount);
|
|
1436
|
+
const key = outpointKey(output.tx, output.outputIndex);
|
|
1437
|
+
if (seen.has(key))
|
|
1438
|
+
reasons.push(Pox5ErrorCode.DuplicateLockupOutpoint);
|
|
1439
|
+
seen.add(key);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
const [clock, stakerInfo, membership, bond, allowance, signerErrs, floor] = await Promise.all([
|
|
1443
|
+
readCycleClock(client),
|
|
1444
|
+
getStakerInfo(client, params.staker),
|
|
1445
|
+
getBondMembership(client, params.staker),
|
|
1446
|
+
getProtocolBond(client, params.bondIndex),
|
|
1447
|
+
getBondAllowance(client, params.bondIndex, params.staker),
|
|
1448
|
+
signerReasons(client, params.signerManager),
|
|
1449
|
+
stxFloor(client, params.staker, amountUstx)
|
|
1450
|
+
]);
|
|
1451
|
+
if (inPrepare(clock))
|
|
1452
|
+
reasons.push(Pox5ErrorCode.StakeInPreparePhase);
|
|
1453
|
+
reasons.push(...signerErrs);
|
|
1454
|
+
if (floor !== undefined)
|
|
1455
|
+
reasons.push(floor);
|
|
1456
|
+
if (!bond)
|
|
1457
|
+
reasons.push(Pox5ErrorCode.BondNotFound);
|
|
1458
|
+
if (allowance == null)
|
|
1459
|
+
reasons.push(Pox5ErrorCode.NotAllowlisted);
|
|
1460
|
+
else if (sats > allowance)
|
|
1461
|
+
reasons.push(Pox5ErrorCode.TooMuchSats);
|
|
1462
|
+
if (bond) {
|
|
1463
|
+
const min = minUstxForSats(sats, bond.stxValueRatio, bond.minUstxRatio);
|
|
1464
|
+
if (amountUstx < min)
|
|
1465
|
+
reasons.push(Pox5ErrorCode.InsufficientStx);
|
|
1466
|
+
}
|
|
1467
|
+
if (clock) {
|
|
1468
|
+
const bondStart = bondPeriodToBurnHeight(Number(intToBigInt(params.bondIndex)), {
|
|
1469
|
+
...clock.params,
|
|
1470
|
+
firstBondPeriodCycle: clock.firstBondPeriodCycle
|
|
1471
|
+
});
|
|
1472
|
+
if (clock.burnHeight >= bondStart) {
|
|
1473
|
+
reasons.push(Pox5ErrorCode.BondAlreadyStarted);
|
|
1474
|
+
}
|
|
1475
|
+
const firstRewardCycle = bondPeriodToRewardCycle(Number(intToBigInt(params.bondIndex)), { ...clock.params, firstBondPeriodCycle: clock.firstBondPeriodCycle });
|
|
1476
|
+
if (stakerInfo) {
|
|
1477
|
+
const unlockCycle = Number(stakerInfo.firstRewardCycle) + Number(stakerInfo.numCycles);
|
|
1478
|
+
if (unlockCycle > firstRewardCycle) {
|
|
1479
|
+
reasons.push(Pox5ErrorCode.AlreadyStaked);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
if (bondOverlaps(membership, firstRewardCycle, clock)) {
|
|
1483
|
+
reasons.push(Pox5ErrorCode.AlreadyRegistered);
|
|
1484
|
+
}
|
|
1485
|
+
if (membership) {
|
|
1486
|
+
const unlock = await getBondL1UnlockHeight(client, membership.bondIndex);
|
|
1487
|
+
if (BigInt(clock.burnHeight) < unlock) {
|
|
1488
|
+
reasons.push(Pox5ErrorCode.RolloverTooEarly);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
if ("l1Outputs" in lockup && lockup.l1Outputs.length > 0) {
|
|
1493
|
+
const minUnlock = await getBondL1UnlockHeight(client, params.bondIndex);
|
|
1494
|
+
for (const output of lockup.l1Outputs) {
|
|
1495
|
+
const height = intToBigInt(output.unlockBurnHeight);
|
|
1496
|
+
if (height < minUnlock || height >= BITCOIN_LOCKTIME_THRESHOLD) {
|
|
1497
|
+
reasons.push(Pox5ErrorCode.InvalidUnlockHeight);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
return finish(reasons);
|
|
1502
|
+
}
|
|
1503
|
+
async function eligibleUnstake(client, params) {
|
|
1504
|
+
const reasons = [];
|
|
1505
|
+
const [clock, stakerInfo] = await Promise.all([
|
|
1506
|
+
readCycleClock(client),
|
|
1507
|
+
getStakerInfo(client, params.staker)
|
|
1508
|
+
]);
|
|
1509
|
+
if (inPrepare(clock))
|
|
1510
|
+
reasons.push(Pox5ErrorCode.UnstakeInPreparePhase);
|
|
1511
|
+
if (!stakerInfo)
|
|
1512
|
+
reasons.push(Pox5ErrorCode.NotStaking);
|
|
1513
|
+
else if (stakerInfo.signer !== params.oldSignerManager) {
|
|
1514
|
+
reasons.push(Pox5ErrorCode.InvalidOldSignerManager);
|
|
1515
|
+
}
|
|
1516
|
+
return finish(reasons);
|
|
1517
|
+
}
|
|
1518
|
+
async function eligibleUnstakeSbtc(client, params) {
|
|
1519
|
+
const reasons = [];
|
|
1520
|
+
const amount = intToBigInt(params.amountSats);
|
|
1521
|
+
const [clock, membership] = await Promise.all([
|
|
1522
|
+
readCycleClock(client),
|
|
1523
|
+
getBondMembership(client, params.staker)
|
|
1524
|
+
]);
|
|
1525
|
+
if (inPrepare(clock))
|
|
1526
|
+
reasons.push(Pox5ErrorCode.StakeInPreparePhase);
|
|
1527
|
+
if (!membership)
|
|
1528
|
+
reasons.push(Pox5ErrorCode.NotBondParticipant);
|
|
1529
|
+
else {
|
|
1530
|
+
if (membership.signer !== params.signerManager) {
|
|
1531
|
+
reasons.push(Pox5ErrorCode.InvalidOldSignerManager);
|
|
1532
|
+
}
|
|
1533
|
+
if (membership.isL1Lock)
|
|
1534
|
+
reasons.push(Pox5ErrorCode.CannotUnstakeSbtc);
|
|
1535
|
+
if (amount > membership.amountSats) {
|
|
1536
|
+
reasons.push(Pox5ErrorCode.InvalidUnstakeSbtcAmount);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
return finish(reasons);
|
|
1540
|
+
}
|
|
1541
|
+
async function eligibleClaimRewards(client, params) {
|
|
1542
|
+
const reasons = [];
|
|
1543
|
+
const contract = pox5ContractId(client);
|
|
1544
|
+
const paused = await getDataVar(client, {
|
|
1545
|
+
contract,
|
|
1546
|
+
varName: "rewards-paused"
|
|
1547
|
+
});
|
|
1548
|
+
if (paused.type === "true")
|
|
1549
|
+
reasons.push(Pox5ErrorCode.RewardsPaused);
|
|
1550
|
+
const periods = params.bondPeriods ?? [];
|
|
1551
|
+
const earnedParts = await Promise.all([
|
|
1552
|
+
getEarned(client, {
|
|
1553
|
+
signer: params.signer,
|
|
1554
|
+
rewardCycle: params.rewardCycle,
|
|
1555
|
+
bondIndex: params.bondIndex
|
|
1556
|
+
}),
|
|
1557
|
+
...periods.map((bondIndex) => getEarned(client, {
|
|
1558
|
+
signer: params.signer,
|
|
1559
|
+
rewardCycle: params.rewardCycle,
|
|
1560
|
+
bondIndex
|
|
1561
|
+
}))
|
|
1562
|
+
]);
|
|
1563
|
+
let total = 0n;
|
|
1564
|
+
for (const part of earnedParts)
|
|
1565
|
+
total += part;
|
|
1566
|
+
if (total === 0n)
|
|
1567
|
+
reasons.push(Pox5ErrorCode.NoClaimableRewards);
|
|
1568
|
+
return finish(reasons);
|
|
1569
|
+
}
|
|
1570
|
+
async function eligibleGrantSignerKey(client, params) {
|
|
1571
|
+
const reasons = [];
|
|
1572
|
+
const used = await getMapEntry(client, {
|
|
1573
|
+
contract: pox5ContractId(client),
|
|
1574
|
+
mapName: "used-signer-key-grants",
|
|
1575
|
+
key: Cl.tuple({
|
|
1576
|
+
"signer-key": Cl.buffer(toBytes2(params.signerKey)),
|
|
1577
|
+
"signer-manager": Cl.principal(params.signerManager),
|
|
1578
|
+
"auth-id": Cl.uint(intToBigInt(params.authId))
|
|
1579
|
+
})
|
|
1580
|
+
});
|
|
1581
|
+
if (used.type === "some")
|
|
1582
|
+
reasons.push(Pox5ErrorCode.SignerKeyGrantUsed);
|
|
1583
|
+
return finish(reasons);
|
|
1584
|
+
}
|
|
1585
|
+
async function eligibleSetBondAdmin(client, params) {
|
|
1586
|
+
const admin = await getDataVar(client, {
|
|
1587
|
+
contract: pox5ContractId(client),
|
|
1588
|
+
varName: "bond-admin"
|
|
1589
|
+
});
|
|
1590
|
+
if (principalValue(admin) !== params.caller) {
|
|
1591
|
+
return finish([Pox5ErrorCode.Unauthorized]);
|
|
1592
|
+
}
|
|
1593
|
+
return { ok: true };
|
|
1594
|
+
}
|
|
1595
|
+
async function eligiblePauseRewards(client, params) {
|
|
1596
|
+
const admin = await getDataVar(client, {
|
|
1597
|
+
contract: pox5ContractId(client),
|
|
1598
|
+
varName: "pause-admin"
|
|
1599
|
+
});
|
|
1600
|
+
if (principalValue(admin) !== params.caller) {
|
|
1601
|
+
return finish([Pox5ErrorCode.Unauthorized]);
|
|
1602
|
+
}
|
|
1603
|
+
return { ok: true };
|
|
1604
|
+
}
|
|
1605
|
+
// src/pox5/script.ts
|
|
1606
|
+
var import_sha2 = require("@noble/hashes/sha2.js");
|
|
1607
|
+
function serializeCScriptNum(n) {
|
|
1608
|
+
const big = typeof n === "bigint" ? n : BigInt(n);
|
|
1609
|
+
if (big < 0n)
|
|
1610
|
+
throw new Error("serializeCScriptNum: negative value");
|
|
1611
|
+
if (big >= C_SCRIPT_NUM_MAX) {
|
|
1612
|
+
throw new Error("serializeCScriptNum: n >= 2^39 (contract rejects with ERR_INVALID_UNLOCK_HEIGHT)");
|
|
1613
|
+
}
|
|
1614
|
+
if (big === 0n)
|
|
1615
|
+
return new Uint8Array(0);
|
|
1616
|
+
const out = [];
|
|
1617
|
+
let v = big;
|
|
1618
|
+
while (v > 0n) {
|
|
1619
|
+
out.push(Number(v & 0xffn));
|
|
1620
|
+
v >>= 8n;
|
|
1621
|
+
}
|
|
1622
|
+
if (out[out.length - 1] & 128)
|
|
1623
|
+
out.push(0);
|
|
1624
|
+
return Uint8Array.from(out);
|
|
1625
|
+
}
|
|
1626
|
+
function pushScriptBytes(bytes) {
|
|
1627
|
+
const len = bytes.length;
|
|
1628
|
+
if (len > 65535)
|
|
1629
|
+
throw new Error(`pushScriptBytes: payload too large (${len} > 65535)`);
|
|
1630
|
+
let prefix;
|
|
1631
|
+
if (len < 76)
|
|
1632
|
+
prefix = Uint8Array.of(len);
|
|
1633
|
+
else if (len < 256)
|
|
1634
|
+
prefix = Uint8Array.of(76, len);
|
|
1635
|
+
else
|
|
1636
|
+
prefix = Uint8Array.of(77, len & 255, len >> 8);
|
|
1637
|
+
return concatBytes(prefix, bytes);
|
|
1638
|
+
}
|
|
1639
|
+
function pushCScriptNum(n) {
|
|
1640
|
+
const big = typeof n === "bigint" ? n : BigInt(n);
|
|
1641
|
+
if (big === 0n)
|
|
1642
|
+
return Uint8Array.of(0);
|
|
1643
|
+
if (big >= 1n && big <= 16n)
|
|
1644
|
+
return Uint8Array.of(80 + Number(big));
|
|
1645
|
+
return pushScriptBytes(serializeCScriptNum(big));
|
|
1646
|
+
}
|
|
1647
|
+
function stakerConsensusBuff(stxAddress) {
|
|
1648
|
+
return serializeCVBytes(Cl.principal(stxAddress));
|
|
1649
|
+
}
|
|
1650
|
+
function stakerPreimage(stxAddress) {
|
|
1651
|
+
return import_sha2.sha256(stakerConsensusBuff(stxAddress));
|
|
1652
|
+
}
|
|
1653
|
+
function toBytes3(input) {
|
|
1654
|
+
return typeof input === "string" ? hexToBytes(input) : input;
|
|
1655
|
+
}
|
|
1656
|
+
function buildLockupScript(opts) {
|
|
1657
|
+
const height = BigInt(opts.unlockBurnHeight);
|
|
1658
|
+
if (height <= 0n) {
|
|
1659
|
+
throw new Error("unlockBurnHeight must be > 0 (contract rejects with ERR_INVALID_UNLOCK_HEIGHT)");
|
|
1660
|
+
}
|
|
1661
|
+
if (height >= BITCOIN_LOCKTIME_THRESHOLD) {
|
|
1662
|
+
throw new Error("unlockBurnHeight >= 500,000,000 would be read by Bitcoin as a timestamp (contract rejects it)");
|
|
1663
|
+
}
|
|
1664
|
+
const stakerHash = import_sha2.sha256(stakerPreimage(opts.stxAddress));
|
|
1665
|
+
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));
|
|
1666
|
+
}
|
|
1667
|
+
function buildLockupOutputScript(opts) {
|
|
1668
|
+
return concatBytes(Uint8Array.of(0, 32), import_sha2.sha256(buildLockupScript(opts)));
|
|
1669
|
+
}
|
|
1670
|
+
function buildLockupAddress(opts, network = "mainnet") {
|
|
1671
|
+
const hash = import_sha2.sha256(buildLockupScript(opts));
|
|
1672
|
+
return bech32.encode(BITCOIN_NETWORK_PARAMS[network].hrp, [
|
|
1673
|
+
0,
|
|
1674
|
+
...bech32.toWords(hash)
|
|
1675
|
+
]);
|
|
1676
|
+
}
|
|
1677
|
+
function buildDefaultStakerUnlockBytes(publicKey) {
|
|
1678
|
+
const pubkey = toBytes3(publicKey);
|
|
1679
|
+
if (pubkey.length !== 33)
|
|
1680
|
+
throw new Error(`Expected 33-byte compressed pubkey, got ${pubkey.length}`);
|
|
1681
|
+
const prefix = pubkey[0];
|
|
1682
|
+
if (prefix !== 2 && prefix !== 3) {
|
|
1683
|
+
throw new Error(`Expected compressed pubkey prefix 0x02 or 0x03, got ${prefix}`);
|
|
1684
|
+
}
|
|
1685
|
+
return concatBytes(pushScriptBytes(pubkey), Uint8Array.of(172));
|
|
1686
|
+
}
|
|
1687
|
+
function buildRegisterMetadata(opts) {
|
|
1688
|
+
const unlockHeight = computeBondUnlockHeight(opts.bondIndex, opts.cycle);
|
|
1689
|
+
const unlockBytes = buildDefaultStakerUnlockBytes(opts.bitcoinPublicKey);
|
|
1690
|
+
const lockOpts = {
|
|
1691
|
+
stxAddress: opts.stxAddress,
|
|
1692
|
+
unlockBurnHeight: unlockHeight,
|
|
1693
|
+
stakerUnlockBytes: unlockBytes,
|
|
1694
|
+
earlyUnlockBytes: opts.earlyUnlockBytes
|
|
1695
|
+
};
|
|
1696
|
+
return {
|
|
1697
|
+
lockAddress: buildLockupAddress(lockOpts, opts.network ?? "mainnet"),
|
|
1698
|
+
lockScript: buildLockupScript(lockOpts),
|
|
1699
|
+
outputScript: buildLockupOutputScript(lockOpts),
|
|
1700
|
+
unlockBytes,
|
|
1701
|
+
unlockHeight
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
// src/pox5/grants.ts
|
|
1705
|
+
var import_secp256k1 = require("@noble/curves/secp256k1.js");
|
|
1706
|
+
function computeSignerGrantHash(opts) {
|
|
1707
|
+
return structuredDataHash({
|
|
1708
|
+
domain: Cl.tuple({
|
|
1709
|
+
name: Cl.stringAscii(POX5_SIGNER_DOMAIN.name),
|
|
1710
|
+
version: Cl.stringAscii(POX5_SIGNER_DOMAIN.version),
|
|
1711
|
+
"chain-id": Cl.uint(opts.chainId)
|
|
1712
|
+
}),
|
|
1713
|
+
message: Cl.tuple({
|
|
1714
|
+
topic: Cl.stringAscii("grant-authorization"),
|
|
1715
|
+
"signer-manager": Cl.principal(opts.signerManager),
|
|
1716
|
+
"auth-id": Cl.uint(opts.authId)
|
|
1717
|
+
})
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
async function signSignerGrant(account, opts) {
|
|
1721
|
+
const vrs = await account.sign(computeSignerGrantHash(opts));
|
|
1722
|
+
if (vrs.length !== 65)
|
|
1723
|
+
throw new Error(`Expected 65-byte recoverable signature, got ${vrs.length}`);
|
|
1724
|
+
return bytesToHex(concatBytes(vrs.slice(1), vrs.slice(0, 1)));
|
|
1725
|
+
}
|
|
1726
|
+
function verifySignerGrant(opts) {
|
|
1727
|
+
try {
|
|
1728
|
+
const sig = typeof opts.signature === "string" ? hexToBytes(opts.signature) : opts.signature;
|
|
1729
|
+
if (sig.length !== 65)
|
|
1730
|
+
return false;
|
|
1731
|
+
const pubkey = typeof opts.publicKey === "string" ? hexToBytes(opts.publicKey) : opts.publicKey;
|
|
1732
|
+
const hash = computeSignerGrantHash(opts);
|
|
1733
|
+
const recovered = import_secp256k1.secp256k1.Signature.fromBytes(sig.slice(0, 64)).addRecoveryBit(sig[64]).recoverPublicKey(hash).toBytes(true);
|
|
1734
|
+
return bytesToHex(recovered) === bytesToHex(pubkey);
|
|
1735
|
+
} catch {
|
|
1736
|
+
return false;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
// src/pox5/lockProof.ts
|
|
1740
|
+
var import_sha22 = require("@noble/hashes/sha2.js");
|
|
1741
|
+
var MAX_LEAF_HASHES2 = 14;
|
|
1742
|
+
function lockupScriptPubKey(lockScript) {
|
|
1743
|
+
return concatBytes(Uint8Array.of(0, 32), import_sha22.sha256(lockScript));
|
|
1744
|
+
}
|
|
1745
|
+
function scriptsEqual(a, b) {
|
|
1746
|
+
return bytesToHex(a) === bytesToHex(b);
|
|
1747
|
+
}
|
|
1748
|
+
function spvProofToL1LockupOutput(opts) {
|
|
1749
|
+
const { proof, lockScript, unlockBurnHeight } = opts;
|
|
1750
|
+
const tx = stripWitness(proof.rawTx);
|
|
1751
|
+
const parsed = parseBitcoinTx(proof.rawTx);
|
|
1752
|
+
const expected = lockupScriptPubKey(lockScript);
|
|
1753
|
+
let outputIndex = opts.vout ?? proof.vout;
|
|
1754
|
+
if (outputIndex === undefined) {
|
|
1755
|
+
const matches = [];
|
|
1756
|
+
for (let i = 0;i < parsed.outputs.length; i++) {
|
|
1757
|
+
const spk = parsed.outputs[i]?.scriptPubKey;
|
|
1758
|
+
if (spk && scriptsEqual(spk, expected))
|
|
1759
|
+
matches.push(i);
|
|
1760
|
+
}
|
|
1761
|
+
if (matches.length !== 1) {
|
|
1762
|
+
throw new Error(`spvProofToL1LockupOutput: expected exactly one matching lockup output, found ${matches.length}`);
|
|
1763
|
+
}
|
|
1764
|
+
outputIndex = matches[0];
|
|
1765
|
+
}
|
|
1766
|
+
const output = parsed.outputs[outputIndex];
|
|
1767
|
+
if (!output) {
|
|
1768
|
+
throw new Error(`spvProofToL1LockupOutput: output ${outputIndex} is out of range (${parsed.outputs.length} outputs)`);
|
|
1769
|
+
}
|
|
1770
|
+
if (!scriptsEqual(output.scriptPubKey, expected)) {
|
|
1771
|
+
throw new Error("lockup output script does not match");
|
|
1772
|
+
}
|
|
1773
|
+
const leafHashes = proof.merkle.siblings;
|
|
1774
|
+
if (leafHashes.length > MAX_LEAF_HASHES2) {
|
|
1775
|
+
throw new Error(`spvProofToL1LockupOutput: ${leafHashes.length} merkle siblings; pox-5 accepts at most ${MAX_LEAF_HASHES2}`);
|
|
1776
|
+
}
|
|
1777
|
+
return {
|
|
1778
|
+
height: proof.height,
|
|
1779
|
+
tx,
|
|
1780
|
+
outputIndex,
|
|
1781
|
+
header: proof.header,
|
|
1782
|
+
leafHashes,
|
|
1783
|
+
txCount: proof.merkle.txCount,
|
|
1784
|
+
txIndex: proof.merkle.txIndex,
|
|
1785
|
+
amount: output.value,
|
|
1786
|
+
unlockBurnHeight
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
async function buildPox5LockProof(opts) {
|
|
1790
|
+
const proof = await buildTxProof(opts.source, {
|
|
1791
|
+
txid: opts.txid,
|
|
1792
|
+
vout: opts.vout
|
|
1793
|
+
});
|
|
1794
|
+
return spvProofToL1LockupOutput({
|
|
1795
|
+
proof,
|
|
1796
|
+
lockScript: opts.lockScript,
|
|
1797
|
+
unlockBurnHeight: opts.unlockBurnHeight,
|
|
1798
|
+
vout: opts.vout
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
// src/pox5/reclaim.ts
|
|
1802
|
+
var import_sha23 = require("@noble/hashes/sha2.js");
|
|
1803
|
+
var btc = __toESM(require("@scure/btc-signer"));
|
|
1804
|
+
var import_utils = require("@scure/btc-signer/utils.js");
|
|
1805
|
+
var SIGHASH_ALL = 1;
|
|
1806
|
+
var DUST_LIMIT_SATS = 546n;
|
|
1807
|
+
var ELSE_SELECTOR = new Uint8Array(0);
|
|
1808
|
+
var IF_SELECTOR = new Uint8Array([1]);
|
|
1809
|
+
var TX_OPTS = {
|
|
1810
|
+
allowUnknownOutputs: true,
|
|
1811
|
+
disableScriptCheck: true,
|
|
1812
|
+
allowUnknownInputs: true
|
|
1813
|
+
};
|
|
1814
|
+
var REGTEST_NETWORK = { ...btc.TEST_NETWORK, bech32: "bcrt" };
|
|
1815
|
+
function btcNetworkFrom(network) {
|
|
1816
|
+
if (network === "mainnet")
|
|
1817
|
+
return btc.NETWORK;
|
|
1818
|
+
if (network === "testnet")
|
|
1819
|
+
return btc.TEST_NETWORK;
|
|
1820
|
+
return REGTEST_NETWORK;
|
|
1821
|
+
}
|
|
1822
|
+
function toBytes4(value) {
|
|
1823
|
+
return typeof value === "string" ? hexToBytes(value) : value;
|
|
1824
|
+
}
|
|
1825
|
+
function toPrivBytes(value) {
|
|
1826
|
+
return toBytes4(value).slice(0, 32);
|
|
1827
|
+
}
|
|
1828
|
+
function bytesEqual(a, b) {
|
|
1829
|
+
return bytesToHex(a) === bytesToHex(b);
|
|
1830
|
+
}
|
|
1831
|
+
function scriptToWshOutput(lockScript) {
|
|
1832
|
+
return concatBytes(Uint8Array.of(0, 32), import_sha23.sha256(lockScript));
|
|
1833
|
+
}
|
|
1834
|
+
var SCAFFOLD_PREFIX_LEN = 10;
|
|
1835
|
+
function decodeLockScript(script) {
|
|
1836
|
+
const ops = btc.Script.decode(script);
|
|
1837
|
+
const endifIdx = ops.reduce((found, op, i) => op === "ENDIF" && ops[i + 1] === "VERIFY" ? i : found, -1);
|
|
1838
|
+
const hashOp = ops[8];
|
|
1839
|
+
const shapeOk = endifIdx >= SCAFFOLD_PREFIX_LEN && ops[0] === "IF" && ops[2] === "CHECKLOCKTIMEVERIFY" && ops[3] === "ELSE" && ops[4] === "SIZE" && ops[6] === "EQUALVERIFY" && ops[7] === "SHA256" && hashOp instanceof Uint8Array && hashOp.length === 32 && ops[9] === "EQUALVERIFY";
|
|
1840
|
+
if (!shapeOk) {
|
|
1841
|
+
throw new Error("reclaim: lockScript is not a pox-5 lockup script (expected the OP_IF/OP_ELSE … OP_ENDIF OP_VERIFY scaffold)");
|
|
1842
|
+
}
|
|
1843
|
+
const keysIn = (slice) => slice.filter((op) => op instanceof Uint8Array && op.length === 33);
|
|
1844
|
+
const heightOp = ops[1];
|
|
1845
|
+
const unlockHeight = typeof heightOp === "number" ? heightOp : heightOp instanceof Uint8Array ? Number(btc.ScriptNum().decode(heightOp)) : undefined;
|
|
1846
|
+
return {
|
|
1847
|
+
cosignerPubs: keysIn(ops.slice(SCAFFOLD_PREFIX_LEN, endifIdx)),
|
|
1848
|
+
stakerPubs: keysIn(ops.slice(endifIdx + 2)),
|
|
1849
|
+
unlockHeight
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
function buildReclaim(opts) {
|
|
1853
|
+
const network = btcNetworkFrom(opts.network);
|
|
1854
|
+
const lockScript = toBytes4(opts.lockScript);
|
|
1855
|
+
const { unlockHeight: scriptHeight } = decodeLockScript(lockScript);
|
|
1856
|
+
const amount = intToBigInt(opts.utxo.value);
|
|
1857
|
+
const { address } = opts.output;
|
|
1858
|
+
const feeSats = intToBigInt(opts.output.feeSats);
|
|
1859
|
+
if (feeSats < 0n) {
|
|
1860
|
+
throw new Error(`buildReclaim: feeSats (${feeSats}) must be non-negative`);
|
|
1861
|
+
}
|
|
1862
|
+
const outputScript = scriptToWshOutput(lockScript);
|
|
1863
|
+
if (opts.utxo.scriptPubKey && !bytesEqual(opts.utxo.scriptPubKey, outputScript)) {
|
|
1864
|
+
throw new Error("buildReclaim: utxo.scriptPubKey does not match the lockScript — the signature would commit to the wrong output and every reclaim attempt would fail at relay");
|
|
1865
|
+
}
|
|
1866
|
+
const sweepSats = amount - feeSats;
|
|
1867
|
+
if (sweepSats <= 0n) {
|
|
1868
|
+
throw new Error(`buildReclaim: fee (${feeSats}) >= utxo value (${amount})`);
|
|
1869
|
+
}
|
|
1870
|
+
if (sweepSats < DUST_LIMIT_SATS) {
|
|
1871
|
+
throw new Error(`buildReclaim: sweep of ${sweepSats} sats is below the ${DUST_LIMIT_SATS}-sat dust limit — nodes would not relay the tx`);
|
|
1872
|
+
}
|
|
1873
|
+
const earlyExit = opts.path === "early-exit";
|
|
1874
|
+
if (!earlyExit && scriptHeight == null) {
|
|
1875
|
+
throw new Error("buildReclaim: the locktime path needs a lockScript that encodes a CLTV unlock height");
|
|
1876
|
+
}
|
|
1877
|
+
const lockTime = earlyExit ? 0 : scriptHeight;
|
|
1878
|
+
const tx = new btc.Transaction({ ...TX_OPTS, lockTime });
|
|
1879
|
+
tx.addInput({
|
|
1880
|
+
txid: opts.utxo.txid,
|
|
1881
|
+
index: opts.utxo.vout,
|
|
1882
|
+
sequence: earlyExit ? 4294967295 : 4294967294,
|
|
1883
|
+
witnessUtxo: { script: outputScript, amount },
|
|
1884
|
+
witnessScript: lockScript
|
|
1885
|
+
});
|
|
1886
|
+
tx.addOutputAddress(address, sweepSats, network);
|
|
1887
|
+
return tx;
|
|
1888
|
+
}
|
|
1889
|
+
function computeReclaimSighash(tx, opts) {
|
|
1890
|
+
const input = tx.getInput(0);
|
|
1891
|
+
const witnessScript = opts?.witnessScript != null ? toBytes4(opts.witnessScript) : input.witnessScript;
|
|
1892
|
+
const amount = opts?.amountSats != null ? intToBigInt(opts.amountSats) : input.witnessUtxo?.amount;
|
|
1893
|
+
if (!witnessScript || amount == null) {
|
|
1894
|
+
throw new Error("computeReclaimSighash: need witnessScript + amount (pass `opts` for a raw-hex tx)");
|
|
1895
|
+
}
|
|
1896
|
+
return tx.preimageWitnessV0(0, witnessScript, SIGHASH_ALL, amount);
|
|
1897
|
+
}
|
|
1898
|
+
function signReclaim(sighash, privateKey, opts) {
|
|
1899
|
+
const priv = toPrivBytes(privateKey);
|
|
1900
|
+
return concatBytes(import_utils.signECDSA(sighash, priv, opts?.lowR ?? false), Uint8Array.of(SIGHASH_ALL));
|
|
1901
|
+
}
|
|
1902
|
+
function finalizeReclaim(opts) {
|
|
1903
|
+
const { tx } = opts;
|
|
1904
|
+
const witnessScript = tx.getInput(0).witnessScript;
|
|
1905
|
+
if (!witnessScript)
|
|
1906
|
+
throw new Error("finalizeReclaim: input 0 has no witnessScript");
|
|
1907
|
+
tx.updateInput(0, { finalScriptWitness: reclaimWitness(opts, witnessScript) }, true);
|
|
1908
|
+
if (!tx.isFinal)
|
|
1909
|
+
throw new Error("finalizeReclaim: witness injection failed (tx not final)");
|
|
1910
|
+
return { txHex: tx.hex, txid: tx.id };
|
|
1911
|
+
}
|
|
1912
|
+
function reclaimWitness(opts, witnessScript) {
|
|
1913
|
+
const sigs = opts.tx.getInput(0).partialSig ?? [];
|
|
1914
|
+
const { stakerPubs, cosignerPubs } = decodeLockScript(witnessScript);
|
|
1915
|
+
if (stakerPubs.length !== 1) {
|
|
1916
|
+
throw new Error(`finalizeReclaim: expected exactly one staker public key in the lockup script, found ${stakerPubs.length} — multi-key subscripts are not supported`);
|
|
1917
|
+
}
|
|
1918
|
+
const stakerPub = stakerPubs[0];
|
|
1919
|
+
if (!stakerPub) {
|
|
1920
|
+
throw new Error("finalizeReclaim: expected exactly one staker public key in the lockup script, found 0 — multi-key subscripts are not supported");
|
|
1921
|
+
}
|
|
1922
|
+
const sigFor = (pub) => sigs.find(([p]) => p !== undefined && bytesEqual(p, pub))?.[1];
|
|
1923
|
+
const stakerSig = sigFor(stakerPub);
|
|
1924
|
+
if (!stakerSig)
|
|
1925
|
+
throw new Error("finalizeReclaim: missing staker signature (partialSig)");
|
|
1926
|
+
if (opts.path === "locktime")
|
|
1927
|
+
return [stakerSig, IF_SELECTOR, witnessScript];
|
|
1928
|
+
if (cosignerPubs.length !== 1) {
|
|
1929
|
+
throw new Error(`finalizeReclaim: expected exactly one cosigner public key in the lockup script, found ${cosignerPubs.length} — multi-key subscripts are not supported`);
|
|
1930
|
+
}
|
|
1931
|
+
const cosignerPub = cosignerPubs[0];
|
|
1932
|
+
if (!cosignerPub) {
|
|
1933
|
+
throw new Error("finalizeReclaim: expected exactly one cosigner public key in the lockup script, found 0 — multi-key subscripts are not supported");
|
|
1934
|
+
}
|
|
1935
|
+
const cosignerSig = sigFor(cosignerPub);
|
|
1936
|
+
if (!cosignerSig)
|
|
1937
|
+
throw new Error("finalizeReclaim: missing cosigner signature (partialSig)");
|
|
1938
|
+
return [
|
|
1939
|
+
stakerSig,
|
|
1940
|
+
cosignerSig,
|
|
1941
|
+
stakerPreimage(opts.stxAddress),
|
|
1942
|
+
ELSE_SELECTOR,
|
|
1943
|
+
witnessScript
|
|
1944
|
+
];
|
|
1945
|
+
}
|
|
1946
|
+
function reclaim(opts) {
|
|
1947
|
+
const tx = buildReclaim(opts);
|
|
1948
|
+
tx.signIdx(toPrivBytes(opts.stakerPrivateKey), 0);
|
|
1949
|
+
if (opts.path === "early-exit") {
|
|
1950
|
+
if (opts.cosignerPrivateKey == null) {
|
|
1951
|
+
throw new Error('reclaim: cosignerPrivateKey is required for path "early-exit"');
|
|
1952
|
+
}
|
|
1953
|
+
if (opts.stxAddress == null) {
|
|
1954
|
+
throw new Error('reclaim: stxAddress is required for path "early-exit" (preimage)');
|
|
1955
|
+
}
|
|
1956
|
+
tx.signIdx(toPrivBytes(opts.cosignerPrivateKey), 0);
|
|
1957
|
+
return finalizeReclaim({
|
|
1958
|
+
path: "early-exit",
|
|
1959
|
+
tx,
|
|
1960
|
+
stxAddress: opts.stxAddress
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
return finalizeReclaim({ path: "locktime", tx });
|
|
1964
|
+
}
|
|
1965
|
+
// src/pox5/extension.ts
|
|
1966
|
+
async function getStakerState(client, staker) {
|
|
1967
|
+
const contract = pox5ContractId(client);
|
|
1968
|
+
const results = await multicall(client, {
|
|
1969
|
+
allowFailure: false,
|
|
1970
|
+
calls: [
|
|
1971
|
+
{
|
|
1972
|
+
contract,
|
|
1973
|
+
functionName: "get-staker-info",
|
|
1974
|
+
args: [Cl.principal(staker)]
|
|
1975
|
+
},
|
|
1976
|
+
{
|
|
1977
|
+
contract,
|
|
1978
|
+
functionName: "get-bond-membership",
|
|
1979
|
+
args: [Cl.principal(staker)]
|
|
1980
|
+
},
|
|
1981
|
+
{
|
|
1982
|
+
contract,
|
|
1983
|
+
functionName: "get-staker-custodied-sbtc",
|
|
1984
|
+
args: [Cl.principal(staker)]
|
|
1985
|
+
},
|
|
1986
|
+
{ contract, functionName: "current-pox-reward-cycle", args: [] }
|
|
1987
|
+
]
|
|
1988
|
+
});
|
|
1989
|
+
const [stakerInfo, bondMembership, custodiedSbtc, currentCycle] = results;
|
|
1990
|
+
return {
|
|
1991
|
+
stakerInfo,
|
|
1992
|
+
bondMembership,
|
|
1993
|
+
custodiedSbtc,
|
|
1994
|
+
currentCycle
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
function pox5() {
|
|
1998
|
+
return (client) => ({
|
|
1999
|
+
pox5: {
|
|
2000
|
+
isActive: () => isPox5Active(client),
|
|
2001
|
+
getActivation: () => getPox5Activation(client),
|
|
2002
|
+
getPoxInfo: () => getPoxInfo(client),
|
|
2003
|
+
contractId: () => pox5ContractId(client),
|
|
2004
|
+
getStakerState: (staker) => getStakerState(client, staker),
|
|
2005
|
+
getStakerInfo: (staker) => getStakerInfo(client, staker),
|
|
2006
|
+
getBondMembership: (staker) => getBondMembership(client, staker),
|
|
2007
|
+
getProtocolBond: (bondIndex) => getProtocolBond(client, bondIndex),
|
|
2008
|
+
getBondAllowance: (bondIndex, staker) => getBondAllowance(client, bondIndex, staker),
|
|
2009
|
+
getTotalSbtcStakedForBond: (bondIndex) => getTotalSbtcStakedForBond(client, bondIndex),
|
|
1286
2010
|
getStakerCustodiedSbtc: (staker) => getStakerCustodiedSbtc(client, staker),
|
|
1287
2011
|
hasAnnouncedL1EarlyExit: (bondIndex, staker) => hasAnnouncedL1EarlyExit(client, bondIndex, staker),
|
|
1288
2012
|
getBondL1UnlockHeight: (bondIndex) => getBondL1UnlockHeight(client, bondIndex),
|
|
@@ -1290,6 +2014,18 @@ function pox5() {
|
|
|
1290
2014
|
verifySignerKeyGrant: (signerManager, signerKey) => verifySignerKeyGrantOnChain(client, signerManager, signerKey),
|
|
1291
2015
|
getCurrentRewardCycle: () => getCurrentRewardCycle(client),
|
|
1292
2016
|
getFirstRewardCycle: () => getFirstPox5RewardCycle(client),
|
|
2017
|
+
getEarned: (params) => getEarned(client, params),
|
|
2018
|
+
getEarnedStakerRewards: (params) => getEarnedStakerRewards(client, params),
|
|
2019
|
+
getLastRewardComputeHeight: () => getLastRewardComputeHeight(client),
|
|
2020
|
+
getTotalSharesStakedForCycle: (rewardCycle, bondIndex) => getTotalSharesStakedForCycle(client, rewardCycle, bondIndex),
|
|
2021
|
+
eligibleStake: (params) => eligibleStake(client, params),
|
|
2022
|
+
eligibleRegisterForBond: (params) => eligibleRegisterForBond(client, params),
|
|
2023
|
+
eligibleUnstake: (params) => eligibleUnstake(client, params),
|
|
2024
|
+
eligibleUnstakeSbtc: (params) => eligibleUnstakeSbtc(client, params),
|
|
2025
|
+
eligibleClaimRewards: (params) => eligibleClaimRewards(client, params),
|
|
2026
|
+
eligibleGrantSignerKey: (params) => eligibleGrantSignerKey(client, params),
|
|
2027
|
+
eligibleSetBondAdmin: (params) => eligibleSetBondAdmin(client, params),
|
|
2028
|
+
eligiblePauseRewards: (params) => eligiblePauseRewards(client, params),
|
|
1293
2029
|
setupBond: (params) => setupBond(client, params),
|
|
1294
2030
|
registerForBond: (params) => registerForBond(client, params),
|
|
1295
2031
|
updateBondRegistration: (params) => updateBondRegistration(client, params),
|
|
@@ -1302,10 +2038,188 @@ function pox5() {
|
|
|
1302
2038
|
claimRewards: (params) => claimRewards(client, params),
|
|
1303
2039
|
claimStakerRewardsForSigner: (params) => claimStakerRewardsForSigner(client, params),
|
|
1304
2040
|
grantSignerKey: (params) => grantSignerKey(client, params),
|
|
1305
|
-
revokeSignerGrant: (params) => revokeSignerGrant(client, params)
|
|
2041
|
+
revokeSignerGrant: (params) => revokeSignerGrant(client, params),
|
|
2042
|
+
setBondAdmin: (params) => setBondAdmin(client, params),
|
|
2043
|
+
setPauseAdmin: (params) => setPauseAdmin(client, params),
|
|
2044
|
+
pauseRewards: (params) => pauseRewards(client, params),
|
|
2045
|
+
buildLockProof: (opts) => buildPox5LockProof(opts)
|
|
1306
2046
|
}
|
|
1307
2047
|
});
|
|
1308
2048
|
}
|
|
2049
|
+
// src/pox5/btcAddress.ts
|
|
2050
|
+
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
2051
|
+
function base58Decode(str) {
|
|
2052
|
+
let num = 0n;
|
|
2053
|
+
for (const char of str) {
|
|
2054
|
+
const idx = BASE58_ALPHABET.indexOf(char);
|
|
2055
|
+
if (idx === -1)
|
|
2056
|
+
throw new Error(`Invalid base58 character: ${char}`);
|
|
2057
|
+
num = num * 58n + BigInt(idx);
|
|
2058
|
+
}
|
|
2059
|
+
const hex = num.toString(16).padStart(2, "0");
|
|
2060
|
+
const padded = hex.length % 2 ? `0${hex}` : hex;
|
|
2061
|
+
const rawBytes = hexToBytes(padded);
|
|
2062
|
+
let leadingZeros = 0;
|
|
2063
|
+
for (const char of str) {
|
|
2064
|
+
if (char === "1")
|
|
2065
|
+
leadingZeros++;
|
|
2066
|
+
else
|
|
2067
|
+
break;
|
|
2068
|
+
}
|
|
2069
|
+
const result = new Uint8Array(leadingZeros + rawBytes.length);
|
|
2070
|
+
result.set(rawBytes, leadingZeros);
|
|
2071
|
+
return result;
|
|
2072
|
+
}
|
|
2073
|
+
function base58CheckEncode(version, payload) {
|
|
2074
|
+
const data = concatBytes(Uint8Array.of(version), payload);
|
|
2075
|
+
const checksum = doubleSha256(data).slice(0, 4);
|
|
2076
|
+
return base58.encode(concatBytes(data, checksum));
|
|
2077
|
+
}
|
|
2078
|
+
function expectedHashLen(version) {
|
|
2079
|
+
if (!Number.isInteger(version) || version < 0 || version > 6) {
|
|
2080
|
+
throw new Error(`Unknown PoX address version: 0x${version.toString(16)}`);
|
|
2081
|
+
}
|
|
2082
|
+
return version <= 4 ? 20 : 32;
|
|
2083
|
+
}
|
|
2084
|
+
function canonicalizeBtcAddress(repr) {
|
|
2085
|
+
const expected = expectedHashLen(repr.version);
|
|
2086
|
+
const { hashbytes } = repr;
|
|
2087
|
+
if (hashbytes.length === expected)
|
|
2088
|
+
return { version: repr.version, hashbytes };
|
|
2089
|
+
if (expected === 20 && hashbytes.length === 32 && hashbytes.slice(20).every((b) => b === 0)) {
|
|
2090
|
+
return { version: repr.version, hashbytes: hashbytes.slice(0, 20) };
|
|
2091
|
+
}
|
|
2092
|
+
throw new Error(`Invalid hashbytes length for PoX version 0x${repr.version.toString(16)}: ${hashbytes.length}`);
|
|
2093
|
+
}
|
|
2094
|
+
function parseBtcAddress(address) {
|
|
2095
|
+
if (address.startsWith("bc1") || address.startsWith("tb1") || address.startsWith("bcrt1")) {
|
|
2096
|
+
return parseSegwitAddress(address);
|
|
2097
|
+
}
|
|
2098
|
+
return parseLegacyAddress(address);
|
|
2099
|
+
}
|
|
2100
|
+
function parseSegwitAddress(address) {
|
|
2101
|
+
let decoded;
|
|
2102
|
+
try {
|
|
2103
|
+
decoded = bech32.decode(address);
|
|
2104
|
+
} catch {
|
|
2105
|
+
decoded = bech32m.decode(address);
|
|
2106
|
+
}
|
|
2107
|
+
const witnessVersion = decoded.words[0];
|
|
2108
|
+
if (witnessVersion === undefined) {
|
|
2109
|
+
throw new Error("Invalid segwit address: missing witness version");
|
|
2110
|
+
}
|
|
2111
|
+
const data = bech32.fromWords(decoded.words.slice(1));
|
|
2112
|
+
const hashbytes = new Uint8Array(data);
|
|
2113
|
+
let version;
|
|
2114
|
+
if (witnessVersion === 0) {
|
|
2115
|
+
if (hashbytes.length === 20) {
|
|
2116
|
+
version = POX_ADDRESS_VERSION.p2wpkh;
|
|
2117
|
+
} else if (hashbytes.length === 32) {
|
|
2118
|
+
version = POX_ADDRESS_VERSION.p2wsh;
|
|
2119
|
+
} else {
|
|
2120
|
+
throw new Error(`Invalid witness v0 program length: ${hashbytes.length}`);
|
|
2121
|
+
}
|
|
2122
|
+
} else if (witnessVersion === 1) {
|
|
2123
|
+
if (hashbytes.length !== 32) {
|
|
2124
|
+
throw new Error(`Invalid witness v1 program length: ${hashbytes.length}`);
|
|
2125
|
+
}
|
|
2126
|
+
version = POX_ADDRESS_VERSION.p2tr;
|
|
2127
|
+
} else {
|
|
2128
|
+
throw new Error(`Unsupported witness version: ${witnessVersion}`);
|
|
2129
|
+
}
|
|
2130
|
+
return { version, hashbytes };
|
|
2131
|
+
}
|
|
2132
|
+
function parseLegacyAddress(address) {
|
|
2133
|
+
const decoded = base58Decode(address);
|
|
2134
|
+
if (decoded.length !== 25) {
|
|
2135
|
+
throw new Error(`Invalid legacy address length: ${decoded.length}`);
|
|
2136
|
+
}
|
|
2137
|
+
const versionByte = decoded[0];
|
|
2138
|
+
if (versionByte === undefined) {
|
|
2139
|
+
throw new Error("Invalid legacy address: missing version byte");
|
|
2140
|
+
}
|
|
2141
|
+
const hashbytes = decoded.slice(1, 21);
|
|
2142
|
+
let version;
|
|
2143
|
+
if (versionByte === 0 || versionByte === 111) {
|
|
2144
|
+
version = POX_ADDRESS_VERSION.p2pkh;
|
|
2145
|
+
} else if (versionByte === 5 || versionByte === 196) {
|
|
2146
|
+
version = POX_ADDRESS_VERSION.p2sh;
|
|
2147
|
+
} else {
|
|
2148
|
+
throw new Error(`Unknown legacy address version byte: 0x${versionByte.toString(16)}`);
|
|
2149
|
+
}
|
|
2150
|
+
return { version, hashbytes };
|
|
2151
|
+
}
|
|
2152
|
+
function stringifyBtcAddress(repr, network) {
|
|
2153
|
+
const { version, hashbytes } = canonicalizeBtcAddress(repr);
|
|
2154
|
+
const params = BITCOIN_NETWORK_PARAMS[network];
|
|
2155
|
+
switch (version) {
|
|
2156
|
+
case POX_ADDRESS_VERSION.p2pkh:
|
|
2157
|
+
return base58CheckEncode(params.p2pkh, hashbytes);
|
|
2158
|
+
case POX_ADDRESS_VERSION.p2sh:
|
|
2159
|
+
case POX_ADDRESS_VERSION.p2sh_p2wpkh:
|
|
2160
|
+
case POX_ADDRESS_VERSION.p2sh_p2wsh:
|
|
2161
|
+
return base58CheckEncode(params.p2sh, hashbytes);
|
|
2162
|
+
case POX_ADDRESS_VERSION.p2wpkh:
|
|
2163
|
+
case POX_ADDRESS_VERSION.p2wsh:
|
|
2164
|
+
return bech32.encode(params.hrp, [0, ...bech32.toWords(hashbytes)]);
|
|
2165
|
+
case POX_ADDRESS_VERSION.p2tr:
|
|
2166
|
+
return bech32m.encode(params.hrp, [1, ...bech32m.toWords(hashbytes)]);
|
|
2167
|
+
default:
|
|
2168
|
+
throw new Error(`Unknown PoX address version: 0x${version.toString(16)}`);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
var BtcAddress = {
|
|
2172
|
+
parse: parseBtcAddress,
|
|
2173
|
+
stringify: stringifyBtcAddress
|
|
2174
|
+
};
|
|
2175
|
+
// src/pox5/signerCalldata.ts
|
|
2176
|
+
function buildSignerCalldata(opts) {
|
|
2177
|
+
const repr = canonicalizeBtcAddress(typeof opts.poxAddress === "string" ? parseBtcAddress(opts.poxAddress) : opts.poxAddress);
|
|
2178
|
+
return serializeCVBytes(Cl.tuple({
|
|
2179
|
+
"pox-addr": Cl.tuple({
|
|
2180
|
+
version: Cl.buffer(Uint8Array.of(repr.version)),
|
|
2181
|
+
hashbytes: Cl.buffer(repr.hashbytes)
|
|
2182
|
+
}),
|
|
2183
|
+
"max-fee": Cl.uint(opts.maxFeeSats)
|
|
2184
|
+
}));
|
|
2185
|
+
}
|
|
2186
|
+
function parseSignerCalldata(calldata) {
|
|
2187
|
+
const cv = deserializeCVBytes(calldata);
|
|
2188
|
+
if (cv.type !== "tuple") {
|
|
2189
|
+
throw new Error("signer calldata is not a Clarity tuple");
|
|
2190
|
+
}
|
|
2191
|
+
const poxAddr = cv.value["pox-addr"];
|
|
2192
|
+
const maxFee = cv.value["max-fee"];
|
|
2193
|
+
if (!poxAddr || poxAddr.type !== "tuple") {
|
|
2194
|
+
throw new Error("signer calldata missing pox-addr tuple");
|
|
2195
|
+
}
|
|
2196
|
+
if (!maxFee || maxFee.type !== "uint") {
|
|
2197
|
+
throw new Error("signer calldata missing max-fee uint");
|
|
2198
|
+
}
|
|
2199
|
+
const versionCv = poxAddr.value.version;
|
|
2200
|
+
const hashCv = poxAddr.value.hashbytes;
|
|
2201
|
+
if (!versionCv || versionCv.type !== "buffer") {
|
|
2202
|
+
throw new Error("signer calldata pox-addr missing version buffer");
|
|
2203
|
+
}
|
|
2204
|
+
if (!hashCv || hashCv.type !== "buffer") {
|
|
2205
|
+
throw new Error("signer calldata pox-addr missing hashbytes buffer");
|
|
2206
|
+
}
|
|
2207
|
+
const versionBytes = hexToBytes(versionCv.value);
|
|
2208
|
+
if (versionBytes.length !== 1) {
|
|
2209
|
+
throw new Error(`signer calldata version must be buff 1, got ${versionBytes.length}`);
|
|
2210
|
+
}
|
|
2211
|
+
const version = versionBytes[0];
|
|
2212
|
+
if (version === undefined) {
|
|
2213
|
+
throw new Error("signer calldata version buffer is empty");
|
|
2214
|
+
}
|
|
2215
|
+
return {
|
|
2216
|
+
poxAddress: canonicalizeBtcAddress({
|
|
2217
|
+
version,
|
|
2218
|
+
hashbytes: hexToBytes(hashCv.value)
|
|
2219
|
+
}),
|
|
2220
|
+
maxFeeSats: maxFee.value
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
1309
2223
|
|
|
1310
|
-
//# debugId=
|
|
2224
|
+
//# debugId=55EF4405864CDEE264756E2164756E21
|
|
1311
2225
|
//# sourceMappingURL=index.cjs.map
|