@arkade-os/sdk 0.4.46 → 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-C9ognsOV.d.cts → ark-CoJgwTi_.d.cts} +26 -2
- package/dist/{ark-C9ognsOV.d.ts → ark-CoJgwTi_.d.ts} +26 -2
- package/dist/{asyncStorageTaskQueue-CmBYVlxw.d.cts → asyncStorageTaskQueue-Dowyo59a.d.cts} +2 -2
- package/dist/{asyncStorageTaskQueue-BAKZ5cS5.d.ts → asyncStorageTaskQueue-Z3aFoXCl.d.ts} +2 -2
- package/dist/{chunk-R2SVAM5Q.js → chunk-3N5I6GT6.js} +3 -3
- package/dist/{chunk-R2SVAM5Q.js.map → chunk-3N5I6GT6.js.map} +1 -1
- package/dist/{chunk-JTCQ6QXQ.js → chunk-AJQ4AOT3.js} +4 -4
- package/dist/{chunk-JTCQ6QXQ.js.map → chunk-AJQ4AOT3.js.map} +1 -1
- package/dist/{chunk-FWG4TSPN.js → chunk-C7TXXTMS.js} +4 -5
- package/dist/chunk-C7TXXTMS.js.map +1 -0
- package/dist/{chunk-CWJ7ZCP7.js → chunk-CDDWXBEM.js} +87 -16
- package/dist/chunk-CDDWXBEM.js.map +1 -0
- package/dist/{chunk-7IYIFMF4.cjs → chunk-HHV5FPC6.cjs} +94 -20
- package/dist/chunk-HHV5FPC6.cjs.map +1 -0
- package/dist/{chunk-SOXGAA52.cjs → chunk-JAYOXOLT.cjs} +14 -14
- package/dist/{chunk-SOXGAA52.cjs.map → chunk-JAYOXOLT.cjs.map} +1 -1
- package/dist/{chunk-MQB5H4X5.cjs → chunk-L5ND2FJJ.cjs} +4 -5
- package/dist/chunk-L5ND2FJJ.cjs.map +1 -0
- package/dist/{chunk-P6VBP3CT.js → chunk-OCS5S3L3.js} +1010 -98
- 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-6TQJOD72.cjs → chunk-VE67GMGM.cjs} +7 -7
- package/dist/{chunk-6TQJOD72.cjs.map → chunk-VE67GMGM.cjs.map} +1 -1
- package/dist/{chunk-D46YDM36.cjs → chunk-WTTLIFS7.cjs} +1207 -285
- 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-aOd5qINr.d.ts → delegate-CuaDqqQi.d.ts} +1 -1
- package/dist/{delegate-DVEgyRwI.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 +213 -161
- 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 +19 -19
- package/dist/repositories/realm/index.d.cts +2 -2
- package/dist/repositories/realm/index.d.ts +2 -2
- package/dist/repositories/realm/index.js +5 -5
- package/dist/repositories/sqlite/index.cjs +18 -18
- package/dist/repositories/sqlite/index.d.cts +2 -2
- package/dist/repositories/sqlite/index.d.ts +2 -2
- package/dist/repositories/sqlite/index.js +5 -5
- package/dist/{taskRunner-B48-V-Pb.d.ts → taskRunner-Dz-TI4Rb.d.ts} +2 -2
- package/dist/{taskRunner-nNecmQ2C.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 +14 -14
- package/dist/wallet/expo/index.d.cts +4 -4
- package/dist/wallet/expo/index.d.ts +4 -4
- package/dist/wallet/expo/index.js +5 -5
- package/dist/{wallet-BUg3D4WJ.d.cts → wallet-Oxz_wLxo.d.cts} +13 -3
- package/dist/{wallet-DM0oShTu.d.ts → wallet-x7egmXq6.d.ts} +13 -3
- package/dist/worker/expo/index.cjs +9 -9
- package/dist/worker/expo/index.d.cts +4 -4
- package/dist/worker/expo/index.d.ts +4 -4
- package/dist/worker/expo/index.js +5 -5
- package/package.json +1 -1
- package/dist/chunk-4KXOCVXP.cjs +0 -973
- package/dist/chunk-4KXOCVXP.cjs.map +0 -1
- package/dist/chunk-7IYIFMF4.cjs.map +0 -1
- package/dist/chunk-CWJ7ZCP7.js.map +0 -1
- package/dist/chunk-D46YDM36.cjs.map +0 -1
- package/dist/chunk-FWG4TSPN.js.map +0 -1
- package/dist/chunk-MQB5H4X5.cjs.map +0 -1
- package/dist/chunk-P6VBP3CT.js.map +0 -1
- package/dist/chunk-QKC2T54O.js +0 -960
- package/dist/chunk-QKC2T54O.js.map +0 -1
- package/dist/index-CPzL4tqg.d.ts +0 -259
- package/dist/index-D8vmtWWO.d.cts +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';
|
|
@@ -1512,6 +1513,62 @@ var UnknownPacket = class {
|
|
|
1512
1513
|
}
|
|
1513
1514
|
};
|
|
1514
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
|
+
|
|
1515
1572
|
// src/extension/index.ts
|
|
1516
1573
|
var ARKADE_MAGIC = new Uint8Array([65, 82, 75]);
|
|
1517
1574
|
var ExtensionNotFoundError = class extends Error {
|
|
@@ -1531,10 +1588,14 @@ var Extension = class _Extension {
|
|
|
1531
1588
|
}
|
|
1532
1589
|
const seen = /* @__PURE__ */ new Set();
|
|
1533
1590
|
for (const p of packets) {
|
|
1534
|
-
|
|
1535
|
-
|
|
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]`);
|
|
1536
1594
|
}
|
|
1537
|
-
seen.
|
|
1595
|
+
if (seen.has(type)) {
|
|
1596
|
+
throw new Error(`duplicate packet type ${type}`);
|
|
1597
|
+
}
|
|
1598
|
+
seen.add(type);
|
|
1538
1599
|
}
|
|
1539
1600
|
return new _Extension(packets);
|
|
1540
1601
|
}
|
|
@@ -1548,7 +1609,7 @@ var Extension = class _Extension {
|
|
|
1548
1609
|
if (decoded.length < 2 || decoded[0] !== "RETURN") return false;
|
|
1549
1610
|
const data = decoded[1];
|
|
1550
1611
|
if (!(data instanceof Uint8Array)) return false;
|
|
1551
|
-
return data.length >= ARKADE_MAGIC.length && equalBytes
|
|
1612
|
+
return data.length >= ARKADE_MAGIC.length && equalBytes(data.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC);
|
|
1552
1613
|
} catch {
|
|
1553
1614
|
return false;
|
|
1554
1615
|
}
|
|
@@ -1579,7 +1640,7 @@ var Extension = class _Extension {
|
|
|
1579
1640
|
payload.set(d, offset);
|
|
1580
1641
|
offset += d.length;
|
|
1581
1642
|
}
|
|
1582
|
-
if (payload.length < ARKADE_MAGIC.length || !equalBytes
|
|
1643
|
+
if (payload.length < ARKADE_MAGIC.length || !equalBytes(payload.slice(0, ARKADE_MAGIC.length), ARKADE_MAGIC)) {
|
|
1583
1644
|
throw new Error(
|
|
1584
1645
|
`expected magic prefix ${hex.encode(ARKADE_MAGIC)}, got ${hex.encode(payload.slice(0, Math.min(payload.length, ARKADE_MAGIC.length)))}`
|
|
1585
1646
|
);
|
|
@@ -1664,12 +1725,45 @@ var Extension = class _Extension {
|
|
|
1664
1725
|
}
|
|
1665
1726
|
return null;
|
|
1666
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
|
+
}
|
|
1667
1757
|
};
|
|
1668
1758
|
function parsePacket(packetType, data) {
|
|
1669
|
-
|
|
1670
|
-
|
|
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);
|
|
1671
1766
|
}
|
|
1672
|
-
return new UnknownPacket(packetType, data);
|
|
1673
1767
|
}
|
|
1674
1768
|
function encodeVarUint(value) {
|
|
1675
1769
|
const bytes = [];
|
|
@@ -1750,7 +1844,7 @@ function validateOnchainRecipient(commitmentTx, recipient, network, usedOutputs)
|
|
|
1750
1844
|
if (!output?.script || output.script.length === 0) {
|
|
1751
1845
|
continue;
|
|
1752
1846
|
}
|
|
1753
|
-
if (equalBytes
|
|
1847
|
+
if (equalBytes(output.script, expectedPkScript)) {
|
|
1754
1848
|
if (output.amount !== BigInt(recipient.amount)) {
|
|
1755
1849
|
continue;
|
|
1756
1850
|
}
|
|
@@ -1774,7 +1868,7 @@ function validateOffchainRecipient(leaves, arkAddress, recipient, usedOutputs) {
|
|
|
1774
1868
|
if (!output?.script || output.script.length === 0) {
|
|
1775
1869
|
continue;
|
|
1776
1870
|
}
|
|
1777
|
-
if (!equalBytes
|
|
1871
|
+
if (!equalBytes(output.script, expectedPkScript)) {
|
|
1778
1872
|
continue;
|
|
1779
1873
|
}
|
|
1780
1874
|
if (output.amount !== expectedAmount) {
|
|
@@ -2303,6 +2397,46 @@ function isValidArkAddress(address) {
|
|
|
2303
2397
|
return false;
|
|
2304
2398
|
}
|
|
2305
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
|
+
}
|
|
2306
2440
|
var AmountVariableName = "amount";
|
|
2307
2441
|
var ExpiryVariableName = "expiry";
|
|
2308
2442
|
var BirthVariableName = "birth";
|
|
@@ -2458,6 +2592,14 @@ function parseProgram(text, env) {
|
|
|
2458
2592
|
}
|
|
2459
2593
|
return { program, text };
|
|
2460
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
|
+
}
|
|
2461
2603
|
var DUST_AMOUNT = 546;
|
|
2462
2604
|
var FALLBACK_WALLET_DUST_AMOUNT = 330n;
|
|
2463
2605
|
function getDustAmount(wallet) {
|
|
@@ -2477,10 +2619,14 @@ function deriveContractTapscripts(contract) {
|
|
|
2477
2619
|
throw new Error(`No handler for contract type '${contract.type}'`);
|
|
2478
2620
|
}
|
|
2479
2621
|
const script = handler.createScript(contract.params);
|
|
2622
|
+
if (isTapscriptDeriving(handler)) {
|
|
2623
|
+
return handler.deriveTapscripts(script, contract);
|
|
2624
|
+
}
|
|
2625
|
+
const legacy = script;
|
|
2480
2626
|
return {
|
|
2481
|
-
forfeitTapLeafScript:
|
|
2482
|
-
intentTapLeafScript:
|
|
2483
|
-
tapTree:
|
|
2627
|
+
forfeitTapLeafScript: legacy.forfeit(),
|
|
2628
|
+
intentTapLeafScript: legacy.forfeit(),
|
|
2629
|
+
tapTree: legacy.encode()
|
|
2484
2630
|
};
|
|
2485
2631
|
}
|
|
2486
2632
|
function cloneTapLeafScript([
|
|
@@ -5989,7 +6135,7 @@ async function makeSignedDelegateIntent(identity, coins, outputs, onchainOutputs
|
|
|
5989
6135
|
};
|
|
5990
6136
|
}
|
|
5991
6137
|
function findDestinationOutputIndex(outputs, destinationScript) {
|
|
5992
|
-
return outputs.findIndex((o) => o.script && equalBytes
|
|
6138
|
+
return outputs.findIndex((o) => o.script && equalBytes(o.script, destinationScript));
|
|
5993
6139
|
}
|
|
5994
6140
|
function getDayTimestamp(timestamp) {
|
|
5995
6141
|
const date = new Date(timestamp);
|
|
@@ -7841,11 +7987,6 @@ var ContractRepositoryImpl = class {
|
|
|
7841
7987
|
}
|
|
7842
7988
|
};
|
|
7843
7989
|
|
|
7844
|
-
// src/contracts/types.ts
|
|
7845
|
-
function isDiscoverable(handler) {
|
|
7846
|
-
return !!handler && typeof handler.discoverAt === "function";
|
|
7847
|
-
}
|
|
7848
|
-
|
|
7849
7990
|
// src/contracts/contractWatcher.ts
|
|
7850
7991
|
function computeReconnectDelay(attempt, baseMs, maxMs) {
|
|
7851
7992
|
return Math.min(baseMs * Math.pow(2, attempt - 1), maxMs);
|
|
@@ -9495,7 +9636,7 @@ var WalletReceiveRotator = class _WalletReceiveRotator {
|
|
|
9495
9636
|
throw e;
|
|
9496
9637
|
}
|
|
9497
9638
|
if (!boot) return void 0;
|
|
9498
|
-
const offchainTapscript = equalBytes
|
|
9639
|
+
const offchainTapscript = equalBytes(
|
|
9499
9640
|
boot.receivePubkey,
|
|
9500
9641
|
setup.offchainTapscript.options.pubKey
|
|
9501
9642
|
) ? setup.offchainTapscript : rebuildTapscript(setup.offchainTapscript, boot.receivePubkey);
|
|
@@ -11342,7 +11483,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11342
11483
|
} catch (e) {
|
|
11343
11484
|
throw new Error("Invalid checkpointTapscript from server");
|
|
11344
11485
|
}
|
|
11345
|
-
if (equalBytes
|
|
11486
|
+
if (equalBytes(xonly, this.arkServerPublicKey)) return;
|
|
11346
11487
|
if (this._receiveRotator) {
|
|
11347
11488
|
await this._receiveRotator.runExclusive(
|
|
11348
11489
|
() => this._doRotateServerSigner(xonly, newServerUnrollScript)
|
|
@@ -11357,7 +11498,7 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
11357
11498
|
return run;
|
|
11358
11499
|
}
|
|
11359
11500
|
async _doRotateServerSigner(xonly, newServerUnrollScript) {
|
|
11360
|
-
if (equalBytes
|
|
11501
|
+
if (equalBytes(xonly, this.arkServerPublicKey)) return;
|
|
11361
11502
|
const manager = await this.getContractManager();
|
|
11362
11503
|
const [currentOffchainRow] = await manager.getContracts({
|
|
11363
11504
|
script: this.defaultContractScript
|
|
@@ -12704,9 +12845,24 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12704
12845
|
}
|
|
12705
12846
|
const assetInputs = selectedCoinsToAssetInputs(selectedCoins);
|
|
12706
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 = [];
|
|
12707
12860
|
if (hasAssets) {
|
|
12708
|
-
|
|
12709
|
-
|
|
12861
|
+
allExtPackets.push(createAssetPacket(assetInputs, recipients, changeReceiver));
|
|
12862
|
+
}
|
|
12863
|
+
allExtPackets.push(...customExtPackets);
|
|
12864
|
+
if (allExtPackets.length > 0) {
|
|
12865
|
+
outputs.push(Extension.create(allExtPackets).txOut());
|
|
12710
12866
|
}
|
|
12711
12867
|
const sentAmount = recipients.reduce((sum, r) => sum + r.amount, 0);
|
|
12712
12868
|
return this._submitOffchainSpend(selectedCoins, outputs, {
|
|
@@ -12845,72 +13001,52 @@ var Wallet2 = class _Wallet extends ReadonlyWallet {
|
|
|
12845
13001
|
index,
|
|
12846
13002
|
lookupScript: VtxoScript.decode(input.tapTree).pkScript
|
|
12847
13003
|
}));
|
|
12848
|
-
const checkpointJobs = offchainTx.checkpoints.map(
|
|
12849
|
-
(c) => this.inputSigningJobsFromWitnessUtxos(c)
|
|
12850
|
-
);
|
|
12851
|
-
let signedVirtualTx;
|
|
12852
|
-
let userSignedCheckpoints;
|
|
12853
13004
|
const identity = this.identity;
|
|
12854
|
-
const
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
|
|
12858
|
-
tx: offchainTx.arkTx.clone(),
|
|
12859
|
-
inputIndexes: arkTxJobs.map((j) => j.index)
|
|
12860
|
-
},
|
|
12861
|
-
...offchainTx.checkpoints.map((c, i) => ({
|
|
12862
|
-
tx: c.clone(),
|
|
12863
|
-
inputIndexes: checkpointJobs[i].map((j) => j.index)
|
|
12864
|
-
}))
|
|
12865
|
-
];
|
|
12866
|
-
const signed = await identity.signMultiple(requests);
|
|
12867
|
-
if (signed.length !== requests.length) {
|
|
12868
|
-
throw new Error(
|
|
12869
|
-
`signMultiple returned ${signed.length} transactions, expected ${requests.length}`
|
|
12870
|
-
);
|
|
12871
|
-
}
|
|
12872
|
-
const [firstSignedTx, ...signedCheckpoints] = signed;
|
|
12873
|
-
signedVirtualTx = firstSignedTx;
|
|
12874
|
-
userSignedCheckpoints = signedCheckpoints;
|
|
12875
|
-
} else {
|
|
12876
|
-
signedVirtualTx = await this._signerRouter.sign(offchainTx.arkTx, arkTxJobs);
|
|
12877
|
-
}
|
|
12878
|
-
await this.setPendingTxFlag(true);
|
|
12879
|
-
const { arkTxid, signedCheckpointTxs } = await this.arkProvider.submitTx(
|
|
12880
|
-
base64.encode(signedVirtualTx.toPSBT()),
|
|
12881
|
-
offchainTx.checkpoints.map((c) => base64.encode(c.toPSBT()))
|
|
12882
|
-
);
|
|
12883
|
-
let finalCheckpoints;
|
|
12884
|
-
if (userSignedCheckpoints) {
|
|
12885
|
-
if (signedCheckpointTxs.length !== userSignedCheckpoints.length) {
|
|
12886
|
-
throw new Error(
|
|
12887
|
-
`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)
|
|
12888
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
|
+
}
|
|
12889
13048
|
}
|
|
12890
|
-
|
|
12891
|
-
const serverSigned = Transaction$2.fromPSBT(base64.decode(c));
|
|
12892
|
-
combineTapscriptSigs(userSignedCheckpoints[i], serverSigned);
|
|
12893
|
-
return base64.encode(serverSigned.toPSBT());
|
|
12894
|
-
});
|
|
12895
|
-
} else {
|
|
12896
|
-
finalCheckpoints = await Promise.all(
|
|
12897
|
-
signedCheckpointTxs.map(async (c) => {
|
|
12898
|
-
const tx = Transaction$2.fromPSBT(base64.decode(c));
|
|
12899
|
-
const signedCheckpoint = await this._signerRouter.sign(
|
|
12900
|
-
tx,
|
|
12901
|
-
this.inputSigningJobsFromWitnessUtxos(tx)
|
|
12902
|
-
);
|
|
12903
|
-
return base64.encode(signedCheckpoint.toPSBT());
|
|
12904
|
-
})
|
|
12905
|
-
);
|
|
12906
|
-
}
|
|
12907
|
-
await this.arkProvider.finalizeTx(arkTxid, finalCheckpoints);
|
|
12908
|
-
try {
|
|
12909
|
-
await this.setPendingTxFlag(false);
|
|
12910
|
-
} catch (error) {
|
|
12911
|
-
console.error("Failed to clear pending tx flag:", error);
|
|
12912
|
-
}
|
|
12913
|
-
return { arkTxid, signedCheckpointTxs };
|
|
13049
|
+
});
|
|
12914
13050
|
}
|
|
12915
13051
|
// mark virtual outputs as spent, save change outputs if any.
|
|
12916
13052
|
// `offchainTapscript` and `serverPubKey` are the epoch snapshot the
|
|
@@ -17316,7 +17452,7 @@ function verifyP2WPKH(message, witnessItems, pkScript, addressHash) {
|
|
|
17316
17452
|
return false;
|
|
17317
17453
|
}
|
|
17318
17454
|
const derived = p2wpkh(pubkey);
|
|
17319
|
-
if (!equalBytes(derived.hash, addressHash)) {
|
|
17455
|
+
if (!equalBytes$1(derived.hash, addressHash)) {
|
|
17320
17456
|
return false;
|
|
17321
17457
|
}
|
|
17322
17458
|
const sighashType = sigWithHash[sigWithHash.length - 1];
|
|
@@ -17346,7 +17482,7 @@ function verifyLegacy(message, sigBytes, addressHash) {
|
|
|
17346
17482
|
const sig = secp256k1.Signature.fromBytes(compactSig, "compact").addRecoveryBit(recoveryId);
|
|
17347
17483
|
const point = sig.recoverPublicKey(msgHash);
|
|
17348
17484
|
const pubkeyBytes = point.toBytes(compressed);
|
|
17349
|
-
return equalBytes(hash160(pubkeyBytes), addressHash);
|
|
17485
|
+
return equalBytes$1(hash160(pubkeyBytes), addressHash);
|
|
17350
17486
|
} catch {
|
|
17351
17487
|
return false;
|
|
17352
17488
|
}
|
|
@@ -17409,6 +17545,132 @@ function craftBIP322ToSignSimple(toSpend, pkScript) {
|
|
|
17409
17545
|
});
|
|
17410
17546
|
return tx;
|
|
17411
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
|
+
};
|
|
17412
17674
|
function finalizeVirtualTx(type, psbtBase64) {
|
|
17413
17675
|
const tx = Transaction.fromPSBT(base64.decode(psbtBase64));
|
|
17414
17676
|
if (type === "INDEXER_CHAINED_TX_TYPE_TREE" /* TREE */) {
|
|
@@ -18441,6 +18703,656 @@ var UnilateralExit = {
|
|
|
18441
18703
|
prepare,
|
|
18442
18704
|
Executor
|
|
18443
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
|
+
}
|
|
18444
19356
|
var ARKCONTRACT_PREFIX = "arkcontract";
|
|
18445
19357
|
function encodeArkContract(contract) {
|
|
18446
19358
|
const params = new URLSearchParams();
|
|
@@ -18500,6 +19412,6 @@ function isArkContract(str) {
|
|
|
18500
19412
|
return str.startsWith(ARKCONTRACT_PREFIX + "=");
|
|
18501
19413
|
}
|
|
18502
19414
|
|
|
18503
|
-
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 };
|
|
18504
|
-
//# sourceMappingURL=chunk-
|
|
18505
|
-
//# 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
|