@arkade-os/sdk 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -174
- package/dist/cjs/arknote/index.js +61 -58
- package/dist/cjs/bip322/errors.js +13 -0
- package/dist/cjs/bip322/index.js +178 -0
- package/dist/cjs/forfeit.js +14 -25
- package/dist/cjs/identity/singleKey.js +68 -0
- package/dist/cjs/index.js +41 -17
- package/dist/cjs/providers/ark.js +253 -317
- package/dist/cjs/providers/indexer.js +525 -0
- package/dist/cjs/providers/onchain.js +193 -15
- package/dist/cjs/script/address.js +48 -17
- package/dist/cjs/script/base.js +120 -3
- package/dist/cjs/script/default.js +18 -4
- package/dist/cjs/script/tapscript.js +46 -14
- package/dist/cjs/script/vhtlc.js +27 -7
- package/dist/cjs/tree/signingSession.js +63 -106
- package/dist/cjs/tree/txTree.js +193 -0
- package/dist/cjs/tree/validation.js +79 -155
- package/dist/cjs/utils/anchor.js +35 -0
- package/dist/cjs/utils/arkTransaction.js +108 -0
- package/dist/cjs/utils/transactionHistory.js +84 -72
- package/dist/cjs/utils/txSizeEstimator.js +12 -0
- package/dist/cjs/utils/unknownFields.js +211 -0
- package/dist/cjs/wallet/index.js +12 -0
- package/dist/cjs/wallet/onchain.js +201 -0
- package/dist/cjs/wallet/ramps.js +95 -0
- package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +32 -0
- package/dist/cjs/wallet/serviceWorker/request.js +15 -12
- package/dist/cjs/wallet/serviceWorker/response.js +22 -27
- package/dist/cjs/wallet/serviceWorker/utils.js +8 -0
- package/dist/cjs/wallet/serviceWorker/wallet.js +58 -34
- package/dist/cjs/wallet/serviceWorker/worker.js +117 -108
- package/dist/cjs/wallet/unroll.js +270 -0
- package/dist/cjs/wallet/wallet.js +701 -454
- package/dist/esm/arknote/index.js +61 -57
- package/dist/esm/bip322/errors.js +9 -0
- package/dist/esm/bip322/index.js +174 -0
- package/dist/esm/forfeit.js +15 -26
- package/dist/esm/identity/singleKey.js +64 -0
- package/dist/esm/index.js +30 -12
- package/dist/esm/providers/ark.js +252 -317
- package/dist/esm/providers/indexer.js +521 -0
- package/dist/esm/providers/onchain.js +193 -15
- package/dist/esm/script/address.js +48 -17
- package/dist/esm/script/base.js +120 -3
- package/dist/esm/script/default.js +18 -4
- package/dist/esm/script/tapscript.js +46 -14
- package/dist/esm/script/vhtlc.js +27 -7
- package/dist/esm/tree/signingSession.js +65 -108
- package/dist/esm/tree/txTree.js +189 -0
- package/dist/esm/tree/validation.js +75 -152
- package/dist/esm/utils/anchor.js +31 -0
- package/dist/esm/utils/arkTransaction.js +105 -0
- package/dist/esm/utils/transactionHistory.js +84 -72
- package/dist/esm/utils/txSizeEstimator.js +12 -0
- package/dist/esm/utils/unknownFields.js +173 -0
- package/dist/esm/wallet/index.js +9 -0
- package/dist/esm/wallet/onchain.js +196 -0
- package/dist/esm/wallet/ramps.js +91 -0
- package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +32 -0
- package/dist/esm/wallet/serviceWorker/request.js +15 -12
- package/dist/esm/wallet/serviceWorker/response.js +22 -27
- package/dist/esm/wallet/serviceWorker/utils.js +8 -0
- package/dist/esm/wallet/serviceWorker/wallet.js +59 -35
- package/dist/esm/wallet/serviceWorker/worker.js +117 -108
- package/dist/esm/wallet/unroll.js +267 -0
- package/dist/esm/wallet/wallet.js +674 -461
- package/dist/types/arknote/index.d.ts +40 -13
- package/dist/types/bip322/errors.d.ts +6 -0
- package/dist/types/bip322/index.d.ts +57 -0
- package/dist/types/forfeit.d.ts +2 -14
- package/dist/types/identity/singleKey.d.ts +27 -0
- package/dist/types/index.d.ts +23 -12
- package/dist/types/providers/ark.d.ts +114 -95
- package/dist/types/providers/indexer.d.ts +186 -0
- package/dist/types/providers/onchain.d.ts +41 -11
- package/dist/types/script/address.d.ts +26 -2
- package/dist/types/script/base.d.ts +13 -3
- package/dist/types/script/default.d.ts +22 -0
- package/dist/types/script/tapscript.d.ts +61 -5
- package/dist/types/script/vhtlc.d.ts +27 -0
- package/dist/types/tree/signingSession.d.ts +5 -5
- package/dist/types/tree/txTree.d.ts +28 -0
- package/dist/types/tree/validation.d.ts +15 -22
- package/dist/types/utils/anchor.d.ts +19 -0
- package/dist/types/utils/arkTransaction.d.ts +27 -0
- package/dist/types/utils/transactionHistory.d.ts +7 -1
- package/dist/types/utils/txSizeEstimator.d.ts +3 -0
- package/dist/types/utils/unknownFields.d.ts +83 -0
- package/dist/types/wallet/index.d.ts +51 -50
- package/dist/types/wallet/onchain.d.ts +49 -0
- package/dist/types/wallet/ramps.d.ts +32 -0
- package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +2 -0
- package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +2 -0
- package/dist/types/wallet/serviceWorker/request.d.ts +14 -16
- package/dist/types/wallet/serviceWorker/response.d.ts +17 -19
- package/dist/types/wallet/serviceWorker/utils.d.ts +8 -0
- package/dist/types/wallet/serviceWorker/wallet.d.ts +36 -8
- package/dist/types/wallet/serviceWorker/worker.d.ts +7 -3
- package/dist/types/wallet/unroll.d.ts +102 -0
- package/dist/types/wallet/wallet.d.ts +71 -25
- package/package.json +14 -15
- package/dist/cjs/identity/inMemoryKey.js +0 -40
- package/dist/cjs/tree/vtxoTree.js +0 -231
- package/dist/cjs/utils/coinselect.js +0 -73
- package/dist/cjs/utils/psbt.js +0 -137
- package/dist/esm/identity/inMemoryKey.js +0 -36
- package/dist/esm/tree/vtxoTree.js +0 -191
- package/dist/esm/utils/coinselect.js +0 -69
- package/dist/esm/utils/psbt.js +0 -131
- package/dist/types/identity/inMemoryKey.d.ts +0 -12
- package/dist/types/tree/vtxoTree.d.ts +0 -33
- package/dist/types/utils/coinselect.d.ts +0 -21
- package/dist/types/utils/psbt.d.ts +0 -11
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { pubSchnorr, randomPrivateKeyBytes } from "@scure/btc-signer/utils";
|
|
2
|
-
import { hex } from "@scure/base";
|
|
3
|
-
import { TreeSignerSession } from '../tree/signingSession.js';
|
|
4
|
-
const ZERO_32 = new Uint8Array(32).fill(0);
|
|
5
|
-
export class InMemoryKey {
|
|
6
|
-
constructor(key) {
|
|
7
|
-
this.key = key || randomPrivateKeyBytes();
|
|
8
|
-
}
|
|
9
|
-
static fromPrivateKey(privateKey) {
|
|
10
|
-
return new InMemoryKey(privateKey);
|
|
11
|
-
}
|
|
12
|
-
static fromHex(privateKeyHex) {
|
|
13
|
-
return new InMemoryKey(hex.decode(privateKeyHex));
|
|
14
|
-
}
|
|
15
|
-
async sign(tx, inputIndexes) {
|
|
16
|
-
const txCpy = tx.clone();
|
|
17
|
-
if (!inputIndexes) {
|
|
18
|
-
if (!txCpy.sign(this.key, undefined, ZERO_32)) {
|
|
19
|
-
throw new Error("Failed to sign transaction");
|
|
20
|
-
}
|
|
21
|
-
return txCpy;
|
|
22
|
-
}
|
|
23
|
-
for (const inputIndex of inputIndexes) {
|
|
24
|
-
if (!txCpy.signIdx(this.key, inputIndex, undefined, ZERO_32)) {
|
|
25
|
-
throw new Error(`Failed to sign input #${inputIndex}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return txCpy;
|
|
29
|
-
}
|
|
30
|
-
xOnlyPublicKey() {
|
|
31
|
-
return pubSchnorr(this.key);
|
|
32
|
-
}
|
|
33
|
-
signerSession() {
|
|
34
|
-
return TreeSignerSession.random();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import * as bip68 from "bip68";
|
|
2
|
-
import { RawTx, ScriptNum, Transaction } from "@scure/btc-signer";
|
|
3
|
-
import { sha256x2 } from "@scure/btc-signer/utils";
|
|
4
|
-
import { base64, hex } from "@scure/base";
|
|
5
|
-
export class TxTreeError extends Error {
|
|
6
|
-
constructor(message) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.name = "TxTreeError";
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
export const ErrLeafNotFound = new TxTreeError("leaf not found in tx tree");
|
|
12
|
-
export const ErrParentNotFound = new TxTreeError("parent not found");
|
|
13
|
-
// TxTree is represented as a matrix of Node objects
|
|
14
|
-
// the first level of the matrix is the root of the tree
|
|
15
|
-
export class TxTree {
|
|
16
|
-
constructor(tree) {
|
|
17
|
-
this.tree = tree;
|
|
18
|
-
}
|
|
19
|
-
get levels() {
|
|
20
|
-
return this.tree;
|
|
21
|
-
}
|
|
22
|
-
// Returns the root node of the vtxo tree
|
|
23
|
-
root() {
|
|
24
|
-
if (this.tree.length <= 0 || this.tree[0].length <= 0) {
|
|
25
|
-
throw new TxTreeError("empty vtxo tree");
|
|
26
|
-
}
|
|
27
|
-
return this.tree[0][0];
|
|
28
|
-
}
|
|
29
|
-
// Returns the leaves of the vtxo tree
|
|
30
|
-
leaves() {
|
|
31
|
-
const leaves = [...this.tree[this.tree.length - 1]];
|
|
32
|
-
// Check other levels for leaf nodes
|
|
33
|
-
for (let i = 0; i < this.tree.length - 1; i++) {
|
|
34
|
-
for (const node of this.tree[i]) {
|
|
35
|
-
if (node.leaf) {
|
|
36
|
-
leaves.push(node);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return leaves;
|
|
41
|
-
}
|
|
42
|
-
// Returns all nodes that have the given node as parent
|
|
43
|
-
children(nodeTxid) {
|
|
44
|
-
const children = [];
|
|
45
|
-
for (const level of this.tree) {
|
|
46
|
-
for (const node of level) {
|
|
47
|
-
if (node.parentTxid === nodeTxid) {
|
|
48
|
-
children.push(node);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return children;
|
|
53
|
-
}
|
|
54
|
-
// Returns the total number of nodes in the vtxo tree
|
|
55
|
-
numberOfNodes() {
|
|
56
|
-
return this.tree.reduce((count, level) => count + level.length, 0);
|
|
57
|
-
}
|
|
58
|
-
// Returns the branch of the given vtxo txid from root to leaf
|
|
59
|
-
branch(vtxoTxid) {
|
|
60
|
-
const branch = [];
|
|
61
|
-
const leaves = this.leaves();
|
|
62
|
-
// Check if the vtxo is a leaf
|
|
63
|
-
const leaf = leaves.find((leaf) => leaf.txid === vtxoTxid);
|
|
64
|
-
if (!leaf) {
|
|
65
|
-
throw ErrLeafNotFound;
|
|
66
|
-
}
|
|
67
|
-
branch.push(leaf);
|
|
68
|
-
const rootTxid = this.root().txid;
|
|
69
|
-
while (branch[0].txid !== rootTxid) {
|
|
70
|
-
const parent = this.findParent(branch[0]);
|
|
71
|
-
branch.unshift(parent);
|
|
72
|
-
}
|
|
73
|
-
return branch;
|
|
74
|
-
}
|
|
75
|
-
// Returns the remaining transactions to broadcast in order to exit the vtxo
|
|
76
|
-
async exitBranch(vtxoTxid, isTxConfirmed) {
|
|
77
|
-
const offchainPart = await getOffchainPart(this.branch(vtxoTxid), isTxConfirmed);
|
|
78
|
-
return offchainPart.map(getExitTransaction);
|
|
79
|
-
}
|
|
80
|
-
// Helper method to find parent of a node
|
|
81
|
-
findParent(node) {
|
|
82
|
-
for (const level of this.tree) {
|
|
83
|
-
for (const potentialParent of level) {
|
|
84
|
-
if (potentialParent.txid === node.parentTxid) {
|
|
85
|
-
return potentialParent;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
throw ErrParentNotFound;
|
|
90
|
-
}
|
|
91
|
-
// Validates that the tree is coherent by checking txids and parent relationships
|
|
92
|
-
validate() {
|
|
93
|
-
// Skip the root level, validate from level 1 onwards
|
|
94
|
-
for (let i = 1; i < this.tree.length; i++) {
|
|
95
|
-
for (const node of this.tree[i]) {
|
|
96
|
-
// Verify that the node's transaction matches its claimed txid
|
|
97
|
-
const tx = Transaction.fromPSBT(base64.decode(node.tx));
|
|
98
|
-
const txid = hex.encode(sha256x2(tx.toBytes(true)).reverse());
|
|
99
|
-
if (txid !== node.txid) {
|
|
100
|
-
throw new TxTreeError(`node ${node.txid} has txid ${node.txid}, but computed txid is ${txid}`);
|
|
101
|
-
}
|
|
102
|
-
// Verify that the node has a valid parent
|
|
103
|
-
try {
|
|
104
|
-
this.findParent(node);
|
|
105
|
-
}
|
|
106
|
-
catch (err) {
|
|
107
|
-
throw new TxTreeError(`node ${node.txid} has no parent: ${err instanceof Error ? err.message : String(err)}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
const COSIGNER_KEY_PREFIX = new Uint8Array("cosigner".split("").map((c) => c.charCodeAt(0)));
|
|
114
|
-
const VTXO_TREE_EXPIRY_PSBT_KEY = new Uint8Array("expiry".split("").map((c) => c.charCodeAt(0)));
|
|
115
|
-
export function getVtxoTreeExpiry(input) {
|
|
116
|
-
if (!input.unknown)
|
|
117
|
-
return null;
|
|
118
|
-
for (const u of input.unknown) {
|
|
119
|
-
// Check if key contains the VTXO tree expiry key
|
|
120
|
-
if (u.key.length < VTXO_TREE_EXPIRY_PSBT_KEY.length)
|
|
121
|
-
continue;
|
|
122
|
-
let found = true;
|
|
123
|
-
for (let i = 0; i < VTXO_TREE_EXPIRY_PSBT_KEY.length; i++) {
|
|
124
|
-
if (u.key[i] !== VTXO_TREE_EXPIRY_PSBT_KEY[i]) {
|
|
125
|
-
found = false;
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if (found) {
|
|
130
|
-
const value = ScriptNum(6, true).decode(u.value);
|
|
131
|
-
const { blocks, seconds } = bip68.decode(Number(value));
|
|
132
|
-
return {
|
|
133
|
-
type: blocks ? "blocks" : "seconds",
|
|
134
|
-
value: BigInt(blocks ?? seconds ?? 0),
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
function parsePrefixedCosignerKey(key) {
|
|
141
|
-
if (key.length < COSIGNER_KEY_PREFIX.length)
|
|
142
|
-
return false;
|
|
143
|
-
for (let i = 0; i < COSIGNER_KEY_PREFIX.length; i++) {
|
|
144
|
-
if (key[i] !== COSIGNER_KEY_PREFIX[i])
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
export function getCosignerKeys(tx) {
|
|
150
|
-
const keys = [];
|
|
151
|
-
const input = tx.getInput(0);
|
|
152
|
-
if (!input.unknown)
|
|
153
|
-
return keys;
|
|
154
|
-
for (const unknown of input.unknown) {
|
|
155
|
-
const ok = parsePrefixedCosignerKey(new Uint8Array([unknown[0].type, ...unknown[0].key]));
|
|
156
|
-
if (!ok)
|
|
157
|
-
continue;
|
|
158
|
-
// Assuming the value is already a valid public key in compressed format
|
|
159
|
-
keys.push(unknown[1]);
|
|
160
|
-
}
|
|
161
|
-
return keys;
|
|
162
|
-
}
|
|
163
|
-
async function getOffchainPart(branch, isTxConfirmed) {
|
|
164
|
-
let offchainPath = [...branch];
|
|
165
|
-
// Iterate from the end of the branch (leaf) to the beginning (root)
|
|
166
|
-
for (let i = branch.length - 1; i >= 0; i--) {
|
|
167
|
-
const node = branch[i];
|
|
168
|
-
// check if the transaction is confirmed on-chain
|
|
169
|
-
if (await isTxConfirmed(node.txid)) {
|
|
170
|
-
// if this is the leaf node, return empty array as everything is confirmed
|
|
171
|
-
if (i === branch.length - 1) {
|
|
172
|
-
return [];
|
|
173
|
-
}
|
|
174
|
-
// otherwise, return the unconfirmed part of the branch
|
|
175
|
-
return branch.slice(i + 1);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
// no confirmation: everything is offchain
|
|
179
|
-
return offchainPath;
|
|
180
|
-
}
|
|
181
|
-
// getExitTransaction finalizes the psbt's input using the musig2 tapkey signature
|
|
182
|
-
function getExitTransaction(treeNode) {
|
|
183
|
-
const tx = Transaction.fromPSBT(base64.decode(treeNode.tx));
|
|
184
|
-
const input = tx.getInput(0);
|
|
185
|
-
if (!input.tapKeySig)
|
|
186
|
-
throw new TxTreeError("missing tapkey signature");
|
|
187
|
-
const rawTx = RawTx.decode(tx.unsignedTx);
|
|
188
|
-
rawTx.witnesses = [[input.tapKeySig]];
|
|
189
|
-
rawTx.segwitFlag = true;
|
|
190
|
-
return hex.encode(RawTx.encode(rawTx));
|
|
191
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Select coins to reach a target amount, prioritizing those closer to expiry
|
|
3
|
-
* @param coins List of coins to select from
|
|
4
|
-
* @param targetAmount Target amount to reach in satoshis
|
|
5
|
-
* @returns Selected coins and change amount, or null if insufficient funds
|
|
6
|
-
*/
|
|
7
|
-
export function selectCoins(coins, targetAmount) {
|
|
8
|
-
// Sort coins by amount (descending)
|
|
9
|
-
const sortedCoins = [...coins].sort((a, b) => b.value - a.value);
|
|
10
|
-
const selectedCoins = [];
|
|
11
|
-
let selectedAmount = 0;
|
|
12
|
-
// Select coins until we have enough
|
|
13
|
-
for (const coin of sortedCoins) {
|
|
14
|
-
selectedCoins.push(coin);
|
|
15
|
-
selectedAmount += coin.value;
|
|
16
|
-
if (selectedAmount >= targetAmount) {
|
|
17
|
-
break;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
// Check if we have enough
|
|
21
|
-
if (selectedAmount < targetAmount) {
|
|
22
|
-
return { inputs: null, changeAmount: 0 };
|
|
23
|
-
}
|
|
24
|
-
// Calculate change
|
|
25
|
-
const changeAmount = selectedAmount - targetAmount;
|
|
26
|
-
return {
|
|
27
|
-
inputs: selectedCoins,
|
|
28
|
-
changeAmount,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Select virtual coins to reach a target amount, prioritizing those closer to expiry
|
|
33
|
-
* @param coins List of virtual coins to select from
|
|
34
|
-
* @param targetAmount Target amount to reach in satoshis
|
|
35
|
-
* @returns Selected coins and change amount, or null if insufficient funds
|
|
36
|
-
*/
|
|
37
|
-
export function selectVirtualCoins(coins, targetAmount) {
|
|
38
|
-
// Sort VTXOs by expiry (ascending) and amount (descending)
|
|
39
|
-
const sortedCoins = [...coins].sort((a, b) => {
|
|
40
|
-
// First sort by expiry if available
|
|
41
|
-
const expiryA = a.virtualStatus.batchExpiry || Number.MAX_SAFE_INTEGER;
|
|
42
|
-
const expiryB = b.virtualStatus.batchExpiry || Number.MAX_SAFE_INTEGER;
|
|
43
|
-
if (expiryA !== expiryB) {
|
|
44
|
-
return expiryA - expiryB; // Earlier expiry first
|
|
45
|
-
}
|
|
46
|
-
// Then sort by amount
|
|
47
|
-
return b.value - a.value; // Larger amount first
|
|
48
|
-
});
|
|
49
|
-
const selectedCoins = [];
|
|
50
|
-
let selectedAmount = 0;
|
|
51
|
-
// Select coins until we have enough
|
|
52
|
-
for (const coin of sortedCoins) {
|
|
53
|
-
selectedCoins.push(coin);
|
|
54
|
-
selectedAmount += coin.value;
|
|
55
|
-
if (selectedAmount >= targetAmount) {
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// Check if we have enough
|
|
60
|
-
if (selectedAmount < targetAmount) {
|
|
61
|
-
return { inputs: null, changeAmount: 0 };
|
|
62
|
-
}
|
|
63
|
-
// Calculate change
|
|
64
|
-
const changeAmount = selectedAmount - targetAmount;
|
|
65
|
-
return {
|
|
66
|
-
inputs: selectedCoins,
|
|
67
|
-
changeAmount,
|
|
68
|
-
};
|
|
69
|
-
}
|
package/dist/esm/utils/psbt.js
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_SEQUENCE, RawWitness, Transaction } from "@scure/btc-signer";
|
|
2
|
-
import { CLTVMultisigTapscript, decodeTapscript } from '../script/tapscript.js';
|
|
3
|
-
import { scriptFromTapLeafScript, VtxoScript, } from '../script/base.js';
|
|
4
|
-
import { ArkAddress } from '../script/address.js';
|
|
5
|
-
import { hex } from "@scure/base";
|
|
6
|
-
const ARK_UNKNOWN_KEY_TYPE = 255;
|
|
7
|
-
// Constant for condition witness key prefix
|
|
8
|
-
export const CONDITION_WITNESS_KEY_PREFIX = new TextEncoder().encode("condition");
|
|
9
|
-
export const VTXO_TAPROOT_TREE_KEY_PREFIX = new TextEncoder().encode("taptree");
|
|
10
|
-
export function addVtxoTaprootTree(inIndex, tx, scripts) {
|
|
11
|
-
tx.updateInput(inIndex, {
|
|
12
|
-
unknown: [
|
|
13
|
-
...(tx.getInput(inIndex)?.unknown ?? []),
|
|
14
|
-
[
|
|
15
|
-
{
|
|
16
|
-
type: ARK_UNKNOWN_KEY_TYPE,
|
|
17
|
-
key: VTXO_TAPROOT_TREE_KEY_PREFIX,
|
|
18
|
-
},
|
|
19
|
-
encodeTaprootTree(scripts),
|
|
20
|
-
],
|
|
21
|
-
],
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
export function addConditionWitness(inIndex, tx, witness) {
|
|
25
|
-
const witnessBytes = RawWitness.encode(witness);
|
|
26
|
-
tx.updateInput(inIndex, {
|
|
27
|
-
unknown: [
|
|
28
|
-
...(tx.getInput(inIndex)?.unknown ?? []),
|
|
29
|
-
[
|
|
30
|
-
{
|
|
31
|
-
type: ARK_UNKNOWN_KEY_TYPE,
|
|
32
|
-
key: CONDITION_WITNESS_KEY_PREFIX,
|
|
33
|
-
},
|
|
34
|
-
witnessBytes,
|
|
35
|
-
],
|
|
36
|
-
],
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
export function createVirtualTx(inputs, outputs) {
|
|
40
|
-
let lockTime = 0n;
|
|
41
|
-
for (const input of inputs) {
|
|
42
|
-
const tapscript = decodeTapscript(scriptFromTapLeafScript(input.tapLeafScript));
|
|
43
|
-
if (CLTVMultisigTapscript.is(tapscript)) {
|
|
44
|
-
if (lockTime !== 0n) {
|
|
45
|
-
// if a locktime is already set, check if the new locktime is in the same unit
|
|
46
|
-
if (isSeconds(lockTime) !==
|
|
47
|
-
isSeconds(tapscript.params.absoluteTimelock)) {
|
|
48
|
-
throw new Error("cannot mix seconds and blocks locktime");
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (tapscript.params.absoluteTimelock > lockTime) {
|
|
52
|
-
lockTime = tapscript.params.absoluteTimelock;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
const tx = new Transaction({
|
|
57
|
-
allowUnknown: true,
|
|
58
|
-
lockTime: Number(lockTime),
|
|
59
|
-
});
|
|
60
|
-
for (const [i, input] of inputs.entries()) {
|
|
61
|
-
tx.addInput({
|
|
62
|
-
txid: input.txid,
|
|
63
|
-
index: input.vout,
|
|
64
|
-
sequence: lockTime ? DEFAULT_SEQUENCE - 1 : undefined,
|
|
65
|
-
witnessUtxo: {
|
|
66
|
-
script: VtxoScript.decode(input.scripts).pkScript,
|
|
67
|
-
amount: BigInt(input.value),
|
|
68
|
-
},
|
|
69
|
-
tapLeafScript: [input.tapLeafScript],
|
|
70
|
-
});
|
|
71
|
-
// add BIP371 encoded taproot tree to the unknown key field
|
|
72
|
-
addVtxoTaprootTree(i, tx, input.scripts.map(hex.decode));
|
|
73
|
-
}
|
|
74
|
-
for (const output of outputs) {
|
|
75
|
-
tx.addOutput({
|
|
76
|
-
amount: output.amount,
|
|
77
|
-
script: ArkAddress.decode(output.address).pkScript,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
return tx;
|
|
81
|
-
}
|
|
82
|
-
function encodeTaprootTree(leaves) {
|
|
83
|
-
const chunks = [];
|
|
84
|
-
// Write number of leaves as compact size uint
|
|
85
|
-
chunks.push(encodeCompactSizeUint(leaves.length));
|
|
86
|
-
for (const tapscript of leaves) {
|
|
87
|
-
// Write depth (always 1 for now)
|
|
88
|
-
chunks.push(new Uint8Array([1]));
|
|
89
|
-
// Write leaf version (0xc0 for tapscript)
|
|
90
|
-
chunks.push(new Uint8Array([0xc0]));
|
|
91
|
-
// Write script length and script
|
|
92
|
-
chunks.push(encodeCompactSizeUint(tapscript.length));
|
|
93
|
-
chunks.push(tapscript);
|
|
94
|
-
}
|
|
95
|
-
// Concatenate all chunks
|
|
96
|
-
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
97
|
-
const result = new Uint8Array(totalLength);
|
|
98
|
-
let offset = 0;
|
|
99
|
-
for (const chunk of chunks) {
|
|
100
|
-
result.set(chunk, offset);
|
|
101
|
-
offset += chunk.length;
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
104
|
-
}
|
|
105
|
-
function encodeCompactSizeUint(value) {
|
|
106
|
-
if (value < 0xfd) {
|
|
107
|
-
return new Uint8Array([value]);
|
|
108
|
-
}
|
|
109
|
-
else if (value <= 0xffff) {
|
|
110
|
-
const buffer = new Uint8Array(3);
|
|
111
|
-
buffer[0] = 0xfd;
|
|
112
|
-
new DataView(buffer.buffer).setUint16(1, value, true);
|
|
113
|
-
return buffer;
|
|
114
|
-
}
|
|
115
|
-
else if (value <= 0xffffffff) {
|
|
116
|
-
const buffer = new Uint8Array(5);
|
|
117
|
-
buffer[0] = 0xfe;
|
|
118
|
-
new DataView(buffer.buffer).setUint32(1, value, true);
|
|
119
|
-
return buffer;
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
const buffer = new Uint8Array(9);
|
|
123
|
-
buffer[0] = 0xff;
|
|
124
|
-
new DataView(buffer.buffer).setBigUint64(1, BigInt(value), true);
|
|
125
|
-
return buffer;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
const nLocktimeMinSeconds = 500000000n;
|
|
129
|
-
function isSeconds(locktime) {
|
|
130
|
-
return locktime >= nLocktimeMinSeconds;
|
|
131
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Transaction } from "@scure/btc-signer";
|
|
2
|
-
import { Identity } from ".";
|
|
3
|
-
import { SignerSession } from "../tree/signingSession";
|
|
4
|
-
export declare class InMemoryKey implements Identity {
|
|
5
|
-
private key;
|
|
6
|
-
private constructor();
|
|
7
|
-
static fromPrivateKey(privateKey: Uint8Array): InMemoryKey;
|
|
8
|
-
static fromHex(privateKeyHex: string): InMemoryKey;
|
|
9
|
-
sign(tx: Transaction, inputIndexes?: number[]): Promise<Transaction>;
|
|
10
|
-
xOnlyPublicKey(): Uint8Array;
|
|
11
|
-
signerSession(): SignerSession;
|
|
12
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { Transaction } from "@scure/btc-signer";
|
|
2
|
-
import { RelativeTimelock } from "../script/tapscript";
|
|
3
|
-
export interface TreeNode {
|
|
4
|
-
txid: string;
|
|
5
|
-
tx: string;
|
|
6
|
-
parentTxid: string;
|
|
7
|
-
leaf: boolean;
|
|
8
|
-
}
|
|
9
|
-
export declare class TxTreeError extends Error {
|
|
10
|
-
constructor(message: string);
|
|
11
|
-
}
|
|
12
|
-
export declare const ErrLeafNotFound: TxTreeError;
|
|
13
|
-
export declare const ErrParentNotFound: TxTreeError;
|
|
14
|
-
export declare class TxTree {
|
|
15
|
-
private tree;
|
|
16
|
-
constructor(tree: TreeNode[][]);
|
|
17
|
-
get levels(): TreeNode[][];
|
|
18
|
-
root(): TreeNode;
|
|
19
|
-
leaves(): TreeNode[];
|
|
20
|
-
children(nodeTxid: string): TreeNode[];
|
|
21
|
-
numberOfNodes(): number;
|
|
22
|
-
branch(vtxoTxid: string): TreeNode[];
|
|
23
|
-
exitBranch(vtxoTxid: string, isTxConfirmed: (txid: string) => Promise<boolean>): Promise<string[]>;
|
|
24
|
-
private findParent;
|
|
25
|
-
validate(): void;
|
|
26
|
-
}
|
|
27
|
-
export declare function getVtxoTreeExpiry(input: {
|
|
28
|
-
unknown?: {
|
|
29
|
-
key: Uint8Array;
|
|
30
|
-
value: Uint8Array;
|
|
31
|
-
}[];
|
|
32
|
-
}): RelativeTimelock | null;
|
|
33
|
-
export declare function getCosignerKeys(tx: Transaction): Uint8Array[];
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Coin, VirtualCoin } from "../wallet";
|
|
2
|
-
/**
|
|
3
|
-
* Select coins to reach a target amount, prioritizing those closer to expiry
|
|
4
|
-
* @param coins List of coins to select from
|
|
5
|
-
* @param targetAmount Target amount to reach in satoshis
|
|
6
|
-
* @returns Selected coins and change amount, or null if insufficient funds
|
|
7
|
-
*/
|
|
8
|
-
export declare function selectCoins(coins: Coin[], targetAmount: number): {
|
|
9
|
-
inputs: Coin[] | null;
|
|
10
|
-
changeAmount: number;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Select virtual coins to reach a target amount, prioritizing those closer to expiry
|
|
14
|
-
* @param coins List of virtual coins to select from
|
|
15
|
-
* @param targetAmount Target amount to reach in satoshis
|
|
16
|
-
* @returns Selected coins and change amount, or null if insufficient funds
|
|
17
|
-
*/
|
|
18
|
-
export declare function selectVirtualCoins(coins: VirtualCoin[], targetAmount: number): {
|
|
19
|
-
inputs: VirtualCoin[] | null;
|
|
20
|
-
changeAmount: number;
|
|
21
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Transaction } from "@scure/btc-signer";
|
|
2
|
-
import { VirtualCoin } from "../wallet";
|
|
3
|
-
import { Output } from "../providers/ark";
|
|
4
|
-
import { EncodedVtxoScript, TapLeafScript } from "../script/base";
|
|
5
|
-
export declare const CONDITION_WITNESS_KEY_PREFIX: Uint8Array<ArrayBufferLike>;
|
|
6
|
-
export declare const VTXO_TAPROOT_TREE_KEY_PREFIX: Uint8Array<ArrayBufferLike>;
|
|
7
|
-
export declare function addVtxoTaprootTree(inIndex: number, tx: Transaction, scripts: Uint8Array[]): void;
|
|
8
|
-
export declare function addConditionWitness(inIndex: number, tx: Transaction, witness: Uint8Array[]): void;
|
|
9
|
-
export declare function createVirtualTx(inputs: ({
|
|
10
|
-
tapLeafScript: TapLeafScript;
|
|
11
|
-
} & EncodedVtxoScript & Pick<VirtualCoin, "txid" | "vout" | "value">)[], outputs: Output[]): Transaction;
|