@arkade-os/sdk 0.4.45 → 0.4.47
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/adapters/expo.cjs +5 -5
- package/dist/adapters/expo.d.cts +2 -2
- package/dist/adapters/expo.d.ts +2 -2
- package/dist/adapters/expo.js +3 -3
- package/dist/adapters/indexedDB.cjs +5 -5
- package/dist/adapters/indexedDB.js +4 -4
- package/dist/{ark-C-PjOngm.d.cts → ark-CoJgwTi_.d.cts} +31 -3
- package/dist/{ark-C-PjOngm.d.ts → ark-CoJgwTi_.d.ts} +31 -3
- package/dist/{asyncStorageTaskQueue-BmBPvTyh.d.cts → asyncStorageTaskQueue-Dowyo59a.d.cts} +2 -2
- package/dist/{asyncStorageTaskQueue-CHpI3moJ.d.ts → asyncStorageTaskQueue-Z3aFoXCl.d.ts} +2 -2
- package/dist/{chunk-QZ6HZZZK.js → chunk-3N5I6GT6.js} +3 -3
- package/dist/{chunk-QZ6HZZZK.js.map → chunk-3N5I6GT6.js.map} +1 -1
- package/dist/{chunk-BOA4SHCD.js → chunk-AJQ4AOT3.js} +4 -4
- package/dist/{chunk-BOA4SHCD.js.map → chunk-AJQ4AOT3.js.map} +1 -1
- package/dist/{chunk-BE5MUB7T.js → chunk-C7TXXTMS.js} +9 -5
- package/dist/chunk-C7TXXTMS.js.map +1 -0
- package/dist/{chunk-YBQV7XBC.js → chunk-CDDWXBEM.js} +139 -21
- package/dist/chunk-CDDWXBEM.js.map +1 -0
- package/dist/{chunk-UHOHLN3C.cjs → chunk-HHV5FPC6.cjs} +148 -25
- package/dist/chunk-HHV5FPC6.cjs.map +1 -0
- package/dist/{chunk-HZ7VIN7P.cjs → chunk-JAYOXOLT.cjs} +14 -14
- package/dist/{chunk-HZ7VIN7P.cjs.map → chunk-JAYOXOLT.cjs.map} +1 -1
- package/dist/{chunk-HXDZ2QTW.cjs → chunk-L5ND2FJJ.cjs} +9 -5
- package/dist/chunk-L5ND2FJJ.cjs.map +1 -0
- package/dist/{chunk-TGRJ5WAX.js → chunk-OCS5S3L3.js} +1085 -103
- package/dist/chunk-OCS5S3L3.js.map +1 -0
- package/dist/chunk-RQSA7ADZ.cjs +2286 -0
- package/dist/chunk-RQSA7ADZ.cjs.map +1 -0
- package/dist/{chunk-OFQGANH2.cjs → chunk-VE67GMGM.cjs} +7 -7
- package/dist/{chunk-OFQGANH2.cjs.map → chunk-VE67GMGM.cjs.map} +1 -1
- package/dist/{chunk-SRCWBFSZ.cjs → chunk-WTTLIFS7.cjs} +1281 -289
- package/dist/chunk-WTTLIFS7.cjs.map +1 -0
- package/dist/chunk-XN3554FT.js +2244 -0
- package/dist/chunk-XN3554FT.js.map +1 -0
- package/dist/contracts/handlers/index.cjs +11 -7
- package/dist/contracts/handlers/index.d.cts +3 -3
- package/dist/contracts/handlers/index.d.ts +3 -3
- package/dist/contracts/handlers/index.js +2 -2
- package/dist/{delegate-BKNqva1j.d.ts → delegate-CuaDqqQi.d.ts} +1 -1
- package/dist/{delegate-DOEobYwH.d.cts → delegate-Dl87_zMA.d.cts} +1 -1
- package/dist/index-BC_G0VTh.d.cts +512 -0
- package/dist/index-CKwpZEpw.d.ts +512 -0
- package/dist/index.cjs +219 -159
- package/dist/index.d.cts +3097 -2090
- package/dist/index.d.ts +3097 -2090
- package/dist/index.js +4 -4
- package/dist/repositories/realm/index.cjs +23 -19
- package/dist/repositories/realm/index.cjs.map +1 -1
- package/dist/repositories/realm/index.d.cts +2 -2
- package/dist/repositories/realm/index.d.ts +2 -2
- package/dist/repositories/realm/index.js +9 -5
- package/dist/repositories/realm/index.js.map +1 -1
- package/dist/repositories/sqlite/index.cjs +22 -18
- package/dist/repositories/sqlite/index.cjs.map +1 -1
- package/dist/repositories/sqlite/index.d.cts +2 -2
- package/dist/repositories/sqlite/index.d.ts +2 -2
- package/dist/repositories/sqlite/index.js +9 -5
- package/dist/repositories/sqlite/index.js.map +1 -1
- package/dist/{taskRunner-BngWSFMi.d.ts → taskRunner-Dz-TI4Rb.d.ts} +2 -2
- package/dist/{taskRunner-DOiyRuJa.d.cts → taskRunner-Dz47k0lp.d.cts} +2 -2
- package/dist/wallet/expo/background.cjs +14 -14
- package/dist/wallet/expo/background.d.cts +3 -3
- package/dist/wallet/expo/background.d.ts +3 -3
- package/dist/wallet/expo/background.js +6 -6
- package/dist/wallet/expo/index.cjs +18 -14
- package/dist/wallet/expo/index.cjs.map +1 -1
- package/dist/wallet/expo/index.d.cts +4 -4
- package/dist/wallet/expo/index.d.ts +4 -4
- package/dist/wallet/expo/index.js +9 -5
- package/dist/wallet/expo/index.js.map +1 -1
- package/dist/{wallet-DBjumS42.d.cts → wallet-Oxz_wLxo.d.cts} +47 -3
- package/dist/{wallet-B3awP1Nj.d.ts → wallet-x7egmXq6.d.ts} +47 -3
- package/dist/worker/expo/index.cjs +10 -9
- package/dist/worker/expo/index.cjs.map +1 -1
- package/dist/worker/expo/index.d.cts +4 -4
- package/dist/worker/expo/index.d.ts +4 -4
- package/dist/worker/expo/index.js +6 -5
- package/dist/worker/expo/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-32YKLQPS.cjs +0 -970
- package/dist/chunk-32YKLQPS.cjs.map +0 -1
- package/dist/chunk-BE5MUB7T.js.map +0 -1
- package/dist/chunk-HXDZ2QTW.cjs.map +0 -1
- package/dist/chunk-SRCWBFSZ.cjs.map +0 -1
- package/dist/chunk-TGRJ5WAX.js.map +0 -1
- package/dist/chunk-UHOHLN3C.cjs.map +0 -1
- package/dist/chunk-YBQV7XBC.js.map +0 -1
- package/dist/chunk-YNEDJTX2.js +0 -957
- package/dist/chunk-YNEDJTX2.js.map +0 -1
- package/dist/index-BlW4I30l.d.cts +0 -259
- package/dist/index-Cm2fGeox.d.ts +0 -259
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { contractHandlers, isMainnetDescriptor, descriptorIsOurs, DEFAULT_PAGE_SIZE, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, DefaultVtxo, BoardingContractHandler } from './chunk-
|
|
3
|
-
import { VtxoScript,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { Transaction, setArkPsbtField, PrevArkTxField, ConditionWitness, AssetId, AssetGroup, AssetInput, AssetOutput, Metadata, Packet, craftToSpendTx, getArkPsbtFields, CosignerPublicKey, BufferReader, BufferWriter, OP_RETURN_EMPTY_PKSCRIPT, VtxoTaprootTree, baseFetch, Intent, maybeArkError, isArkError, ArkErrorName, ProviderUnavailableError, FetchError, isFetchTimeoutError, AssetRef, isEventSourceError, RestArkProvider, RestIndexerProvider, ArkError } from './chunk-CDDWXBEM.js';
|
|
2
|
+
import { witnessRefToBytes, validateProgram, toASM, stringifyArtifact, serializeArkadeContractParams, resolveAsm, parseArtifact, getOpcodeValue, getOpcodeName, fromASM, deserializeArkadeContractParams, computeArkadeScriptPublicKey, bytesToASM, asmToBytes, arkadeWitnessHash, arkadeScriptHash, SUPPORTED_PROGRAM_VERSION, OPCODE_VALUES, OPCODE_NAMES, bignum_exports, ArkadeScript, ArkadeProgramScript, ARKADE_OPS, ARKADE_OPCODE_VALUES, ARKADE_OPCODE_NAMES, ARKADE_OPCODES, ARKADE_OP, inputName, contractHandlers, isMainnetDescriptor, descriptorIsOurs, DEFAULT_PAGE_SIZE, DelegateVtxo, WALLET_RECEIVE_SOURCE, deriveDescriptorLeafPubKey, DefaultVtxo, BoardingContractHandler } from './chunk-XN3554FT.js';
|
|
3
|
+
import { VtxoScript, CSVMultisigTapscript, DEFAULT_NETWORK, ArkAddress, sequenceToTimelock, decodeTapscript, scriptFromTapLeafScript, CLTVMultisigTapscript, timelockToSequence, DEFAULT_NETWORK_NAME, getSequence, getNetwork, MultisigTapscript, networks as networks$1, DEFAULT_ARKADE_SERVER_URL } from './chunk-C7TXXTMS.js';
|
|
4
|
+
import { __export } from './chunk-NSBPE2FW.js';
|
|
5
|
+
import { sha256, equalBytes, hash160, sha256x2, concatBytes, randomPrivateKeyBytes, pubECDSA, pubSchnorr } from '@scure/btc-signer/utils.js';
|
|
6
|
+
import { SigHash, Script, OP, OutScript, Address, RawWitness, p2tr, p2wpkh, TaprootControlBlock, DEFAULT_SEQUENCE, Transaction as Transaction$2 } from '@scure/btc-signer';
|
|
6
7
|
import { hex, base58, base64 } from '@scure/base';
|
|
7
8
|
import * as musig2 from '@scure/btc-signer/musig2.js';
|
|
8
|
-
import { equalBytes, bytesToNumberBE } from '@noble/curves/utils.js';
|
|
9
|
+
import { equalBytes as equalBytes$1, bytesToNumberBE } from '@noble/curves/utils.js';
|
|
9
10
|
import { signAsync, schnorr as schnorr$1, Point } from '@noble/secp256k1';
|
|
10
11
|
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
|
|
11
12
|
import { Script as Script$1 } from '@scure/btc-signer/script.js';
|
|
@@ -62,6 +63,8 @@ var PartialSig = class _PartialSig {
|
|
|
62
63
|
throw new PartialSignatureError("Invalid R length");
|
|
63
64
|
}
|
|
64
65
|
}
|
|
66
|
+
s;
|
|
67
|
+
R;
|
|
65
68
|
/**
|
|
66
69
|
* Encodes the partial signature into bytes
|
|
67
70
|
* Returns a 32-byte array containing just the s value
|
|
@@ -111,6 +114,7 @@ var TreeSignerSession = class _TreeSignerSession {
|
|
|
111
114
|
constructor(secretKey) {
|
|
112
115
|
this.secretKey = secretKey;
|
|
113
116
|
}
|
|
117
|
+
secretKey;
|
|
114
118
|
static NOT_INITIALIZED = new Error("session not initialized, call init method");
|
|
115
119
|
myNonces = null;
|
|
116
120
|
aggregateNonces = null;
|
|
@@ -337,6 +341,7 @@ var ReadonlySingleKey = class _ReadonlySingleKey {
|
|
|
337
341
|
throw new Error("Invalid public key length");
|
|
338
342
|
}
|
|
339
343
|
}
|
|
344
|
+
publicKey;
|
|
340
345
|
/**
|
|
341
346
|
* Create a ReadonlySingleKey from a compressed public key.
|
|
342
347
|
*
|
|
@@ -948,6 +953,7 @@ var RestDelegateProvider = class {
|
|
|
948
953
|
constructor(url) {
|
|
949
954
|
this.url = url;
|
|
950
955
|
}
|
|
956
|
+
url;
|
|
951
957
|
/**
|
|
952
958
|
* Submit a delegation request to the remote delegation service.
|
|
953
959
|
*
|
|
@@ -1017,6 +1023,7 @@ var EsploraProvider = class {
|
|
|
1017
1023
|
this.pollingInterval = opts?.pollingInterval ?? 15e3;
|
|
1018
1024
|
this.forcePolling = opts?.forcePolling ?? false;
|
|
1019
1025
|
}
|
|
1026
|
+
baseUrl;
|
|
1020
1027
|
pollingInterval;
|
|
1021
1028
|
forcePolling;
|
|
1022
1029
|
async getCoins(address) {
|
|
@@ -1496,6 +1503,8 @@ var UnknownPacket = class {
|
|
|
1496
1503
|
this.packetType = packetType;
|
|
1497
1504
|
this.data = data;
|
|
1498
1505
|
}
|
|
1506
|
+
packetType;
|
|
1507
|
+
data;
|
|
1499
1508
|
type() {
|
|
1500
1509
|
return this.packetType;
|
|
1501
1510
|
}
|
|
@@ -1504,6 +1513,62 @@ var UnknownPacket = class {
|
|
|
1504
1513
|
}
|
|
1505
1514
|
};
|
|
1506
1515
|
|
|
1516
|
+
// src/extension/emulator/packet.ts
|
|
1517
|
+
var EmulatorPacket = class _EmulatorPacket {
|
|
1518
|
+
constructor(entries) {
|
|
1519
|
+
this.entries = entries;
|
|
1520
|
+
}
|
|
1521
|
+
entries;
|
|
1522
|
+
/** PACKET_TYPE is the 1-byte TLV type tag used in the Extension envelope. */
|
|
1523
|
+
static PACKET_TYPE = 1;
|
|
1524
|
+
static create(entries) {
|
|
1525
|
+
if (entries.length === 0) {
|
|
1526
|
+
throw new Error("empty emulator packet");
|
|
1527
|
+
}
|
|
1528
|
+
for (const entry of entries) {
|
|
1529
|
+
if (entry.script.length === 0) {
|
|
1530
|
+
throw new Error(`empty script for vin ${entry.vin}`);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1534
|
+
for (const entry of entries) {
|
|
1535
|
+
if (seen.has(entry.vin)) {
|
|
1536
|
+
throw new Error(`duplicate vin ${entry.vin}`);
|
|
1537
|
+
}
|
|
1538
|
+
seen.add(entry.vin);
|
|
1539
|
+
}
|
|
1540
|
+
return new _EmulatorPacket(entries);
|
|
1541
|
+
}
|
|
1542
|
+
static fromBytes(data) {
|
|
1543
|
+
const reader = new BufferReader(data);
|
|
1544
|
+
const entryCount = reader.readCompactSize();
|
|
1545
|
+
const entries = [];
|
|
1546
|
+
for (let i = 0; i < entryCount; i++) {
|
|
1547
|
+
const vin = reader.readUint16LE();
|
|
1548
|
+
const script = reader.readCompactSlice();
|
|
1549
|
+
const witness = reader.readCompactSlice();
|
|
1550
|
+
entries.push({ vin, script, witness });
|
|
1551
|
+
}
|
|
1552
|
+
if (reader.remaining() > 0) {
|
|
1553
|
+
throw new Error(`unexpected ${reader.remaining()} trailing bytes`);
|
|
1554
|
+
}
|
|
1555
|
+
return _EmulatorPacket.create(entries);
|
|
1556
|
+
}
|
|
1557
|
+
type() {
|
|
1558
|
+
return _EmulatorPacket.PACKET_TYPE;
|
|
1559
|
+
}
|
|
1560
|
+
serialize() {
|
|
1561
|
+
const writer = new BufferWriter();
|
|
1562
|
+
writer.writeCompactSize(this.entries.length);
|
|
1563
|
+
for (const entry of this.entries) {
|
|
1564
|
+
writer.writeUint16LE(entry.vin);
|
|
1565
|
+
writer.writeCompactSlice(entry.script);
|
|
1566
|
+
writer.writeCompactSlice(entry.witness ?? new Uint8Array(0));
|
|
1567
|
+
}
|
|
1568
|
+
return writer.toBytes();
|
|
1569
|
+
}
|
|
1570
|
+
};
|
|
1571
|
+
|
|
1507
1572
|
// src/extension/index.ts
|
|
1508
1573
|
var ARKADE_MAGIC = new Uint8Array([65, 82, 75]);
|
|
1509
1574
|
var ExtensionNotFoundError = class extends Error {
|
|
@@ -1516,16 +1581,21 @@ var Extension = class _Extension {
|
|
|
1516
1581
|
constructor(packets) {
|
|
1517
1582
|
this.packets = packets;
|
|
1518
1583
|
}
|
|
1584
|
+
packets;
|
|
1519
1585
|
static create(packets) {
|
|
1520
1586
|
if (packets.length === 0) {
|
|
1521
1587
|
throw new Error("missing packets");
|
|
1522
1588
|
}
|
|
1523
1589
|
const seen = /* @__PURE__ */ new Set();
|
|
1524
1590
|
for (const p of packets) {
|
|
1525
|
-
|
|
1526
|
-
|
|
1591
|
+
const type = p.type();
|
|
1592
|
+
if (!Number.isInteger(type) || type < 0 || type > 255) {
|
|
1593
|
+
throw new Error(`invalid packet type ${type}: must be an integer in [0, 255]`);
|
|
1527
1594
|
}
|
|
1528
|
-
seen.
|
|
1595
|
+
if (seen.has(type)) {
|
|
1596
|
+
throw new Error(`duplicate packet type ${type}`);
|
|
1597
|
+
}
|
|
1598
|
+
seen.add(type);
|
|
1529
1599
|
}
|
|
1530
1600
|
return new _Extension(packets);
|
|
1531
1601
|
}
|
|
@@ -1539,7 +1609,7 @@ var Extension = class _Extension {
|
|
|
1539
1609
|
if (decoded.length < 2 || decoded[0] !== "RETURN") return false;
|
|
1540
1610
|
const data = decoded[1];
|
|
1541
1611
|
if (!(data instanceof Uint8Array)) return false;
|
|
1542
|
-
return data.length >= ARKADE_MAGIC.length && equalBytes
|
|
1612
|
+
return data.length >= ARKADE_MAGIC.length && equalBytes(data.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC);
|
|
1543
1613
|
} catch {
|
|
1544
1614
|
return false;
|
|
1545
1615
|
}
|
|
@@ -1570,7 +1640,7 @@ var Extension = class _Extension {
|
|
|
1570
1640
|
payload.set(d, offset);
|
|
1571
1641
|
offset += d.length;
|
|
1572
1642
|
}
|
|
1573
|
-
if (payload.length < ARKADE_MAGIC.length || !equalBytes
|
|
1643
|
+
if (payload.length < ARKADE_MAGIC.length || !equalBytes(payload.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC)) {
|
|
1574
1644
|
throw new Error(
|
|
1575
1645
|
`expected magic prefix ${hex.encode(ARKADE_MAGIC)}, got ${hex.encode(payload.slice(0, Math.min(payload.length, ARKADE_MAGIC.length)))}`
|
|
1576
1646
|
);
|
|
@@ -1655,12 +1725,45 @@ var Extension = class _Extension {
|
|
|
1655
1725
|
}
|
|
1656
1726
|
return null;
|
|
1657
1727
|
}
|
|
1728
|
+
/**
|
|
1729
|
+
* getEmulatorPacket returns the embedded EmulatorPacket, or null if not present.
|
|
1730
|
+
*/
|
|
1731
|
+
getEmulatorPacket() {
|
|
1732
|
+
for (const p of this.packets) {
|
|
1733
|
+
if (p instanceof EmulatorPacket) {
|
|
1734
|
+
return p;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return null;
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* getPacketByType returns the first packet matching the given type tag, or null.
|
|
1741
|
+
*/
|
|
1742
|
+
getPacketByType(packetType) {
|
|
1743
|
+
for (const p of this.packets) {
|
|
1744
|
+
if (p.type() === packetType) {
|
|
1745
|
+
return p;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return null;
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* Returns all embedded packets in insertion order. Used when callers need
|
|
1752
|
+
* to rebuild an Extension from an existing one (e.g. appending a new packet).
|
|
1753
|
+
*/
|
|
1754
|
+
getPackets() {
|
|
1755
|
+
return this.packets;
|
|
1756
|
+
}
|
|
1658
1757
|
};
|
|
1659
1758
|
function parsePacket(packetType, data) {
|
|
1660
|
-
|
|
1661
|
-
|
|
1759
|
+
switch (packetType) {
|
|
1760
|
+
case Packet.PACKET_TYPE:
|
|
1761
|
+
return Packet.fromBytes(data);
|
|
1762
|
+
case EmulatorPacket.PACKET_TYPE:
|
|
1763
|
+
return EmulatorPacket.fromBytes(data);
|
|
1764
|
+
default:
|
|
1765
|
+
return new UnknownPacket(packetType, data);
|
|
1662
1766
|
}
|
|
1663
|
-
return new UnknownPacket(packetType, data);
|
|
1664
1767
|
}
|
|
1665
1768
|
function encodeVarUint(value) {
|
|
1666
1769
|
const bytes = [];
|
|
@@ -1741,7 +1844,7 @@ function validateOnchainRecipient(commitmentTx, recipient, network, usedOutputs)
|
|
|
1741
1844
|
if (!output?.script || output.script.length === 0) {
|
|
1742
1845
|
continue;
|
|
1743
1846
|
}
|
|
1744
|
-
if (equalBytes
|
|
1847
|
+
if (equalBytes(output.script, expectedPkScript)) {
|
|
1745
1848
|
if (output.amount !== BigInt(recipient.amount)) {
|
|
1746
1849
|
continue;
|
|
1747
1850
|
}
|
|
@@ -1765,7 +1868,7 @@ function validateOffchainRecipient(leaves, arkAddress, recipient, usedOutputs) {
|
|
|
1765
1868
|
if (!output?.script || output.script.length === 0) {
|
|
1766
1869
|
continue;
|
|
1767
1870
|
}
|
|
1768
|
-
if (!equalBytes
|
|
1871
|
+
if (!equalBytes(output.script, expectedPkScript)) {
|
|
1769
1872
|
continue;
|
|
1770
1873
|
}
|
|
1771
1874
|
if (output.amount !== expectedAmount) {
|
|
@@ -1954,6 +2057,7 @@ function isExpired(vtxo) {
|
|
|
1954
2057
|
return expiry <= Date.now();
|
|
1955
2058
|
}
|
|
1956
2059
|
function isSubdust(vtxo, dust) {
|
|
2060
|
+
if (typeof vtxo === "bigint") return vtxo < dust;
|
|
1957
2061
|
return vtxo.value < dust;
|
|
1958
2062
|
}
|
|
1959
2063
|
|
|
@@ -2293,6 +2397,46 @@ function isValidArkAddress(address) {
|
|
|
2293
2397
|
return false;
|
|
2294
2398
|
}
|
|
2295
2399
|
}
|
|
2400
|
+
async function submitOffchainTx(provider, offchainTx, signer, hooks) {
|
|
2401
|
+
const { arkTx: signedArkTx, userSignedCheckpoints } = await signer.signArkTx(
|
|
2402
|
+
offchainTx.arkTx,
|
|
2403
|
+
offchainTx.checkpoints
|
|
2404
|
+
);
|
|
2405
|
+
if (userSignedCheckpoints && userSignedCheckpoints.length !== offchainTx.checkpoints.length) {
|
|
2406
|
+
throw new Error(
|
|
2407
|
+
`signer returned ${userSignedCheckpoints.length} signed checkpoints, expected ${offchainTx.checkpoints.length}`
|
|
2408
|
+
);
|
|
2409
|
+
}
|
|
2410
|
+
await hooks?.beforeSubmit?.();
|
|
2411
|
+
const { arkTxid, signedCheckpointTxs } = await provider.submitTx(
|
|
2412
|
+
base64.encode(signedArkTx.toPSBT()),
|
|
2413
|
+
offchainTx.checkpoints.map((c) => base64.encode(c.toPSBT()))
|
|
2414
|
+
);
|
|
2415
|
+
if (signedCheckpointTxs.length !== offchainTx.checkpoints.length) {
|
|
2416
|
+
throw new Error(
|
|
2417
|
+
`submitTx returned ${signedCheckpointTxs.length} checkpoints, expected ${offchainTx.checkpoints.length}`
|
|
2418
|
+
);
|
|
2419
|
+
}
|
|
2420
|
+
let finalCheckpoints;
|
|
2421
|
+
if (userSignedCheckpoints) {
|
|
2422
|
+
finalCheckpoints = signedCheckpointTxs.map((c, i) => {
|
|
2423
|
+
const serverSigned = Transaction.fromPSBT(base64.decode(c));
|
|
2424
|
+
combineTapscriptSigs(userSignedCheckpoints[i], serverSigned);
|
|
2425
|
+
return base64.encode(serverSigned.toPSBT());
|
|
2426
|
+
});
|
|
2427
|
+
} else {
|
|
2428
|
+
finalCheckpoints = await Promise.all(
|
|
2429
|
+
signedCheckpointTxs.map(async (c) => {
|
|
2430
|
+
const tx = Transaction.fromPSBT(base64.decode(c));
|
|
2431
|
+
const signed = await signer.signCheckpoint(tx);
|
|
2432
|
+
return base64.encode(signed.toPSBT());
|
|
2433
|
+
})
|
|
2434
|
+
);
|
|
2435
|
+
}
|
|
2436
|
+
await provider.finalizeTx(arkTxid, finalCheckpoints);
|
|
2437
|
+
await hooks?.afterFinalize?.();
|
|
2438
|
+
return { arkTxid, signedCheckpointTxs };
|
|
2439
|
+
}
|
|
2296
2440
|
var AmountVariableName = "amount";
|
|
2297
2441
|
var ExpiryVariableName = "expiry";
|
|
2298
2442
|
var BirthVariableName = "birth";
|
|
@@ -2312,6 +2456,7 @@ var FeeAmount = class _FeeAmount {
|
|
|
2312
2456
|
constructor(value) {
|
|
2313
2457
|
this.value = value;
|
|
2314
2458
|
}
|
|
2459
|
+
value;
|
|
2315
2460
|
static ZERO = new _FeeAmount(0);
|
|
2316
2461
|
/** Returns the fee amount rounded up to whole satoshis. */
|
|
2317
2462
|
get satoshis() {
|
|
@@ -2336,6 +2481,7 @@ var Estimator = class {
|
|
|
2336
2481
|
this.intentOffchainOutput = config.offchainOutput ? parseProgram(config.offchainOutput, IntentOutputEnv) : void 0;
|
|
2337
2482
|
this.intentOnchainOutput = config.onchainOutput ? parseProgram(config.onchainOutput, IntentOutputEnv) : void 0;
|
|
2338
2483
|
}
|
|
2484
|
+
config;
|
|
2339
2485
|
intentOffchainInput;
|
|
2340
2486
|
intentOnchainInput;
|
|
2341
2487
|
intentOffchainOutput;
|
|
@@ -2446,6 +2592,14 @@ function parseProgram(text, env) {
|
|
|
2446
2592
|
}
|
|
2447
2593
|
return { program, text };
|
|
2448
2594
|
}
|
|
2595
|
+
|
|
2596
|
+
// src/contracts/types.ts
|
|
2597
|
+
function isDiscoverable(handler) {
|
|
2598
|
+
return !!handler && typeof handler.discoverAt === "function";
|
|
2599
|
+
}
|
|
2600
|
+
function isTapscriptDeriving(handler) {
|
|
2601
|
+
return !!handler && typeof handler.deriveTapscripts === "function";
|
|
2602
|
+
}
|
|
2449
2603
|
var DUST_AMOUNT = 546;
|
|
2450
2604
|
var FALLBACK_WALLET_DUST_AMOUNT = 330n;
|
|
2451
2605
|
function getDustAmount(wallet) {
|
|
@@ -2465,10 +2619,14 @@ function deriveContractTapscripts(contract) {
|
|
|
2465
2619
|
throw new Error(`No handler for contract type '${contract.type}'`);
|
|
2466
2620
|
}
|
|
2467
2621
|
const script = handler.createScript(contract.params);
|
|
2622
|
+
if (isTapscriptDeriving(handler)) {
|
|
2623
|
+
return handler.deriveTapscripts(script, contract);
|
|
2624
|
+
}
|
|
2625
|
+
const legacy = script;
|
|
2468
2626
|
return {
|
|
2469
|
-
forfeitTapLeafScript:
|
|
2470
|
-
intentTapLeafScript:
|
|
2471
|
-
tapTree:
|
|
2627
|
+
forfeitTapLeafScript: legacy.forfeit(),
|
|
2628
|
+
intentTapLeafScript: legacy.forfeit(),
|
|
2629
|
+
tapTree: legacy.encode()
|
|
2472
2630
|
};
|
|
2473
2631
|
}
|
|
2474
2632
|
function cloneTapLeafScript([
|
|
@@ -2738,6 +2896,8 @@ var VtxoManager = class _VtxoManager {
|
|
|
2738
2896
|
}
|
|
2739
2897
|
this.contractEventsSubscriptionReady = this.initializeSubscription();
|
|
2740
2898
|
}
|
|
2899
|
+
wallet;
|
|
2900
|
+
renewalConfig;
|
|
2741
2901
|
settlementConfig;
|
|
2742
2902
|
contractEventsSubscriptionReady;
|
|
2743
2903
|
disposePromise;
|
|
@@ -3775,7 +3935,7 @@ var VtxoManager = class _VtxoManager {
|
|
|
3775
3935
|
*/
|
|
3776
3936
|
extractSpentOutpoint(error) {
|
|
3777
3937
|
const ark = maybeArkError(error);
|
|
3778
|
-
if (!ark
|
|
3938
|
+
if (!isArkError(ark, ArkErrorName.VTXO_ALREADY_SPENT)) return void 0;
|
|
3779
3939
|
const raw = ark.metadata?.vtxo_outpoint;
|
|
3780
3940
|
if (typeof raw !== "string") return void 0;
|
|
3781
3941
|
const [txid, voutStr] = raw.split(":");
|
|
@@ -4079,6 +4239,9 @@ var ArkNote = class _ArkNote {
|
|
|
4079
4239
|
this.status = { confirmed: true };
|
|
4080
4240
|
this.extraWitness = [this.preimage];
|
|
4081
4241
|
}
|
|
4242
|
+
preimage;
|
|
4243
|
+
value;
|
|
4244
|
+
HRP;
|
|
4082
4245
|
static DefaultHRP = "arknote";
|
|
4083
4246
|
static PreimageLength = 32;
|
|
4084
4247
|
// 32 bytes for the preimage
|
|
@@ -4455,6 +4618,7 @@ var IndexerExitDataSource = class {
|
|
|
4455
4618
|
constructor(indexer) {
|
|
4456
4619
|
this.indexer = indexer;
|
|
4457
4620
|
}
|
|
4621
|
+
indexer;
|
|
4458
4622
|
name = "indexer";
|
|
4459
4623
|
async getVtxoChain(vtxo) {
|
|
4460
4624
|
const chain = [];
|
|
@@ -4516,6 +4680,7 @@ var RepositoryExitDataSource = class {
|
|
|
4516
4680
|
constructor(repo) {
|
|
4517
4681
|
this.repo = repo;
|
|
4518
4682
|
}
|
|
4683
|
+
repo;
|
|
4519
4684
|
name = "repository";
|
|
4520
4685
|
async getVtxoChain(vtxo) {
|
|
4521
4686
|
const branch = await this.repo.getBranch(vtxo);
|
|
@@ -4553,6 +4718,8 @@ var OrderedExitChainResolver = class {
|
|
|
4553
4718
|
this.sources = sources;
|
|
4554
4719
|
this.persist = persist;
|
|
4555
4720
|
}
|
|
4721
|
+
sources;
|
|
4722
|
+
persist;
|
|
4556
4723
|
async getVtxoChain(vtxo) {
|
|
4557
4724
|
let lastErr;
|
|
4558
4725
|
for (const source of this.sources) {
|
|
@@ -4878,6 +5045,8 @@ var TxTree = class {
|
|
|
4878
5045
|
this.root = root;
|
|
4879
5046
|
this.children = children;
|
|
4880
5047
|
}
|
|
5048
|
+
root;
|
|
5049
|
+
children;
|
|
4881
5050
|
static create(chunks) {
|
|
4882
5051
|
if (chunks.length === 0) {
|
|
4883
5052
|
throw new Error("empty chunks");
|
|
@@ -5349,6 +5518,7 @@ var ReadonlyAssetManager = class {
|
|
|
5349
5518
|
constructor(indexer) {
|
|
5350
5519
|
this.indexer = indexer;
|
|
5351
5520
|
}
|
|
5521
|
+
indexer;
|
|
5352
5522
|
async getAssetDetails(assetId) {
|
|
5353
5523
|
return this.indexer.getAssetDetails(assetId);
|
|
5354
5524
|
}
|
|
@@ -5358,6 +5528,7 @@ var AssetManager = class extends ReadonlyAssetManager {
|
|
|
5358
5528
|
super(wallet.indexerProvider);
|
|
5359
5529
|
this.wallet = wallet;
|
|
5360
5530
|
}
|
|
5531
|
+
wallet;
|
|
5361
5532
|
/**
|
|
5362
5533
|
* Issue a new asset.
|
|
5363
5534
|
* @param params - Parameters for asset issuance
|
|
@@ -5679,6 +5850,9 @@ var DelegateManagerImpl = class {
|
|
|
5679
5850
|
this.arkInfoProvider = arkInfoProvider;
|
|
5680
5851
|
this.identity = identity;
|
|
5681
5852
|
}
|
|
5853
|
+
delegateProvider;
|
|
5854
|
+
arkInfoProvider;
|
|
5855
|
+
identity;
|
|
5682
5856
|
async getDelegateInfo() {
|
|
5683
5857
|
return this.delegateProvider.getDelegateInfo();
|
|
5684
5858
|
}
|
|
@@ -5845,12 +6019,12 @@ async function delegate(identity, delegateProvider, arkInfo, delegateInfo, vtxos
|
|
|
5845
6019
|
script: hex.encode(output.script)
|
|
5846
6020
|
}).satoshis;
|
|
5847
6021
|
}, 0);
|
|
5848
|
-
if (amount - BigInt(outputFee)
|
|
6022
|
+
if (isSubdust(amount - BigInt(outputFee), dust)) {
|
|
5849
6023
|
throw new Error("Amount is below dust limit, cannot delegate");
|
|
5850
6024
|
}
|
|
5851
6025
|
amount -= BigInt(outputFee);
|
|
5852
6026
|
amount -= delegateFee;
|
|
5853
|
-
if (amount
|
|
6027
|
+
if (isSubdust(amount, dust)) {
|
|
5854
6028
|
throw new Error("Amount is below dust limit, cannot delegate");
|
|
5855
6029
|
}
|
|
5856
6030
|
outputs.push({
|
|
@@ -5961,7 +6135,7 @@ async function makeSignedDelegateIntent(identity, coins, outputs, onchainOutputs
|
|
|
5961
6135
|
};
|
|
5962
6136
|
}
|
|
5963
6137
|
function findDestinationOutputIndex(outputs, destinationScript) {
|
|
5964
|
-
return outputs.findIndex((o) => o.script && equalBytes
|
|
6138
|
+
return outputs.findIndex((o) => o.script && equalBytes(o.script, destinationScript));
|
|
5965
6139
|
}
|
|
5966
6140
|
function getDayTimestamp(timestamp) {
|
|
5967
6141
|
const date = new Date(timestamp);
|
|
@@ -6447,6 +6621,7 @@ var IndexedDBVirtualTxRepository = class {
|
|
|
6447
6621
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
6448
6622
|
this.dbName = dbName;
|
|
6449
6623
|
}
|
|
6624
|
+
dbName;
|
|
6450
6625
|
version = 1;
|
|
6451
6626
|
db = null;
|
|
6452
6627
|
async getDB() {
|
|
@@ -6576,6 +6751,7 @@ var IndexedDBIntentRepository = class {
|
|
|
6576
6751
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
6577
6752
|
this.dbName = dbName;
|
|
6578
6753
|
}
|
|
6754
|
+
dbName;
|
|
6579
6755
|
version = 1;
|
|
6580
6756
|
db = null;
|
|
6581
6757
|
async getDB() {
|
|
@@ -6886,6 +7062,7 @@ var IndexedDBContractRepository = class {
|
|
|
6886
7062
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
6887
7063
|
this.dbName = dbName;
|
|
6888
7064
|
}
|
|
7065
|
+
dbName;
|
|
6889
7066
|
version = 1;
|
|
6890
7067
|
db = null;
|
|
6891
7068
|
async clear() {
|
|
@@ -7052,6 +7229,7 @@ var IndexedDBWalletRepository = class {
|
|
|
7052
7229
|
constructor(dbName = DEFAULT_DB_NAME) {
|
|
7053
7230
|
this.dbName = dbName;
|
|
7054
7231
|
}
|
|
7232
|
+
dbName;
|
|
7055
7233
|
version = 1;
|
|
7056
7234
|
db = null;
|
|
7057
7235
|
async clear() {
|
|
@@ -7809,11 +7987,6 @@ var ContractRepositoryImpl = class {
|
|
|
7809
7987
|
}
|
|
7810
7988
|
};
|
|
7811
7989
|
|
|
7812
|
-
// src/contracts/types.ts
|
|
7813
|
-
function isDiscoverable(handler) {
|
|
7814
|
-
return !!handler && typeof handler.discoverAt === "function";
|
|
7815
|
-
}
|
|
7816
|
-
|
|
7817
7990
|
// src/contracts/contractWatcher.ts
|
|
7818
7991
|
function computeReconnectDelay(attempt, baseMs, maxMs) {
|
|
7819
7992
|
return Math.min(baseMs * Math.pow(2, attempt - 1), maxMs);
|
|
@@ -9197,6 +9370,8 @@ var HDDescriptorProvider = class _HDDescriptorProvider {
|
|
|
9197
9370
|
this.identity = identity;
|
|
9198
9371
|
this.walletRepository = walletRepository;
|
|
9199
9372
|
}
|
|
9373
|
+
identity;
|
|
9374
|
+
walletRepository;
|
|
9200
9375
|
/**
|
|
9201
9376
|
* Construct an HDDescriptorProvider. No I/O is performed here;
|
|
9202
9377
|
* persisted state is read lazily on the first call to
|
|
@@ -9389,6 +9564,7 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
9389
9564
|
this.currentTaggedScript = priorTaggedScript;
|
|
9390
9565
|
this.logger = logger ?? console;
|
|
9391
9566
|
}
|
|
9567
|
+
provider;
|
|
9392
9568
|
unsubscribe;
|
|
9393
9569
|
chain = Promise.resolve();
|
|
9394
9570
|
/**
|
|
@@ -9460,7 +9636,7 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
9460
9636
|
throw e;
|
|
9461
9637
|
}
|
|
9462
9638
|
if (!boot) return void 0;
|
|
9463
|
-
const offchainTapscript = equalBytes
|
|
9639
|
+
const offchainTapscript = equalBytes(
|
|
9464
9640
|
boot.receivePubkey,
|
|
9465
9641
|
setup.offchainTapscript.options.pubKey
|
|
9466
9642
|
) ? setup.offchainTapscript : rebuildTapscript(setup.offchainTapscript, boot.receivePubkey);
|
|
@@ -9742,6 +9918,8 @@ var MissingSigningDescriptorError = class extends Error {
|
|
|
9742
9918
|
this.contractScript = contractScript;
|
|
9743
9919
|
this.contractType = contractType;
|
|
9744
9920
|
}
|
|
9921
|
+
contractScript;
|
|
9922
|
+
contractType;
|
|
9745
9923
|
name = "MissingSigningDescriptorError";
|
|
9746
9924
|
};
|
|
9747
9925
|
var DescriptorSigningProviderMissingError = class extends Error {
|
|
@@ -9757,6 +9935,7 @@ var InputSignerRouter = class {
|
|
|
9757
9935
|
constructor(deps) {
|
|
9758
9936
|
this.deps = deps;
|
|
9759
9937
|
}
|
|
9938
|
+
deps;
|
|
9760
9939
|
/**
|
|
9761
9940
|
* Resolve each job to its target signer without invoking signing. The
|
|
9762
9941
|
* returned plan is the single source of truth for both {@link sign} and
|
|
@@ -9975,6 +10154,14 @@ var ReadonlyWallet = class _ReadonlyWallet {
|
|
|
9975
10154
|
this._assetManager = new ReadonlyAssetManager(this.indexerProvider);
|
|
9976
10155
|
this.walletContractTimelocks = walletContractTimelocks && walletContractTimelocks.length > 0 ? dedupeTimelocks(walletContractTimelocks) : [this.offchainTapscript.options.csvTimelock];
|
|
9977
10156
|
}
|
|
10157
|
+
identity;
|
|
10158
|
+
network;
|
|
10159
|
+
onchainProvider;
|
|
10160
|
+
indexerProvider;
|
|
10161
|
+
dustAmount;
|
|
10162
|
+
walletRepository;
|
|
10163
|
+
contractRepository;
|
|
10164
|
+
delegateProvider;
|
|
9978
10165
|
_contractManager;
|
|
9979
10166
|
_contractManagerInitializing;
|
|
9980
10167
|
watcherConfig;
|
|
@@ -11081,6 +11268,9 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11081
11268
|
boardingPkScript: boardingTapscript.pkScript
|
|
11082
11269
|
});
|
|
11083
11270
|
}
|
|
11271
|
+
arkProvider;
|
|
11272
|
+
forfeitOutputScript;
|
|
11273
|
+
forfeitPubkey;
|
|
11084
11274
|
static MIN_FEE_RATE = 1;
|
|
11085
11275
|
// sats/vbyte
|
|
11086
11276
|
identity;
|
|
@@ -11293,7 +11483,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11293
11483
|
} catch (e) {
|
|
11294
11484
|
throw new Error("Invalid checkpointTapscript from server");
|
|
11295
11485
|
}
|
|
11296
|
-
if (equalBytes
|
|
11486
|
+
if (equalBytes(xonly, this.arkServerPublicKey)) return;
|
|
11297
11487
|
if (this._receiveRotator) {
|
|
11298
11488
|
await this._receiveRotator.runExclusive(
|
|
11299
11489
|
() => this._doRotateServerSigner(xonly, newServerUnrollScript)
|
|
@@ -11308,7 +11498,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11308
11498
|
return run;
|
|
11309
11499
|
}
|
|
11310
11500
|
async _doRotateServerSigner(xonly, newServerUnrollScript) {
|
|
11311
|
-
if (equalBytes
|
|
11501
|
+
if (equalBytes(xonly, this.arkServerPublicKey)) return;
|
|
11312
11502
|
const manager = await this.getContractManager();
|
|
11313
11503
|
const [currentOffchainRow] = await manager.getContracts({
|
|
11314
11504
|
script: this.defaultContractScript
|
|
@@ -11845,7 +12035,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11845
12035
|
script: offchainOutputScript
|
|
11846
12036
|
});
|
|
11847
12037
|
output.amount -= BigInt(outputFee.satoshis);
|
|
11848
|
-
if (output.amount
|
|
12038
|
+
if (isSubdust(output.amount, this.dustAmount)) {
|
|
11849
12039
|
throw new Error("Output amount is below dust limit");
|
|
11850
12040
|
}
|
|
11851
12041
|
params = {
|
|
@@ -12093,7 +12283,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12093
12283
|
}
|
|
12094
12284
|
continue;
|
|
12095
12285
|
}
|
|
12096
|
-
if (isRecoverable(input) || isSubdust(input, this.dustAmount)) {
|
|
12286
|
+
if (isRecoverable(input) || isSubdust({ value: input.value }, this.dustAmount)) {
|
|
12097
12287
|
continue;
|
|
12098
12288
|
}
|
|
12099
12289
|
if (connectorsLeaves.length === 0) {
|
|
@@ -12655,9 +12845,24 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12655
12845
|
}
|
|
12656
12846
|
const assetInputs = selectedCoinsToAssetInputs(selectedCoins);
|
|
12657
12847
|
const hasAssets = assetInputs.size > 0 || recipients.some((r) => r.assets && r.assets.length > 0);
|
|
12848
|
+
const customExtPackets = [];
|
|
12849
|
+
for (const r of args) {
|
|
12850
|
+
if (r.extensions) {
|
|
12851
|
+
for (const ext of r.extensions) {
|
|
12852
|
+
customExtPackets.push({
|
|
12853
|
+
type: () => ext.type,
|
|
12854
|
+
serialize: () => ext.payload
|
|
12855
|
+
});
|
|
12856
|
+
}
|
|
12857
|
+
}
|
|
12858
|
+
}
|
|
12859
|
+
const allExtPackets = [];
|
|
12658
12860
|
if (hasAssets) {
|
|
12659
|
-
|
|
12660
|
-
|
|
12861
|
+
allExtPackets.push(createAssetPacket(assetInputs, recipients, changeReceiver));
|
|
12862
|
+
}
|
|
12863
|
+
allExtPackets.push(...customExtPackets);
|
|
12864
|
+
if (allExtPackets.length > 0) {
|
|
12865
|
+
outputs.push(Extension.create(allExtPackets).txOut());
|
|
12661
12866
|
}
|
|
12662
12867
|
const sentAmount = recipients.reduce((sum, r) => sum + r.amount, 0);
|
|
12663
12868
|
return this._submitOffchainSpend(selectedCoins, outputs, {
|
|
@@ -12796,72 +13001,52 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12796
13001
|
index,
|
|
12797
13002
|
lookupScript: VtxoScript.decode(input.tapTree).pkScript
|
|
12798
13003
|
}));
|
|
12799
|
-
const checkpointJobs = offchainTx.checkpoints.map(
|
|
12800
|
-
(c) => this.inputSigningJobsFromWitnessUtxos(c)
|
|
12801
|
-
);
|
|
12802
|
-
let signedVirtualTx;
|
|
12803
|
-
let userSignedCheckpoints;
|
|
12804
13004
|
const identity = this.identity;
|
|
12805
|
-
const
|
|
12806
|
-
|
|
12807
|
-
|
|
12808
|
-
|
|
12809
|
-
tx: offchainTx.arkTx.clone(),
|
|
12810
|
-
inputIndexes: arkTxJobs.map((j) => j.index)
|
|
12811
|
-
},
|
|
12812
|
-
...offchainTx.checkpoints.map((c, i) => ({
|
|
12813
|
-
tx: c.clone(),
|
|
12814
|
-
inputIndexes: checkpointJobs[i].map((j) => j.index)
|
|
12815
|
-
}))
|
|
12816
|
-
];
|
|
12817
|
-
const signed = await identity.signMultiple(requests);
|
|
12818
|
-
if (signed.length !== requests.length) {
|
|
12819
|
-
throw new Error(
|
|
12820
|
-
`signMultiple returned ${signed.length} transactions, expected ${requests.length}`
|
|
12821
|
-
);
|
|
12822
|
-
}
|
|
12823
|
-
const [firstSignedTx, ...signedCheckpoints] = signed;
|
|
12824
|
-
signedVirtualTx = firstSignedTx;
|
|
12825
|
-
userSignedCheckpoints = signedCheckpoints;
|
|
12826
|
-
} else {
|
|
12827
|
-
signedVirtualTx = await this._signerRouter.sign(offchainTx.arkTx, arkTxJobs);
|
|
12828
|
-
}
|
|
12829
|
-
await this.setPendingTxFlag(true);
|
|
12830
|
-
const { arkTxid, signedCheckpointTxs } = await this.arkProvider.submitTx(
|
|
12831
|
-
base64.encode(signedVirtualTx.toPSBT()),
|
|
12832
|
-
offchainTx.checkpoints.map((c) => base64.encode(c.toPSBT()))
|
|
12833
|
-
);
|
|
12834
|
-
let finalCheckpoints;
|
|
12835
|
-
if (userSignedCheckpoints) {
|
|
12836
|
-
if (signedCheckpointTxs.length !== userSignedCheckpoints.length) {
|
|
12837
|
-
throw new Error(
|
|
12838
|
-
`submitTx returned ${signedCheckpointTxs.length} checkpoints, expected ${userSignedCheckpoints.length}`
|
|
13005
|
+
const signer = {
|
|
13006
|
+
signArkTx: async (arkTx, checkpoints) => {
|
|
13007
|
+
const checkpointJobs = checkpoints.map(
|
|
13008
|
+
(c) => this.inputSigningJobsFromWitnessUtxos(c)
|
|
12839
13009
|
);
|
|
13010
|
+
const batchEligible = isBatchSignable(identity) && await this._signerRouter.canBatch(arkTxJobs, ...checkpointJobs);
|
|
13011
|
+
if (batchEligible) {
|
|
13012
|
+
const requests = [
|
|
13013
|
+
{
|
|
13014
|
+
tx: arkTx.clone(),
|
|
13015
|
+
inputIndexes: arkTxJobs.map((j) => j.index)
|
|
13016
|
+
},
|
|
13017
|
+
...checkpoints.map((c, i) => ({
|
|
13018
|
+
tx: c.clone(),
|
|
13019
|
+
inputIndexes: checkpointJobs[i].map((j) => j.index)
|
|
13020
|
+
}))
|
|
13021
|
+
];
|
|
13022
|
+
const signed = await identity.signMultiple(requests);
|
|
13023
|
+
if (signed.length !== requests.length) {
|
|
13024
|
+
throw new Error(
|
|
13025
|
+
`signMultiple returned ${signed.length} transactions, expected ${requests.length}`
|
|
13026
|
+
);
|
|
13027
|
+
}
|
|
13028
|
+
const [firstSignedTx, ...signedCheckpoints] = signed;
|
|
13029
|
+
return { arkTx: firstSignedTx, userSignedCheckpoints: signedCheckpoints };
|
|
13030
|
+
}
|
|
13031
|
+
return { arkTx: await this._signerRouter.sign(arkTx, arkTxJobs) };
|
|
13032
|
+
},
|
|
13033
|
+
signCheckpoint: (checkpoint) => this._signerRouter.sign(
|
|
13034
|
+
checkpoint,
|
|
13035
|
+
this.inputSigningJobsFromWitnessUtxos(checkpoint)
|
|
13036
|
+
)
|
|
13037
|
+
};
|
|
13038
|
+
return submitOffchainTx(this.arkProvider, offchainTx, signer, {
|
|
13039
|
+
// Mark pending before submitting — if we crash between submit and
|
|
13040
|
+
// finalize, the next init recovers via finalizePendingTxs.
|
|
13041
|
+
beforeSubmit: () => this.setPendingTxFlag(true),
|
|
13042
|
+
afterFinalize: async () => {
|
|
13043
|
+
try {
|
|
13044
|
+
await this.setPendingTxFlag(false);
|
|
13045
|
+
} catch (error) {
|
|
13046
|
+
console.error("Failed to clear pending tx flag:", error);
|
|
13047
|
+
}
|
|
12840
13048
|
}
|
|
12841
|
-
|
|
12842
|
-
const serverSigned = Transaction$2.fromPSBT(base64.decode(c));
|
|
12843
|
-
combineTapscriptSigs(userSignedCheckpoints[i], serverSigned);
|
|
12844
|
-
return base64.encode(serverSigned.toPSBT());
|
|
12845
|
-
});
|
|
12846
|
-
} else {
|
|
12847
|
-
finalCheckpoints = await Promise.all(
|
|
12848
|
-
signedCheckpointTxs.map(async (c) => {
|
|
12849
|
-
const tx = Transaction$2.fromPSBT(base64.decode(c));
|
|
12850
|
-
const signedCheckpoint = await this._signerRouter.sign(
|
|
12851
|
-
tx,
|
|
12852
|
-
this.inputSigningJobsFromWitnessUtxos(tx)
|
|
12853
|
-
);
|
|
12854
|
-
return base64.encode(signedCheckpoint.toPSBT());
|
|
12855
|
-
})
|
|
12856
|
-
);
|
|
12857
|
-
}
|
|
12858
|
-
await this.arkProvider.finalizeTx(arkTxid, finalCheckpoints);
|
|
12859
|
-
try {
|
|
12860
|
-
await this.setPendingTxFlag(false);
|
|
12861
|
-
} catch (error) {
|
|
12862
|
-
console.error("Failed to clear pending tx flag:", error);
|
|
12863
|
-
}
|
|
12864
|
-
return { arkTxid, signedCheckpointTxs };
|
|
13049
|
+
});
|
|
12865
13050
|
}
|
|
12866
13051
|
// mark virtual outputs as spent, save change outputs if any.
|
|
12867
13052
|
// `offchainTapscript` and `serverPubKey` are the epoch snapshot the
|
|
@@ -13172,6 +13357,8 @@ var MessageBus = class {
|
|
|
13172
13357
|
this.debug = debug;
|
|
13173
13358
|
this.buildServicesFn = buildServices ?? this.buildServices.bind(this);
|
|
13174
13359
|
}
|
|
13360
|
+
walletRepository;
|
|
13361
|
+
contractRepository;
|
|
13175
13362
|
handlers;
|
|
13176
13363
|
tickIntervalMs;
|
|
13177
13364
|
messageTimeoutMs;
|
|
@@ -13673,6 +13860,7 @@ var Ramps = class {
|
|
|
13673
13860
|
constructor(wallet) {
|
|
13674
13861
|
this.wallet = wallet;
|
|
13675
13862
|
}
|
|
13863
|
+
wallet;
|
|
13676
13864
|
/**
|
|
13677
13865
|
* Onboard boarding inputs.
|
|
13678
13866
|
*
|
|
@@ -15019,6 +15207,8 @@ var ServiceWorkerReadonlyAssetManager = class {
|
|
|
15019
15207
|
this.sendMessage = sendMessage;
|
|
15020
15208
|
this.messageTag = messageTag;
|
|
15021
15209
|
}
|
|
15210
|
+
sendMessage;
|
|
15211
|
+
messageTag;
|
|
15022
15212
|
async getAssetDetails(assetId) {
|
|
15023
15213
|
const message = {
|
|
15024
15214
|
tag: this.messageTag,
|
|
@@ -15103,6 +15293,8 @@ var ServiceWorkerReadonlyWallet = class _ServiceWorkerReadonlyWallet {
|
|
|
15103
15293
|
messageTag
|
|
15104
15294
|
);
|
|
15105
15295
|
}
|
|
15296
|
+
serviceWorker;
|
|
15297
|
+
messageTag;
|
|
15106
15298
|
walletRepository;
|
|
15107
15299
|
contractRepository;
|
|
15108
15300
|
identity;
|
|
@@ -15855,6 +16047,7 @@ var ServiceWorkerWallet = class _ServiceWorkerWallet extends ServiceWorkerReadon
|
|
|
15855
16047
|
);
|
|
15856
16048
|
this.hasDelegate = hasDelegate;
|
|
15857
16049
|
}
|
|
16050
|
+
serviceWorker;
|
|
15858
16051
|
walletRepository;
|
|
15859
16052
|
contractRepository;
|
|
15860
16053
|
identity;
|
|
@@ -16236,6 +16429,7 @@ var OnchainWallet = class _OnchainWallet {
|
|
|
16236
16429
|
this.onchainP2TR = onchainP2TR;
|
|
16237
16430
|
this.provider = provider;
|
|
16238
16431
|
}
|
|
16432
|
+
identity;
|
|
16239
16433
|
static MIN_FEE_RATE = 1;
|
|
16240
16434
|
// sat/vbyte
|
|
16241
16435
|
onchainP2TR;
|
|
@@ -16537,6 +16731,8 @@ var WsElectrumChainSource = class {
|
|
|
16537
16731
|
this.ws = ws;
|
|
16538
16732
|
this.network = network;
|
|
16539
16733
|
}
|
|
16734
|
+
ws;
|
|
16735
|
+
network;
|
|
16540
16736
|
// Cached chain tip kept fresh by the headers subscription. Initialized
|
|
16541
16737
|
// lazily on first call to subscribeHeaders().
|
|
16542
16738
|
cachedTip = null;
|
|
@@ -16819,6 +17015,8 @@ var ElectrumOnchainProvider = class {
|
|
|
16819
17015
|
this.network = network;
|
|
16820
17016
|
this.chain = new WsElectrumChainSource(ws, network);
|
|
16821
17017
|
}
|
|
17018
|
+
ws;
|
|
17019
|
+
network;
|
|
16822
17020
|
chain;
|
|
16823
17021
|
async getCoins(address) {
|
|
16824
17022
|
const script = this.encodeAddress(address);
|
|
@@ -17254,7 +17452,7 @@ function verifyP2WPKH(message, witnessItems, pkScript, addressHash) {
|
|
|
17254
17452
|
return false;
|
|
17255
17453
|
}
|
|
17256
17454
|
const derived = p2wpkh(pubkey);
|
|
17257
|
-
if (!equalBytes(derived.hash, addressHash)) {
|
|
17455
|
+
if (!equalBytes$1(derived.hash, addressHash)) {
|
|
17258
17456
|
return false;
|
|
17259
17457
|
}
|
|
17260
17458
|
const sighashType = sigWithHash[sigWithHash.length - 1];
|
|
@@ -17284,7 +17482,7 @@ function verifyLegacy(message, sigBytes, addressHash) {
|
|
|
17284
17482
|
const sig = secp256k1.Signature.fromBytes(compactSig, "compact").addRecoveryBit(recoveryId);
|
|
17285
17483
|
const point = sig.recoverPublicKey(msgHash);
|
|
17286
17484
|
const pubkeyBytes = point.toBytes(compressed);
|
|
17287
|
-
return equalBytes(hash160(pubkeyBytes), addressHash);
|
|
17485
|
+
return equalBytes$1(hash160(pubkeyBytes), addressHash);
|
|
17288
17486
|
} catch {
|
|
17289
17487
|
return false;
|
|
17290
17488
|
}
|
|
@@ -17347,6 +17545,132 @@ function craftBIP322ToSignSimple(toSpend, pkScript) {
|
|
|
17347
17545
|
});
|
|
17348
17546
|
return tx;
|
|
17349
17547
|
}
|
|
17548
|
+
|
|
17549
|
+
// src/providers/emulator.ts
|
|
17550
|
+
var RestEmulatorProvider = class {
|
|
17551
|
+
constructor(serverUrl) {
|
|
17552
|
+
this.serverUrl = serverUrl;
|
|
17553
|
+
}
|
|
17554
|
+
serverUrl;
|
|
17555
|
+
async getInfo() {
|
|
17556
|
+
const url = `${this.serverUrl}/v1/info`;
|
|
17557
|
+
const response = await fetch(url);
|
|
17558
|
+
if (!response.ok) {
|
|
17559
|
+
const errorText2 = await response.text();
|
|
17560
|
+
throw new Error(`Failed to get emulator info: ${errorText2}`);
|
|
17561
|
+
}
|
|
17562
|
+
const data = await response.json();
|
|
17563
|
+
const signerPubkey = data.signerPubkey;
|
|
17564
|
+
if (typeof signerPubkey !== "string" || !signerPubkey) {
|
|
17565
|
+
throw new Error("Invalid emulator info response: missing signerPubkey");
|
|
17566
|
+
}
|
|
17567
|
+
return { signerPubkey };
|
|
17568
|
+
}
|
|
17569
|
+
async submitTx(arkTx, checkpointTxs) {
|
|
17570
|
+
const url = `${this.serverUrl}/v1/tx`;
|
|
17571
|
+
const response = await fetch(url, {
|
|
17572
|
+
method: "POST",
|
|
17573
|
+
headers: { "Content-Type": "application/json" },
|
|
17574
|
+
body: JSON.stringify({
|
|
17575
|
+
arkTx,
|
|
17576
|
+
checkpointTxs
|
|
17577
|
+
})
|
|
17578
|
+
});
|
|
17579
|
+
if (!response.ok) {
|
|
17580
|
+
const errorText2 = await response.text();
|
|
17581
|
+
throw new Error(`Failed to submit tx to emulator: ${errorText2}`);
|
|
17582
|
+
}
|
|
17583
|
+
const data = await response.json();
|
|
17584
|
+
if (typeof data.signedArkTx !== "string" || !data.signedArkTx) {
|
|
17585
|
+
throw new Error("Invalid emulator submitTx response: missing signedArkTx");
|
|
17586
|
+
}
|
|
17587
|
+
if (!Array.isArray(data.signedCheckpointTxs) || !data.signedCheckpointTxs.every((item) => typeof item === "string")) {
|
|
17588
|
+
throw new Error(
|
|
17589
|
+
"Invalid emulator submitTx response: signedCheckpointTxs must be an array of strings"
|
|
17590
|
+
);
|
|
17591
|
+
}
|
|
17592
|
+
return {
|
|
17593
|
+
signedArkTx: data.signedArkTx,
|
|
17594
|
+
signedCheckpointTxs: data.signedCheckpointTxs
|
|
17595
|
+
};
|
|
17596
|
+
}
|
|
17597
|
+
async submitIntent(intent) {
|
|
17598
|
+
const url = `${this.serverUrl}/v1/intent`;
|
|
17599
|
+
const response = await fetch(url, {
|
|
17600
|
+
method: "POST",
|
|
17601
|
+
headers: { "Content-Type": "application/json" },
|
|
17602
|
+
body: JSON.stringify({
|
|
17603
|
+
intent: {
|
|
17604
|
+
proof: intent.proof,
|
|
17605
|
+
message: JSON.stringify(intent.message)
|
|
17606
|
+
}
|
|
17607
|
+
})
|
|
17608
|
+
});
|
|
17609
|
+
if (!response.ok) {
|
|
17610
|
+
const errorText2 = await response.text();
|
|
17611
|
+
throw new Error(`Failed to submit intent to emulator: ${errorText2}`);
|
|
17612
|
+
}
|
|
17613
|
+
const data = await response.json();
|
|
17614
|
+
if (typeof data.signedProof !== "string" || !data.signedProof) {
|
|
17615
|
+
throw new Error("Invalid emulator submitIntent response: missing signedProof");
|
|
17616
|
+
}
|
|
17617
|
+
return data.signedProof;
|
|
17618
|
+
}
|
|
17619
|
+
async submitFinalization(intent, forfeits, connectorTree, commitmentTx) {
|
|
17620
|
+
const url = `${this.serverUrl}/v1/finalization`;
|
|
17621
|
+
const response = await fetch(url, {
|
|
17622
|
+
method: "POST",
|
|
17623
|
+
headers: { "Content-Type": "application/json" },
|
|
17624
|
+
// Uses "signedIntent" (not "intent") because the proof was already
|
|
17625
|
+
// co-signed by the emulator via submitIntent in a prior step.
|
|
17626
|
+
body: JSON.stringify({
|
|
17627
|
+
signedIntent: {
|
|
17628
|
+
proof: intent.proof,
|
|
17629
|
+
message: JSON.stringify(intent.message)
|
|
17630
|
+
},
|
|
17631
|
+
forfeits,
|
|
17632
|
+
connectorTree,
|
|
17633
|
+
commitmentTx
|
|
17634
|
+
})
|
|
17635
|
+
});
|
|
17636
|
+
if (!response.ok) {
|
|
17637
|
+
const errorText2 = await response.text();
|
|
17638
|
+
throw new Error(`Failed to submit finalization to emulator: ${errorText2}`);
|
|
17639
|
+
}
|
|
17640
|
+
const data = await response.json();
|
|
17641
|
+
if (!Array.isArray(data.signedForfeits) || !data.signedForfeits.every((item) => typeof item === "string")) {
|
|
17642
|
+
throw new Error(
|
|
17643
|
+
"Invalid emulator submitFinalization response: signedForfeits must be an array of strings"
|
|
17644
|
+
);
|
|
17645
|
+
}
|
|
17646
|
+
if ("signedCommitmentTx" in data && typeof data.signedCommitmentTx !== "string") {
|
|
17647
|
+
throw new Error(
|
|
17648
|
+
"Invalid emulator submitFinalization response: invalid signedCommitmentTx"
|
|
17649
|
+
);
|
|
17650
|
+
}
|
|
17651
|
+
return {
|
|
17652
|
+
signedForfeits: data.signedForfeits,
|
|
17653
|
+
signedCommitmentTx: data.signedCommitmentTx
|
|
17654
|
+
};
|
|
17655
|
+
}
|
|
17656
|
+
async submitOnchainTx(tx) {
|
|
17657
|
+
const url = `${this.serverUrl}/v1/onchain-tx`;
|
|
17658
|
+
const response = await fetch(url, {
|
|
17659
|
+
method: "POST",
|
|
17660
|
+
headers: { "Content-Type": "application/json" },
|
|
17661
|
+
body: JSON.stringify({ tx })
|
|
17662
|
+
});
|
|
17663
|
+
if (!response.ok) {
|
|
17664
|
+
const errorText2 = await response.text();
|
|
17665
|
+
throw new Error(`Failed to submit onchain tx to emulator: ${errorText2}`);
|
|
17666
|
+
}
|
|
17667
|
+
const data = await response.json();
|
|
17668
|
+
if (typeof data.signedTx !== "string" || !data.signedTx) {
|
|
17669
|
+
throw new Error("Invalid emulator submitOnchainTx response: missing signedTx");
|
|
17670
|
+
}
|
|
17671
|
+
return { signedTx: data.signedTx };
|
|
17672
|
+
}
|
|
17673
|
+
};
|
|
17350
17674
|
function finalizeVirtualTx(type, psbtBase64) {
|
|
17351
17675
|
const tx = Transaction.fromPSBT(base64.decode(psbtBase64));
|
|
17352
17676
|
if (type === "INDEXER_CHAINED_TX_TYPE_TREE" /* TREE */) {
|
|
@@ -17372,6 +17696,7 @@ var ExitPathError = class extends Error {
|
|
|
17372
17696
|
this.reason = reason;
|
|
17373
17697
|
this.name = "ExitPathError";
|
|
17374
17698
|
}
|
|
17699
|
+
reason;
|
|
17375
17700
|
};
|
|
17376
17701
|
function delayWeight(sequence) {
|
|
17377
17702
|
const t = sequenceToTimelock(sequence);
|
|
@@ -17455,6 +17780,11 @@ var Unroll;
|
|
|
17455
17780
|
this.indexer = indexer;
|
|
17456
17781
|
this.virtualTxRepository = virtualTxRepository;
|
|
17457
17782
|
}
|
|
17783
|
+
toUnroll;
|
|
17784
|
+
bumper;
|
|
17785
|
+
explorer;
|
|
17786
|
+
indexer;
|
|
17787
|
+
virtualTxRepository;
|
|
17458
17788
|
/** Create an unroll session by loading the virtual output chain from the indexer. */
|
|
17459
17789
|
static async create(toUnroll, bumper, explorer, indexer, virtualTxRepository) {
|
|
17460
17790
|
const { chain } = await indexer.getVtxoChain(toUnroll);
|
|
@@ -17935,6 +18265,8 @@ var Executor = class {
|
|
|
17935
18265
|
this.pollIntervalMs = opts?.pollIntervalMs ?? 5e3;
|
|
17936
18266
|
this.feeWallet = opts?.feeWallet;
|
|
17937
18267
|
}
|
|
18268
|
+
pkg;
|
|
18269
|
+
provider;
|
|
17938
18270
|
pollIntervalMs;
|
|
17939
18271
|
feeWallet;
|
|
17940
18272
|
sleep() {
|
|
@@ -18371,6 +18703,656 @@ var UnilateralExit = {
|
|
|
18371
18703
|
prepare,
|
|
18372
18704
|
Executor
|
|
18373
18705
|
};
|
|
18706
|
+
|
|
18707
|
+
// src/arkade/index.ts
|
|
18708
|
+
var arkade_exports = {};
|
|
18709
|
+
__export(arkade_exports, {
|
|
18710
|
+
ARKADE_OP: () => ARKADE_OP,
|
|
18711
|
+
ARKADE_OPCODES: () => ARKADE_OPCODES,
|
|
18712
|
+
ARKADE_OPCODE_NAMES: () => ARKADE_OPCODE_NAMES,
|
|
18713
|
+
ARKADE_OPCODE_VALUES: () => ARKADE_OPCODE_VALUES,
|
|
18714
|
+
ARKADE_OPS: () => ARKADE_OPS,
|
|
18715
|
+
Arkade: () => Arkade,
|
|
18716
|
+
ArkadeContract: () => ArkadeContract,
|
|
18717
|
+
ArkadeProgramScript: () => ArkadeProgramScript,
|
|
18718
|
+
ArkadeScript: () => ArkadeScript,
|
|
18719
|
+
ArkadeTransactionBuilder: () => ArkadeTransactionBuilder,
|
|
18720
|
+
BigNum: () => bignum_exports,
|
|
18721
|
+
OP: () => OP,
|
|
18722
|
+
OPCODE_NAMES: () => OPCODE_NAMES,
|
|
18723
|
+
OPCODE_VALUES: () => OPCODE_VALUES,
|
|
18724
|
+
SUPPORTED_PROGRAM_VERSION: () => SUPPORTED_PROGRAM_VERSION,
|
|
18725
|
+
Script: () => Script,
|
|
18726
|
+
arkadeScriptHash: () => arkadeScriptHash,
|
|
18727
|
+
arkadeWitnessHash: () => arkadeWitnessHash,
|
|
18728
|
+
asmToBytes: () => asmToBytes,
|
|
18729
|
+
bytesToASM: () => bytesToASM,
|
|
18730
|
+
computeArkadeScriptPublicKey: () => computeArkadeScriptPublicKey,
|
|
18731
|
+
createArkadeBatchHandler: () => createArkadeBatchHandler,
|
|
18732
|
+
deserializeArkadeContractParams: () => deserializeArkadeContractParams,
|
|
18733
|
+
fromASM: () => fromASM,
|
|
18734
|
+
getOpcodeName: () => getOpcodeName,
|
|
18735
|
+
getOpcodeValue: () => getOpcodeValue,
|
|
18736
|
+
parseArtifact: () => parseArtifact,
|
|
18737
|
+
resolveAsm: () => resolveAsm,
|
|
18738
|
+
serializeArkadeContractParams: () => serializeArkadeContractParams,
|
|
18739
|
+
stringifyArtifact: () => stringifyArtifact,
|
|
18740
|
+
toASM: () => toASM,
|
|
18741
|
+
validateProgram: () => validateProgram,
|
|
18742
|
+
witnessRefToBytes: () => witnessRefToBytes
|
|
18743
|
+
});
|
|
18744
|
+
var isVtxoCoin = (input) => "virtualStatus" in input;
|
|
18745
|
+
function createArkadeBatchHandler(intentId, inputs, signer, signedProof, message, session, arkProvider, emulator, network, recipients) {
|
|
18746
|
+
let batchId;
|
|
18747
|
+
let sweepTapTreeRoot;
|
|
18748
|
+
return {
|
|
18749
|
+
onBatchStarted: async (event) => {
|
|
18750
|
+
const utf8IntentId = new TextEncoder().encode(intentId);
|
|
18751
|
+
const intentIdHash = sha256(utf8IntentId);
|
|
18752
|
+
const intentIdHashStr = hex.encode(intentIdHash);
|
|
18753
|
+
if (!event.intentIdHashes.includes(intentIdHashStr)) return { skip: true };
|
|
18754
|
+
await arkProvider.confirmRegistration(intentId);
|
|
18755
|
+
batchId = event.id;
|
|
18756
|
+
const sweepTapscript = CSVMultisigTapscript.encode({
|
|
18757
|
+
timelock: {
|
|
18758
|
+
value: event.batchExpiry,
|
|
18759
|
+
// BIP-65: values >= 512 are interpreted as seconds, below as blocks
|
|
18760
|
+
type: event.batchExpiry >= 512n ? "seconds" : "blocks"
|
|
18761
|
+
},
|
|
18762
|
+
pubkeys: [hex.decode((await arkProvider.getInfo()).forfeitPubkey).subarray(1)]
|
|
18763
|
+
}).script;
|
|
18764
|
+
sweepTapTreeRoot = tapLeafHash(sweepTapscript);
|
|
18765
|
+
return { skip: false };
|
|
18766
|
+
},
|
|
18767
|
+
onTreeSigningStarted: async (event, vtxoTree) => {
|
|
18768
|
+
const signerPubKey = await session.getPublicKey();
|
|
18769
|
+
const xonlySignerPubKey = signerPubKey.subarray(1);
|
|
18770
|
+
const xOnlyPubkeys = event.cosignersPublicKeys.map((k) => k.slice(2));
|
|
18771
|
+
if (!xOnlyPubkeys.includes(hex.encode(xonlySignerPubKey))) {
|
|
18772
|
+
return { skip: true };
|
|
18773
|
+
}
|
|
18774
|
+
const commitmentTx = Transaction.fromPSBT(base64.decode(event.unsignedCommitmentTx));
|
|
18775
|
+
validateVtxoTxGraph(vtxoTree, commitmentTx, sweepTapTreeRoot);
|
|
18776
|
+
if (recipients && recipients.length > 0) {
|
|
18777
|
+
validateBatchRecipients(commitmentTx, vtxoTree.leaves(), recipients, network);
|
|
18778
|
+
}
|
|
18779
|
+
const sharedOutput = commitmentTx.getOutput(0);
|
|
18780
|
+
if (!sharedOutput?.amount) {
|
|
18781
|
+
throw new Error("Shared output not found");
|
|
18782
|
+
}
|
|
18783
|
+
await session.init(vtxoTree, sweepTapTreeRoot, sharedOutput.amount);
|
|
18784
|
+
const pubkey = hex.encode(await session.getPublicKey());
|
|
18785
|
+
const nonces = await session.getNonces();
|
|
18786
|
+
await arkProvider.submitTreeNonces(batchId, pubkey, nonces);
|
|
18787
|
+
return { skip: false };
|
|
18788
|
+
},
|
|
18789
|
+
onTreeNonces: async (event) => {
|
|
18790
|
+
const { hasAllNonces } = await session.aggregatedNonces(event.txid, event.nonces);
|
|
18791
|
+
if (!hasAllNonces) return { fullySigned: false };
|
|
18792
|
+
const signatures = await session.sign();
|
|
18793
|
+
const pubkey = hex.encode(await session.getPublicKey());
|
|
18794
|
+
await arkProvider.submitTreeSignatures(batchId, pubkey, signatures);
|
|
18795
|
+
return { fullySigned: true };
|
|
18796
|
+
},
|
|
18797
|
+
onBatchFinalization: async (event, _vtxoTree, connectorTree) => {
|
|
18798
|
+
const info = await arkProvider.getInfo();
|
|
18799
|
+
const forfeitOutputScript = OutScript.encode(
|
|
18800
|
+
Address(network).decode(info.forfeitAddress)
|
|
18801
|
+
);
|
|
18802
|
+
if (connectorTree) {
|
|
18803
|
+
validateConnectorsTxGraph(event.commitmentTx, connectorTree);
|
|
18804
|
+
}
|
|
18805
|
+
let commitmentPsbt = Transaction.fromPSBT(base64.decode(event.commitmentTx));
|
|
18806
|
+
const signedForfeits = [];
|
|
18807
|
+
let connectorIndex = 0;
|
|
18808
|
+
const connectorLeaves = connectorTree?.leaves() || [];
|
|
18809
|
+
const boardingIndices = [];
|
|
18810
|
+
for (const input of inputs) {
|
|
18811
|
+
if (!isVtxoCoin(input)) {
|
|
18812
|
+
let boardingIdx = null;
|
|
18813
|
+
for (let i = 0; i < commitmentPsbt.inputsLength; i++) {
|
|
18814
|
+
const psbtInput = commitmentPsbt.getInput(i);
|
|
18815
|
+
if (!psbtInput.txid) continue;
|
|
18816
|
+
if (hex.encode(psbtInput.txid) === input.txid && psbtInput.index === input.vout) {
|
|
18817
|
+
boardingIdx = i;
|
|
18818
|
+
break;
|
|
18819
|
+
}
|
|
18820
|
+
}
|
|
18821
|
+
if (boardingIdx === null) continue;
|
|
18822
|
+
commitmentPsbt.updateInput(boardingIdx, {
|
|
18823
|
+
tapLeafScript: [input.forfeitTapLeafScript]
|
|
18824
|
+
});
|
|
18825
|
+
boardingIndices.push(boardingIdx);
|
|
18826
|
+
continue;
|
|
18827
|
+
}
|
|
18828
|
+
if (isRecoverable(input) || isSubdust(input, info.dust)) {
|
|
18829
|
+
continue;
|
|
18830
|
+
}
|
|
18831
|
+
if (connectorIndex >= connectorLeaves.length) {
|
|
18832
|
+
throw new Error("not enough connectors received");
|
|
18833
|
+
}
|
|
18834
|
+
const connectorLeaf = connectorLeaves[connectorIndex++];
|
|
18835
|
+
const connectorTxId = connectorLeaf.id;
|
|
18836
|
+
const connectorOutput = connectorLeaf.getOutput(0);
|
|
18837
|
+
if (!connectorOutput?.amount || !connectorOutput?.script) {
|
|
18838
|
+
throw new Error(
|
|
18839
|
+
`Invalid connector output at index ${connectorIndex - 1}: missing amount or script`
|
|
18840
|
+
);
|
|
18841
|
+
}
|
|
18842
|
+
let forfeitTx = buildForfeitTx(
|
|
18843
|
+
[
|
|
18844
|
+
{
|
|
18845
|
+
txid: input.txid,
|
|
18846
|
+
index: input.vout,
|
|
18847
|
+
witnessUtxo: {
|
|
18848
|
+
amount: BigInt(input.value),
|
|
18849
|
+
script: VtxoScript.decode(input.tapTree).pkScript
|
|
18850
|
+
},
|
|
18851
|
+
sighashType: SigHash.DEFAULT,
|
|
18852
|
+
tapLeafScript: [input.forfeitTapLeafScript]
|
|
18853
|
+
},
|
|
18854
|
+
{
|
|
18855
|
+
txid: connectorTxId,
|
|
18856
|
+
index: 0,
|
|
18857
|
+
witnessUtxo: {
|
|
18858
|
+
amount: connectorOutput.amount,
|
|
18859
|
+
script: connectorOutput.script
|
|
18860
|
+
}
|
|
18861
|
+
}
|
|
18862
|
+
],
|
|
18863
|
+
forfeitOutputScript
|
|
18864
|
+
);
|
|
18865
|
+
forfeitTx = await signer.sign(forfeitTx, [0]);
|
|
18866
|
+
signedForfeits.push(base64.encode(forfeitTx.toPSBT()));
|
|
18867
|
+
}
|
|
18868
|
+
if (boardingIndices.length > 0) {
|
|
18869
|
+
commitmentPsbt = await signer.sign(commitmentPsbt, boardingIndices);
|
|
18870
|
+
}
|
|
18871
|
+
let connectorTreeNodes = null;
|
|
18872
|
+
if (connectorTree) {
|
|
18873
|
+
connectorTreeNodes = [];
|
|
18874
|
+
for (const subtree of connectorTree.iterator()) {
|
|
18875
|
+
const children = {};
|
|
18876
|
+
for (const [outputIndex, child] of subtree.children) {
|
|
18877
|
+
children[String(outputIndex)] = child.txid;
|
|
18878
|
+
}
|
|
18879
|
+
connectorTreeNodes.push({
|
|
18880
|
+
txid: subtree.txid,
|
|
18881
|
+
tx: base64.encode(subtree.root.toPSBT()),
|
|
18882
|
+
children
|
|
18883
|
+
});
|
|
18884
|
+
}
|
|
18885
|
+
}
|
|
18886
|
+
const hasBoardingInputs = boardingIndices.length > 0;
|
|
18887
|
+
const commitmentB64 = hasBoardingInputs ? base64.encode(commitmentPsbt.toPSBT()) : event.commitmentTx;
|
|
18888
|
+
const emuResult = await emulator.submitFinalization(
|
|
18889
|
+
{ proof: signedProof, message },
|
|
18890
|
+
signedForfeits,
|
|
18891
|
+
connectorTreeNodes,
|
|
18892
|
+
commitmentB64
|
|
18893
|
+
);
|
|
18894
|
+
await arkProvider.submitSignedForfeitTxs(
|
|
18895
|
+
emuResult.signedForfeits,
|
|
18896
|
+
emuResult.signedCommitmentTx || (hasBoardingInputs ? commitmentB64 : void 0)
|
|
18897
|
+
);
|
|
18898
|
+
}
|
|
18899
|
+
};
|
|
18900
|
+
}
|
|
18901
|
+
var Arkade = class _Arkade {
|
|
18902
|
+
arkProvider;
|
|
18903
|
+
/** The co-signing service, or undefined for emulator-less (pure tapscript) usage. */
|
|
18904
|
+
emulator;
|
|
18905
|
+
network;
|
|
18906
|
+
serverKey;
|
|
18907
|
+
/** The co-signer's x-only key, present only when an emulator is configured. */
|
|
18908
|
+
emulatorKey;
|
|
18909
|
+
checkpoint;
|
|
18910
|
+
indexer;
|
|
18911
|
+
identity;
|
|
18912
|
+
/** The signing identity's x-only public key, resolved at connect — identifies which inputs the wallet signs. */
|
|
18913
|
+
userKey;
|
|
18914
|
+
/** The wallet's contract manager, when contract persistence is wired up. */
|
|
18915
|
+
contractManager;
|
|
18916
|
+
constructor(fields) {
|
|
18917
|
+
this.arkProvider = fields.arkProvider;
|
|
18918
|
+
this.emulator = fields.emulator;
|
|
18919
|
+
this.network = fields.network;
|
|
18920
|
+
this.serverKey = fields.serverKey;
|
|
18921
|
+
this.emulatorKey = fields.emulatorKey;
|
|
18922
|
+
this.checkpoint = fields.checkpoint;
|
|
18923
|
+
this.indexer = fields.indexer;
|
|
18924
|
+
this.identity = fields.identity;
|
|
18925
|
+
this.userKey = fields.userKey;
|
|
18926
|
+
this.contractManager = fields.contractManager;
|
|
18927
|
+
}
|
|
18928
|
+
/** Connect and resolve the server key, checkpoint closure and (if present) the co-signer key. */
|
|
18929
|
+
static async connect(opts) {
|
|
18930
|
+
const info = await opts.arkade.getInfo();
|
|
18931
|
+
const serverKey = hex.decode(info.signerPubkey).slice(1);
|
|
18932
|
+
const checkpoint = CSVMultisigTapscript.decode(hex.decode(info.checkpointTapscript));
|
|
18933
|
+
let emulatorKey;
|
|
18934
|
+
if (opts.emulator) {
|
|
18935
|
+
const eInfo = await opts.emulator.getInfo();
|
|
18936
|
+
emulatorKey = hex.decode(eInfo.signerPubkey);
|
|
18937
|
+
}
|
|
18938
|
+
let userKey;
|
|
18939
|
+
if (opts.identity) {
|
|
18940
|
+
const pub = await opts.identity.xOnlyPublicKey();
|
|
18941
|
+
userKey = pub.length === 33 ? pub.slice(1) : pub;
|
|
18942
|
+
}
|
|
18943
|
+
return new _Arkade({
|
|
18944
|
+
arkProvider: opts.arkade,
|
|
18945
|
+
emulator: opts.emulator,
|
|
18946
|
+
network: opts.network ?? DEFAULT_NETWORK,
|
|
18947
|
+
serverKey,
|
|
18948
|
+
emulatorKey,
|
|
18949
|
+
checkpoint,
|
|
18950
|
+
indexer: opts.indexer,
|
|
18951
|
+
identity: opts.identity,
|
|
18952
|
+
userKey,
|
|
18953
|
+
contractManager: opts.contractManager
|
|
18954
|
+
});
|
|
18955
|
+
}
|
|
18956
|
+
/**
|
|
18957
|
+
* Instantiate a contract from a program and its constructor arguments. The
|
|
18958
|
+
* `program`'s literal type is preserved (`const` inference) so the resulting
|
|
18959
|
+
* contract's `functions` map is strongly typed — `functions.<name>(...)`
|
|
18960
|
+
* knows each argument's type from the function's `inputs` descriptors.
|
|
18961
|
+
*
|
|
18962
|
+
* When the program declares a `server` or `user` param and the caller does
|
|
18963
|
+
* not bind it, it defaults to the client's server key or the identity's
|
|
18964
|
+
* key respectively; explicit args always win.
|
|
18965
|
+
*/
|
|
18966
|
+
contract(program, args = {}) {
|
|
18967
|
+
const declared = (program.params ?? []).map(inputName);
|
|
18968
|
+
if (declared.includes("server") && args.server === void 0) {
|
|
18969
|
+
args = { ...args, server: this.serverKey };
|
|
18970
|
+
}
|
|
18971
|
+
if (declared.includes("user") && args.user === void 0 && this.userKey) {
|
|
18972
|
+
args = { ...args, user: this.userKey };
|
|
18973
|
+
}
|
|
18974
|
+
return new ArkadeContract(this, program, args);
|
|
18975
|
+
}
|
|
18976
|
+
};
|
|
18977
|
+
var ArkadeContract = class _ArkadeContract {
|
|
18978
|
+
constructor(client, program, args = {}, keys) {
|
|
18979
|
+
this.client = client;
|
|
18980
|
+
this.program = program;
|
|
18981
|
+
this.args = args;
|
|
18982
|
+
this.keys = keys ?? {
|
|
18983
|
+
serverKey: client.serverKey,
|
|
18984
|
+
userKey: client.userKey,
|
|
18985
|
+
emulatorKey: client.emulatorKey
|
|
18986
|
+
};
|
|
18987
|
+
this.vtxoScript = new ArkadeProgramScript(program, args, this.keys);
|
|
18988
|
+
this.tapTree = this.vtxoScript.encode();
|
|
18989
|
+
this.compiled = this.vtxoScript.compiled;
|
|
18990
|
+
}
|
|
18991
|
+
client;
|
|
18992
|
+
program;
|
|
18993
|
+
args;
|
|
18994
|
+
/** The compiled taproot tree of spending-path leaves. */
|
|
18995
|
+
vtxoScript;
|
|
18996
|
+
/** Encoded taproot tree (shared spend context for every path). */
|
|
18997
|
+
tapTree;
|
|
18998
|
+
/** The signer keys the program was compiled against. */
|
|
18999
|
+
keys;
|
|
19000
|
+
/** Spending paths in declaration order. */
|
|
19001
|
+
compiled;
|
|
19002
|
+
/**
|
|
19003
|
+
* Rebuild a callable contract from a persisted `"arkade"` contract row
|
|
19004
|
+
* (see {@link ArkadeContract.register}). The stored keys are used for
|
|
19005
|
+
* compilation — not the client's current ones — so the derived script and
|
|
19006
|
+
* address stay identical to the registered contract even after a server
|
|
19007
|
+
* signer rotation.
|
|
19008
|
+
*/
|
|
19009
|
+
static fromContract(client, contract) {
|
|
19010
|
+
if (contract.type !== "arkade") {
|
|
19011
|
+
throw new Error(
|
|
19012
|
+
`ArkadeContract.fromContract: expected contract type 'arkade', got '${contract.type}'`
|
|
19013
|
+
);
|
|
19014
|
+
}
|
|
19015
|
+
const typed = deserializeArkadeContractParams(contract.params);
|
|
19016
|
+
return new _ArkadeContract(client, typed.program, typed.args, {
|
|
19017
|
+
serverKey: typed.serverKey,
|
|
19018
|
+
userKey: typed.userKey,
|
|
19019
|
+
emulatorKey: typed.emulatorKey
|
|
19020
|
+
});
|
|
19021
|
+
}
|
|
19022
|
+
/** Resolve the {@link TapLeafScript} for a spending path by its index. */
|
|
19023
|
+
leafScript(index) {
|
|
19024
|
+
const fn = this.compiled[index];
|
|
19025
|
+
if (!fn) throw new Error(`leaf index ${index} out of range`);
|
|
19026
|
+
return fn.tapLeafScript;
|
|
19027
|
+
}
|
|
19028
|
+
/** Arkade funding address. */
|
|
19029
|
+
get address() {
|
|
19030
|
+
return this.vtxoScript.address(this.client.network.hrp, this.keys.serverKey).encode();
|
|
19031
|
+
}
|
|
19032
|
+
/** Taproot output script. */
|
|
19033
|
+
get pkScript() {
|
|
19034
|
+
return this.vtxoScript.pkScript;
|
|
19035
|
+
}
|
|
19036
|
+
/**
|
|
19037
|
+
* Callable spending paths: `contract.functions.<name>(...args)`. Strongly
|
|
19038
|
+
* typed from the program's literal type — each function's argument types are
|
|
19039
|
+
* derived from its `inputs` descriptors (see {@link ContractFunctions}).
|
|
19040
|
+
*/
|
|
19041
|
+
get functions() {
|
|
19042
|
+
const out = {};
|
|
19043
|
+
for (const fn of this.compiled) {
|
|
19044
|
+
out[fn.name] = (...callArgs) => new ArkadeTransactionBuilder(this, fn, bindInputs(fn, callArgs));
|
|
19045
|
+
}
|
|
19046
|
+
return out;
|
|
19047
|
+
}
|
|
19048
|
+
/**
|
|
19049
|
+
* The `createContract` payload for this contract — the serialized program,
|
|
19050
|
+
* args and keys plus the derived script/address. Useful when registering
|
|
19051
|
+
* through a manager the client does not hold.
|
|
19052
|
+
*/
|
|
19053
|
+
toContractParams() {
|
|
19054
|
+
return {
|
|
19055
|
+
type: "arkade",
|
|
19056
|
+
params: serializeArkadeContractParams({
|
|
19057
|
+
program: this.program,
|
|
19058
|
+
args: this.args,
|
|
19059
|
+
serverKey: this.keys.serverKey,
|
|
19060
|
+
userKey: this.keys.userKey,
|
|
19061
|
+
emulatorKey: this.keys.emulatorKey
|
|
19062
|
+
}),
|
|
19063
|
+
script: hex.encode(this.pkScript),
|
|
19064
|
+
address: this.address
|
|
19065
|
+
};
|
|
19066
|
+
}
|
|
19067
|
+
/**
|
|
19068
|
+
* Persist this contract through the wallet's {@link IContractManager} so it
|
|
19069
|
+
* is tracked like any other contract type: stored in the contract
|
|
19070
|
+
* repository, watched for VTXO events, counted in repository-backed
|
|
19071
|
+
* balances, and re-derivable offline via the `"arkade"` contract handler.
|
|
19072
|
+
* Idempotent — re-registering the same script is a no-op.
|
|
19073
|
+
*/
|
|
19074
|
+
async register(options) {
|
|
19075
|
+
const manager = this.client.contractManager;
|
|
19076
|
+
if (!manager) {
|
|
19077
|
+
throw new Error(
|
|
19078
|
+
"ArkadeContract.register requires a `contractManager` on the Arkade client \u2014 pass one to Arkade.connect"
|
|
19079
|
+
);
|
|
19080
|
+
}
|
|
19081
|
+
return manager.createContract({
|
|
19082
|
+
...this.toContractParams(),
|
|
19083
|
+
label: options?.label,
|
|
19084
|
+
metadata: options?.metadata
|
|
19085
|
+
});
|
|
19086
|
+
}
|
|
19087
|
+
/**
|
|
19088
|
+
* Spendable VTXOs locked by this contract.
|
|
19089
|
+
*
|
|
19090
|
+
* When a `contractManager` is configured and this contract is registered,
|
|
19091
|
+
* reads the repository-backed state (offline-first, kept fresh by the
|
|
19092
|
+
* contract watcher). Otherwise falls back to a direct indexer query.
|
|
19093
|
+
*/
|
|
19094
|
+
async getUtxos() {
|
|
19095
|
+
const manager = this.client.contractManager;
|
|
19096
|
+
const scriptHex = hex.encode(this.pkScript);
|
|
19097
|
+
if (manager) {
|
|
19098
|
+
const [registered] = await manager.getContracts({ script: scriptHex });
|
|
19099
|
+
if (registered) {
|
|
19100
|
+
const [withVtxos] = await manager.getContractsWithVtxos({ script: scriptHex });
|
|
19101
|
+
return (withVtxos?.vtxos ?? []).filter(isSpendable);
|
|
19102
|
+
}
|
|
19103
|
+
}
|
|
19104
|
+
if (!this.client.indexer) {
|
|
19105
|
+
throw new Error("ArkadeContract.getUtxos: an indexer is required");
|
|
19106
|
+
}
|
|
19107
|
+
const { vtxos } = await this.client.indexer.getVtxos({
|
|
19108
|
+
scripts: [scriptHex],
|
|
19109
|
+
spendableOnly: true
|
|
19110
|
+
});
|
|
19111
|
+
return vtxos;
|
|
19112
|
+
}
|
|
19113
|
+
/** Total spendable balance (requires an indexer). */
|
|
19114
|
+
async getBalance() {
|
|
19115
|
+
const utxos = await this.getUtxos();
|
|
19116
|
+
return utxos.reduce((sum, u) => sum + BigInt(u.value), 0n);
|
|
19117
|
+
}
|
|
19118
|
+
};
|
|
19119
|
+
var ArkadeTransactionBuilder = class {
|
|
19120
|
+
/** @internal */
|
|
19121
|
+
constructor(contract, fn, args) {
|
|
19122
|
+
this.contract = contract;
|
|
19123
|
+
this.fn = fn;
|
|
19124
|
+
this.args = args;
|
|
19125
|
+
}
|
|
19126
|
+
contract;
|
|
19127
|
+
fn;
|
|
19128
|
+
args;
|
|
19129
|
+
outputs = [];
|
|
19130
|
+
fundingCoins = [];
|
|
19131
|
+
assetSpecs = [];
|
|
19132
|
+
coin;
|
|
19133
|
+
changeScript;
|
|
19134
|
+
from(coin) {
|
|
19135
|
+
this.coin = coin;
|
|
19136
|
+
return this;
|
|
19137
|
+
}
|
|
19138
|
+
/**
|
|
19139
|
+
* Add extra inputs the caller funds (e.g. a taker's own coins in a swap).
|
|
19140
|
+
* These become inputs 1..n and are signed with the client identity.
|
|
19141
|
+
*/
|
|
19142
|
+
fund(coins) {
|
|
19143
|
+
this.fundingCoins.push(...coins);
|
|
19144
|
+
return this;
|
|
19145
|
+
}
|
|
19146
|
+
/** Transfer an asset group: which inputs supply it and which outputs receive it. */
|
|
19147
|
+
withAsset(spec) {
|
|
19148
|
+
this.assetSpecs.push(spec);
|
|
19149
|
+
return this;
|
|
19150
|
+
}
|
|
19151
|
+
/** Destination for any surplus (inputs − outputs). Required when the spend is not exact. */
|
|
19152
|
+
change(script) {
|
|
19153
|
+
this.changeScript = script;
|
|
19154
|
+
return this;
|
|
19155
|
+
}
|
|
19156
|
+
to(scriptOrOutputs, amount) {
|
|
19157
|
+
if (Array.isArray(scriptOrOutputs)) {
|
|
19158
|
+
for (const [i, out] of scriptOrOutputs.entries()) {
|
|
19159
|
+
if (out.amount === void 0) {
|
|
19160
|
+
throw new Error(`to(outputs): output ${i} is missing an amount`);
|
|
19161
|
+
}
|
|
19162
|
+
}
|
|
19163
|
+
this.outputs.push(...scriptOrOutputs);
|
|
19164
|
+
} else {
|
|
19165
|
+
if (amount === void 0) throw new Error("to(script, amount): amount is required");
|
|
19166
|
+
this.outputs.push({ script: scriptOrOutputs, amount });
|
|
19167
|
+
}
|
|
19168
|
+
return this;
|
|
19169
|
+
}
|
|
19170
|
+
/** Assemble the unsigned ark transaction and its checkpoints. */
|
|
19171
|
+
async build() {
|
|
19172
|
+
if (this.outputs.length === 0) {
|
|
19173
|
+
throw new Error("ArkadeTransactionBuilder: at least one output is required");
|
|
19174
|
+
}
|
|
19175
|
+
const outputsSum = this.outputs.reduce((s, o) => s + (o.amount ?? 0n), 0n);
|
|
19176
|
+
const coin = this.coin ?? await this.selectCoin(outputsSum);
|
|
19177
|
+
const def = this.fn.def;
|
|
19178
|
+
const outputs = [...this.outputs];
|
|
19179
|
+
const fundingSum = this.fundingCoins.reduce((s, f) => s + BigInt(f.value), 0n);
|
|
19180
|
+
const surplus = BigInt(coin.value) + fundingSum - outputsSum;
|
|
19181
|
+
if (surplus < 0n) {
|
|
19182
|
+
throw new Error(
|
|
19183
|
+
`ArkadeTransactionBuilder: insufficient inputs \u2014 outputs ${outputsSum} exceed inputs ${BigInt(coin.value) + fundingSum}`
|
|
19184
|
+
);
|
|
19185
|
+
}
|
|
19186
|
+
if (surplus > 0n) {
|
|
19187
|
+
if (!this.changeScript) {
|
|
19188
|
+
throw new Error(
|
|
19189
|
+
`ArkadeTransactionBuilder: ${surplus} sats surplus with no change output \u2014 call .change(script)`
|
|
19190
|
+
);
|
|
19191
|
+
}
|
|
19192
|
+
outputs.push({ script: this.changeScript, amount: surplus });
|
|
19193
|
+
}
|
|
19194
|
+
const { arkTx, checkpoints } = buildOffchainTx(
|
|
19195
|
+
[
|
|
19196
|
+
{
|
|
19197
|
+
txid: coin.txid,
|
|
19198
|
+
vout: coin.vout,
|
|
19199
|
+
value: coin.value,
|
|
19200
|
+
tapLeafScript: this.fn.tapLeafScript,
|
|
19201
|
+
tapTree: this.contract.tapTree
|
|
19202
|
+
},
|
|
19203
|
+
...this.fundingCoins
|
|
19204
|
+
],
|
|
19205
|
+
outputs,
|
|
19206
|
+
this.contract.client.checkpoint
|
|
19207
|
+
);
|
|
19208
|
+
if (coin.sourceTx) {
|
|
19209
|
+
setArkPsbtField(arkTx, 0, PrevArkTxField, coin.sourceTx);
|
|
19210
|
+
}
|
|
19211
|
+
const condition = (def.tapscript.witness ?? []).map((w) => this.witnessBytes(w));
|
|
19212
|
+
if (condition.length > 0) {
|
|
19213
|
+
setArkPsbtField(arkTx, 0, ConditionWitness, condition);
|
|
19214
|
+
setArkPsbtField(checkpoints[0], 0, ConditionWitness, condition);
|
|
19215
|
+
}
|
|
19216
|
+
const packets = [];
|
|
19217
|
+
if (this.assetSpecs.length > 0) {
|
|
19218
|
+
packets.push(this.buildAssetPacket());
|
|
19219
|
+
}
|
|
19220
|
+
const arkadeScript = this.fn.arkadeScript;
|
|
19221
|
+
if (arkadeScript) {
|
|
19222
|
+
const stack = (def.arkadeScript?.witness ?? []).map((w) => this.witnessBytes(w));
|
|
19223
|
+
packets.push(
|
|
19224
|
+
EmulatorPacket.create([
|
|
19225
|
+
{ vin: 0, script: arkadeScript, witness: RawWitness.encode(stack) }
|
|
19226
|
+
])
|
|
19227
|
+
);
|
|
19228
|
+
}
|
|
19229
|
+
if (packets.length > 0) {
|
|
19230
|
+
attachExtension(arkTx, packets);
|
|
19231
|
+
}
|
|
19232
|
+
return { arkTx, checkpoints };
|
|
19233
|
+
}
|
|
19234
|
+
/** Build, submit and return the finalized transaction. */
|
|
19235
|
+
async send() {
|
|
19236
|
+
const { arkTx, checkpoints } = await this.build();
|
|
19237
|
+
const client = this.contract.client;
|
|
19238
|
+
const userInputs = this.userInputIndexes();
|
|
19239
|
+
if (this.fn.arkadeScript) {
|
|
19240
|
+
if (!client.emulator) {
|
|
19241
|
+
throw new Error("covenant spends require an `emulator` on the Arkade client");
|
|
19242
|
+
}
|
|
19243
|
+
const signedArk2 = await this.signArk(arkTx, userInputs);
|
|
19244
|
+
const signedCps = userInputs.length > 0 ? await Promise.all(
|
|
19245
|
+
checkpoints.map(
|
|
19246
|
+
(c, i) => userInputs.includes(i) ? client.identity.sign(c, [0]) : c
|
|
19247
|
+
)
|
|
19248
|
+
) : checkpoints;
|
|
19249
|
+
const res2 = await client.emulator.submitTx(
|
|
19250
|
+
base64.encode(signedArk2.toPSBT()),
|
|
19251
|
+
signedCps.map((c) => base64.encode(c.toPSBT()))
|
|
19252
|
+
);
|
|
19253
|
+
const txid = Transaction.fromPSBT(base64.decode(res2.signedArkTx)).id;
|
|
19254
|
+
return {
|
|
19255
|
+
txid,
|
|
19256
|
+
signedArkTx: res2.signedArkTx,
|
|
19257
|
+
signedCheckpointTxs: res2.signedCheckpointTxs
|
|
19258
|
+
};
|
|
19259
|
+
}
|
|
19260
|
+
if (!client.identity) {
|
|
19261
|
+
throw new Error("a signing identity is required for non-covenant spends");
|
|
19262
|
+
}
|
|
19263
|
+
const signedArk = await this.signArk(arkTx, userInputs);
|
|
19264
|
+
const res = await client.arkProvider.submitTx(
|
|
19265
|
+
base64.encode(signedArk.toPSBT()),
|
|
19266
|
+
checkpoints.map((c) => base64.encode(c.toPSBT()))
|
|
19267
|
+
);
|
|
19268
|
+
const finalCps = await Promise.all(
|
|
19269
|
+
res.signedCheckpointTxs.map(
|
|
19270
|
+
async (b) => base64.encode(
|
|
19271
|
+
(await client.identity.sign(Transaction.fromPSBT(base64.decode(b)), [0])).toPSBT()
|
|
19272
|
+
)
|
|
19273
|
+
)
|
|
19274
|
+
);
|
|
19275
|
+
await client.arkProvider.finalizeTx(res.arkTxid, finalCps);
|
|
19276
|
+
return {
|
|
19277
|
+
txid: res.arkTxid,
|
|
19278
|
+
signedArkTx: res.finalArkTx,
|
|
19279
|
+
signedCheckpointTxs: res.signedCheckpointTxs
|
|
19280
|
+
};
|
|
19281
|
+
}
|
|
19282
|
+
/** Sign the client-owned inputs of the ark tx (no-op when there are none). */
|
|
19283
|
+
async signArk(arkTx, userInputs) {
|
|
19284
|
+
if (userInputs.length === 0) return arkTx;
|
|
19285
|
+
const { identity } = this.contract.client;
|
|
19286
|
+
if (!identity) {
|
|
19287
|
+
throw new Error("this spend requires an `identity` to sign its user/funding inputs");
|
|
19288
|
+
}
|
|
19289
|
+
return identity.sign(arkTx, userInputs);
|
|
19290
|
+
}
|
|
19291
|
+
/** Indexes of inputs the client owns and must sign (contract input + funded inputs). */
|
|
19292
|
+
userInputIndexes() {
|
|
19293
|
+
const idxs = [];
|
|
19294
|
+
const userKey = this.contract.keys.userKey;
|
|
19295
|
+
if (userKey && this.fn.signerKeys.some((k) => equalBytes(k, userKey))) {
|
|
19296
|
+
idxs.push(0);
|
|
19297
|
+
}
|
|
19298
|
+
for (let i = 0; i < this.fundingCoins.length; i++) {
|
|
19299
|
+
idxs.push(i + 1);
|
|
19300
|
+
}
|
|
19301
|
+
return idxs;
|
|
19302
|
+
}
|
|
19303
|
+
async selectCoin(amount) {
|
|
19304
|
+
const utxos = await this.contract.getUtxos();
|
|
19305
|
+
if (utxos.length === 0) throw new Error("no spendable coins for this contract");
|
|
19306
|
+
const covering = utxos.filter((u) => BigInt(u.value) >= amount).sort((a, b) => a.value - b.value);
|
|
19307
|
+
if (covering.length > 0) return covering[0];
|
|
19308
|
+
return [...utxos].sort((a, b) => b.value - a.value)[0];
|
|
19309
|
+
}
|
|
19310
|
+
buildAssetPacket() {
|
|
19311
|
+
const groups = this.assetSpecs.map((s) => {
|
|
19312
|
+
const id = typeof s.assetId === "string" ? AssetId.fromString(s.assetId) : AssetId.fromBytes(s.assetId);
|
|
19313
|
+
return AssetGroup.create(
|
|
19314
|
+
id,
|
|
19315
|
+
null,
|
|
19316
|
+
s.inputs.map((i) => AssetInput.create(i.vin, i.amount)),
|
|
19317
|
+
s.outputs.map((o) => AssetOutput.create(o.vout, o.amount)),
|
|
19318
|
+
(s.metadata ?? []).map((m) => Metadata.create(m.key, m.value))
|
|
19319
|
+
);
|
|
19320
|
+
});
|
|
19321
|
+
return Packet.create(groups);
|
|
19322
|
+
}
|
|
19323
|
+
witnessBytes(ref) {
|
|
19324
|
+
return witnessRefToBytes(ref, this.args, this.contract.args);
|
|
19325
|
+
}
|
|
19326
|
+
};
|
|
19327
|
+
function bindInputs(fn, callArgs) {
|
|
19328
|
+
const names = (fn.def.inputs ?? []).map(inputName);
|
|
19329
|
+
if (callArgs.length !== names.length) {
|
|
19330
|
+
throw new Error(`${fn.name}: expected ${names.length} argument(s), got ${callArgs.length}`);
|
|
19331
|
+
}
|
|
19332
|
+
const bound = {};
|
|
19333
|
+
names.forEach((n, i) => bound[n] = callArgs[i]);
|
|
19334
|
+
return bound;
|
|
19335
|
+
}
|
|
19336
|
+
function attachExtension(tx, newPackets) {
|
|
19337
|
+
for (let i = 0; i < tx.outputsLength; i++) {
|
|
19338
|
+
const out = tx.getOutput(i);
|
|
19339
|
+
if (!out?.script || !Extension.isExtension(out.script)) continue;
|
|
19340
|
+
const existing = Extension.fromBytes(out.script);
|
|
19341
|
+
const merged = Extension.create([...existing.getPackets(), ...newPackets]);
|
|
19342
|
+
tx.updateOutput(i, { script: merged.serialize(), amount: 0n });
|
|
19343
|
+
return;
|
|
19344
|
+
}
|
|
19345
|
+
const ext = Extension.create(newPackets);
|
|
19346
|
+
const newOut = ext.txOut();
|
|
19347
|
+
const lastIdx = tx.outputsLength - 1;
|
|
19348
|
+
const lastOut = tx.getOutput(lastIdx);
|
|
19349
|
+
if (lastOut?.script && lastOut.script.length === ANCHOR_PKSCRIPT.length && lastOut.script.every((b, j) => b === ANCHOR_PKSCRIPT[j])) {
|
|
19350
|
+
tx.updateOutput(lastIdx, { script: newOut.script, amount: newOut.amount });
|
|
19351
|
+
tx.addOutput({ script: lastOut.script, amount: lastOut.amount ?? 0n });
|
|
19352
|
+
return;
|
|
19353
|
+
}
|
|
19354
|
+
tx.addOutput({ script: newOut.script, amount: newOut.amount });
|
|
19355
|
+
}
|
|
18374
19356
|
var ARKCONTRACT_PREFIX = "arkcontract";
|
|
18375
19357
|
function encodeArkContract(contract) {
|
|
18376
19358
|
const params = new URLSearchParams();
|
|
@@ -18430,6 +19412,6 @@ function isArkContract(str) {
|
|
|
18430
19412
|
return str.startsWith(ARKCONTRACT_PREFIX + "=");
|
|
18431
19413
|
}
|
|
18432
19414
|
|
|
18433
|
-
export { ActivityRegistry, ArkNote, AssetManager, BIP322, Batch, ChainedTxType, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EsploraProvider, Estimator, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnilateralExit, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, assertIntentIdUnique, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createDefaultActivityRegistry, createExitChainResolver, decodeArkContract, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, intentMatchesFilter, intentPageBounds, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable, isExpired, isRecoverable, isRetryableProviderError, isSpendable, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, mergeChainedTxType, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signerSetFromInfo, toXOnlySignerHex, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
|
|
18434
|
-
//# sourceMappingURL=chunk-
|
|
18435
|
-
//# sourceMappingURL=chunk-
|
|
19415
|
+
export { ARKADE_MAGIC, ActivityRegistry, ArkNote, AssetManager, BIP322, Batch, ChainedTxType, ContractManager, ContractRepositoryImpl, ContractWatcher, DB_VERSION, DEFAULT_MESSAGE_TIMEOUTS, DelegateManagerImpl, DelegateNotConfiguredError, DelegatorManagerImpl, DelegatorNotConfiguredError, DescriptorSigningProviderMissingError, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, ESPLORA_URL, ElectrumOnchainProvider, EmulatorPacket, EsploraProvider, Estimator, Extension, ExtensionNotFoundError, HDDescriptorProvider, INTENT_TERMINAL_STATES, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, MissingSigningDescriptorError, MnemonicIdentity, OnchainWallet, P2A, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, RestDelegateProvider, RestDelegatorProvider, RestEmulatorProvider, SeedIdentity, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, SingleKey, TxTree, TxType, TxWeightEstimator, UnilateralExit, UnknownPacket, Unroll, VtxoManager, Wallet2 as Wallet, WalletMessageHandler, WalletNotInitializedError, WalletRepositoryImpl, WsElectrumChainSource, arkade_exports, assertIntentIdUnique, boardingResolver, buildForfeitTx, buildOffchainTx, classifyAgainstSignerSet, classifyContractSigner, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, createAssetPacket, createDefaultActivityRegistry, createExitChainResolver, decodeArkContract, deserializeAssets, deserializeExitPackage, deserializeUtxo, deserializeVtxo, encodeArkContract, extendVirtualCoinForContract, getMigrationStatus, getRandomId, hasBoardingTxExpired, intentMatchesFilter, intentPageBounds, isArkContract, isBatchSignable, isCooperativelyMigratable, isDiscoverable, isExpired, isRecoverable, isRetryableProviderError, isSpendable, isSubdust, isTerminalIntentState, isValidArkAddress, isVtxoExpiringSoon, isVtxoForScript, mergeChainedTxType, migrateWalletRepository, openDatabase, requiresMigration, rollbackMigration, saveVtxosForContract, scriptFromArkAddress, selectCoinsWithAsset, selectVirtualCoins, serializeAssets, serializeExitPackage, serializeUtxo, serializeVtxo, setupServiceWorker, signerSetFromInfo, toXOnlySignerHex, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, waitForIncomingFunds, warnAndFilterVtxosForScript };
|
|
19416
|
+
//# sourceMappingURL=chunk-OCS5S3L3.js.map
|
|
19417
|
+
//# sourceMappingURL=chunk-OCS5S3L3.js.map
|