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