@arkade-os/sdk 0.3.0-alpha.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -14
- package/dist/cjs/adapters/expo.js +8 -0
- package/dist/cjs/arknote/index.js +3 -3
- package/dist/cjs/forfeit.js +2 -2
- package/dist/cjs/identity/singleKey.js +8 -8
- package/dist/cjs/index.js +14 -5
- package/dist/cjs/{bip322 → intent}/index.js +38 -61
- package/dist/cjs/musig2/index.js +2 -1
- package/dist/cjs/musig2/nonces.js +4 -0
- package/dist/cjs/providers/ark.js +76 -45
- package/dist/cjs/providers/errors.js +59 -0
- package/dist/cjs/providers/expoArk.js +82 -0
- package/dist/cjs/providers/expoIndexer.js +105 -0
- package/dist/cjs/providers/expoUtils.js +124 -0
- package/dist/cjs/providers/indexer.js +3 -1
- package/dist/cjs/providers/onchain.js +19 -20
- package/dist/cjs/repositories/walletRepository.js +64 -28
- package/dist/cjs/script/base.js +15 -7
- package/dist/cjs/script/tapscript.js +20 -21
- package/dist/cjs/script/vhtlc.js +2 -2
- package/dist/cjs/tree/signingSession.js +44 -11
- package/dist/cjs/tree/txTree.js +3 -4
- package/dist/cjs/tree/validation.js +2 -3
- package/dist/cjs/utils/arkTransaction.js +118 -15
- package/dist/cjs/utils/transaction.js +28 -0
- package/dist/cjs/utils/unknownFields.js +7 -7
- package/dist/cjs/wallet/index.js +1 -1
- package/dist/cjs/wallet/onchain.js +6 -7
- package/dist/cjs/wallet/serviceWorker/response.js +32 -0
- package/dist/cjs/wallet/serviceWorker/utils.js +2 -9
- package/dist/cjs/wallet/serviceWorker/wallet.js +7 -8
- package/dist/cjs/wallet/serviceWorker/worker.js +48 -32
- package/dist/cjs/wallet/unroll.js +7 -9
- package/dist/cjs/wallet/utils.js +20 -0
- package/dist/cjs/wallet/vtxo-manager.js +323 -0
- package/dist/cjs/wallet/wallet.js +165 -174
- package/dist/esm/adapters/expo.js +3 -0
- package/dist/esm/arknote/index.js +2 -2
- package/dist/esm/forfeit.js +1 -1
- package/dist/esm/identity/singleKey.js +9 -9
- package/dist/esm/index.js +14 -10
- package/dist/esm/{bip322 → intent}/index.js +32 -54
- package/dist/esm/musig2/index.js +1 -1
- package/dist/esm/musig2/nonces.js +3 -0
- package/dist/esm/providers/ark.js +76 -45
- package/dist/esm/providers/errors.js +54 -0
- package/dist/esm/providers/expoArk.js +78 -0
- package/dist/esm/providers/expoIndexer.js +101 -0
- package/dist/esm/providers/expoUtils.js +87 -0
- package/dist/esm/providers/indexer.js +3 -1
- package/dist/esm/providers/onchain.js +19 -20
- package/dist/esm/repositories/walletRepository.js +64 -28
- package/dist/esm/script/base.js +12 -4
- package/dist/esm/script/tapscript.js +1 -2
- package/dist/esm/script/vhtlc.js +1 -1
- package/dist/esm/tree/signingSession.js +45 -12
- package/dist/esm/tree/txTree.js +3 -4
- package/dist/esm/tree/validation.js +2 -3
- package/dist/esm/utils/arkTransaction.js +110 -9
- package/dist/esm/utils/transaction.js +24 -0
- package/dist/esm/utils/unknownFields.js +3 -3
- package/dist/esm/wallet/index.js +1 -1
- package/dist/esm/wallet/onchain.js +3 -4
- package/dist/esm/wallet/serviceWorker/response.js +32 -0
- package/dist/esm/wallet/serviceWorker/utils.js +1 -8
- package/dist/esm/wallet/serviceWorker/wallet.js +8 -9
- package/dist/esm/wallet/serviceWorker/worker.js +49 -33
- package/dist/esm/wallet/unroll.js +5 -7
- package/dist/esm/wallet/utils.js +16 -0
- package/dist/esm/wallet/vtxo-manager.js +317 -0
- package/dist/esm/wallet/wallet.js +159 -168
- package/dist/types/adapters/expo.d.ts +4 -0
- package/dist/types/arknote/index.d.ts +1 -1
- package/dist/types/forfeit.d.ts +2 -2
- package/dist/types/identity/index.d.ts +2 -2
- package/dist/types/identity/singleKey.d.ts +2 -2
- package/dist/types/index.d.ts +11 -9
- package/dist/types/intent/index.d.ts +41 -0
- package/dist/types/musig2/index.d.ts +1 -1
- package/dist/types/musig2/nonces.d.ts +1 -0
- package/dist/types/providers/ark.d.ts +197 -27
- package/dist/types/providers/errors.d.ts +13 -0
- package/dist/types/providers/expoArk.d.ts +22 -0
- package/dist/types/providers/expoIndexer.d.ts +18 -0
- package/dist/types/providers/expoUtils.d.ts +18 -0
- package/dist/types/providers/indexer.d.ts +8 -8
- package/dist/types/providers/onchain.d.ts +6 -2
- package/dist/types/repositories/walletRepository.d.ts +9 -5
- package/dist/types/script/base.d.ts +5 -2
- package/dist/types/tree/signingSession.d.ts +16 -11
- package/dist/types/utils/anchor.d.ts +2 -2
- package/dist/types/utils/arkTransaction.d.ts +15 -5
- package/dist/types/utils/transaction.d.ts +13 -0
- package/dist/types/utils/unknownFields.d.ts +4 -4
- package/dist/types/wallet/index.d.ts +47 -7
- package/dist/types/wallet/onchain.d.ts +1 -1
- package/dist/types/wallet/serviceWorker/response.d.ts +16 -2
- package/dist/types/wallet/serviceWorker/utils.d.ts +1 -2
- package/dist/types/wallet/serviceWorker/wallet.d.ts +2 -2
- package/dist/types/wallet/serviceWorker/worker.d.ts +7 -1
- package/dist/types/wallet/unroll.d.ts +1 -1
- package/dist/types/wallet/utils.d.ts +3 -0
- package/dist/types/wallet/vtxo-manager.d.ts +179 -0
- package/dist/types/wallet/wallet.d.ts +17 -5
- package/package.json +11 -3
- package/dist/cjs/bip322/errors.js +0 -13
- package/dist/esm/bip322/errors.js +0 -9
- package/dist/types/bip322/errors.d.ts +0 -6
- package/dist/types/bip322/index.d.ts +0 -57
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildOffchainTx = buildOffchainTx;
|
|
4
|
-
|
|
4
|
+
exports.hasBoardingTxExpired = hasBoardingTxExpired;
|
|
5
|
+
exports.verifyTapscriptSignatures = verifyTapscriptSignatures;
|
|
6
|
+
const secp256k1_js_1 = require("@noble/curves/secp256k1.js");
|
|
7
|
+
const base_1 = require("@scure/base");
|
|
8
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
9
|
+
const payment_js_1 = require("@scure/btc-signer/payment.js");
|
|
5
10
|
const tapscript_1 = require("../script/tapscript");
|
|
6
|
-
const
|
|
11
|
+
const base_2 = require("../script/base");
|
|
7
12
|
const anchor_1 = require("./anchor");
|
|
8
|
-
const base_2 = require("@scure/base");
|
|
9
|
-
const utils_js_1 = require("@scure/btc-signer/utils.js");
|
|
10
13
|
const unknownFields_1 = require("./unknownFields");
|
|
14
|
+
const transaction_1 = require("./transaction");
|
|
11
15
|
/**
|
|
12
16
|
* Builds an offchain transaction with checkpoint transactions.
|
|
13
17
|
*
|
|
@@ -31,7 +35,7 @@ function buildOffchainTx(inputs, outputs, serverUnrollScript) {
|
|
|
31
35
|
function buildVirtualTx(inputs, outputs) {
|
|
32
36
|
let lockTime = 0n;
|
|
33
37
|
for (const input of inputs) {
|
|
34
|
-
const tapscript = (0, tapscript_1.decodeTapscript)((0,
|
|
38
|
+
const tapscript = (0, tapscript_1.decodeTapscript)((0, base_2.scriptFromTapLeafScript)(input.tapLeafScript));
|
|
35
39
|
if (tapscript_1.CLTVMultisigTapscript.is(tapscript)) {
|
|
36
40
|
if (lockTime !== 0n) {
|
|
37
41
|
// if a locktime is already set, check if the new locktime is in the same unit
|
|
@@ -45,19 +49,17 @@ function buildVirtualTx(inputs, outputs) {
|
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
|
-
const tx = new
|
|
52
|
+
const tx = new transaction_1.Transaction({
|
|
49
53
|
version: 3,
|
|
50
|
-
allowUnknown: true,
|
|
51
|
-
allowUnknownOutputs: true,
|
|
52
54
|
lockTime: Number(lockTime),
|
|
53
55
|
});
|
|
54
56
|
for (const [i, input] of inputs.entries()) {
|
|
55
57
|
tx.addInput({
|
|
56
58
|
txid: input.txid,
|
|
57
59
|
index: input.vout,
|
|
58
|
-
sequence: lockTime ?
|
|
60
|
+
sequence: lockTime ? btc_signer_1.DEFAULT_SEQUENCE - 1 : undefined,
|
|
59
61
|
witnessUtxo: {
|
|
60
|
-
script:
|
|
62
|
+
script: base_2.VtxoScript.decode(input.tapTree).pkScript,
|
|
61
63
|
amount: BigInt(input.value),
|
|
62
64
|
},
|
|
63
65
|
tapLeafScript: [input.tapLeafScript],
|
|
@@ -73,10 +75,9 @@ function buildVirtualTx(inputs, outputs) {
|
|
|
73
75
|
}
|
|
74
76
|
function buildCheckpointTx(vtxo, serverUnrollScript) {
|
|
75
77
|
// create the checkpoint vtxo script from collaborative closure
|
|
76
|
-
const collaborativeClosure = (0, tapscript_1.decodeTapscript)(vtxo.
|
|
77
|
-
(0, base_1.scriptFromTapLeafScript)(vtxo.tapLeafScript));
|
|
78
|
+
const collaborativeClosure = (0, tapscript_1.decodeTapscript)((0, base_2.scriptFromTapLeafScript)(vtxo.tapLeafScript));
|
|
78
79
|
// create the checkpoint vtxo script combining collaborative closure and server unroll script
|
|
79
|
-
const checkpointVtxoScript = new
|
|
80
|
+
const checkpointVtxoScript = new base_2.VtxoScript([
|
|
80
81
|
serverUnrollScript.script,
|
|
81
82
|
collaborativeClosure.script,
|
|
82
83
|
]);
|
|
@@ -88,10 +89,10 @@ function buildCheckpointTx(vtxo, serverUnrollScript) {
|
|
|
88
89
|
},
|
|
89
90
|
]);
|
|
90
91
|
// get the collaborative leaf proof
|
|
91
|
-
const collaborativeLeafProof = checkpointVtxoScript.findLeaf(
|
|
92
|
+
const collaborativeLeafProof = checkpointVtxoScript.findLeaf(base_1.hex.encode(collaborativeClosure.script));
|
|
92
93
|
// create the checkpoint input that will be used as input of the virtual tx
|
|
93
94
|
const checkpointInput = {
|
|
94
|
-
txid:
|
|
95
|
+
txid: checkpointTx.id,
|
|
95
96
|
vout: 0,
|
|
96
97
|
value: vtxo.value,
|
|
97
98
|
tapLeafScript: collaborativeLeafProof,
|
|
@@ -106,3 +107,105 @@ const nLocktimeMinSeconds = 500000000n;
|
|
|
106
107
|
function isSeconds(locktime) {
|
|
107
108
|
return locktime >= nLocktimeMinSeconds;
|
|
108
109
|
}
|
|
110
|
+
function hasBoardingTxExpired(coin, boardingTimelock) {
|
|
111
|
+
if (!coin.status.block_time)
|
|
112
|
+
return false;
|
|
113
|
+
if (boardingTimelock.value === 0n)
|
|
114
|
+
return true;
|
|
115
|
+
if (boardingTimelock.type !== "blocks")
|
|
116
|
+
return false; // TODO: handle get chain tip
|
|
117
|
+
// validate expiry in terms of seconds
|
|
118
|
+
const now = BigInt(Math.floor(Date.now() / 1000));
|
|
119
|
+
const blockTime = BigInt(Math.floor(coin.status.block_time));
|
|
120
|
+
return blockTime + boardingTimelock.value <= now;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Formats a sighash type as a hex string (e.g., 0x01)
|
|
124
|
+
*/
|
|
125
|
+
function formatSighash(type) {
|
|
126
|
+
return `0x${type.toString(16).padStart(2, "0")}`;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Verify tapscript signatures on a transaction input
|
|
130
|
+
* @param tx Transaction to verify
|
|
131
|
+
* @param inputIndex Index of the input to verify
|
|
132
|
+
* @param requiredSigners List of required signer pubkeys (hex encoded)
|
|
133
|
+
* @param excludePubkeys List of pubkeys to exclude from verification (hex encoded, e.g., server key not yet signed)
|
|
134
|
+
* @param allowedSighashTypes List of allowed sighash types (defaults to [SigHash.DEFAULT])
|
|
135
|
+
* @throws Error if verification fails
|
|
136
|
+
*/
|
|
137
|
+
function verifyTapscriptSignatures(tx, inputIndex, requiredSigners, excludePubkeys = [], allowedSighashTypes = [btc_signer_1.SigHash.DEFAULT]) {
|
|
138
|
+
const input = tx.getInput(inputIndex);
|
|
139
|
+
// Collect prevout scripts and amounts for ALL inputs (required for preimageWitnessV1)
|
|
140
|
+
const prevoutScripts = [];
|
|
141
|
+
const prevoutAmounts = [];
|
|
142
|
+
for (let i = 0; i < tx.inputsLength; i++) {
|
|
143
|
+
const inp = tx.getInput(i);
|
|
144
|
+
if (!inp.witnessUtxo) {
|
|
145
|
+
throw new Error(`Input ${i} is missing witnessUtxo`);
|
|
146
|
+
}
|
|
147
|
+
prevoutScripts.push(inp.witnessUtxo.script);
|
|
148
|
+
prevoutAmounts.push(inp.witnessUtxo.amount);
|
|
149
|
+
}
|
|
150
|
+
// Verify tapScriptSig signatures
|
|
151
|
+
if (!input.tapScriptSig || input.tapScriptSig.length === 0) {
|
|
152
|
+
throw new Error(`Input ${inputIndex} is missing tapScriptSig`);
|
|
153
|
+
}
|
|
154
|
+
// Verify each signature in tapScriptSig
|
|
155
|
+
for (const [tapScriptSigData, signature] of input.tapScriptSig) {
|
|
156
|
+
const pubKey = tapScriptSigData.pubKey;
|
|
157
|
+
const pubKeyHex = base_1.hex.encode(pubKey);
|
|
158
|
+
// Skip verification for excluded pubkeys
|
|
159
|
+
if (excludePubkeys.includes(pubKeyHex)) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
// Extract sighash type from signature
|
|
163
|
+
// Schnorr signatures are 64 bytes, with optional 1-byte sighash appended
|
|
164
|
+
const sighashType = signature.length === 65 ? signature[64] : btc_signer_1.SigHash.DEFAULT;
|
|
165
|
+
const sig = signature.subarray(0, 64);
|
|
166
|
+
// Verify sighash type is allowed
|
|
167
|
+
if (!allowedSighashTypes.includes(sighashType)) {
|
|
168
|
+
const sighashName = formatSighash(sighashType);
|
|
169
|
+
throw new Error(`Unallowed sighash type ${sighashName} for input ${inputIndex}, pubkey ${pubKeyHex}.`);
|
|
170
|
+
}
|
|
171
|
+
// Find the tapLeafScript that matches this signature's leafHash
|
|
172
|
+
if (!input.tapLeafScript || input.tapLeafScript.length === 0) {
|
|
173
|
+
throw new Error();
|
|
174
|
+
}
|
|
175
|
+
// Search for the leaf that matches the leafHash in tapScriptSigData
|
|
176
|
+
const leafHash = tapScriptSigData.leafHash;
|
|
177
|
+
const leafHashHex = base_1.hex.encode(leafHash);
|
|
178
|
+
let matchingScript;
|
|
179
|
+
let matchingVersion;
|
|
180
|
+
for (const [_, scriptWithVersion] of input.tapLeafScript) {
|
|
181
|
+
const script = scriptWithVersion.subarray(0, -1);
|
|
182
|
+
const version = scriptWithVersion[scriptWithVersion.length - 1];
|
|
183
|
+
// Compute the leaf hash for this script and compare as hex strings
|
|
184
|
+
const computedLeafHash = (0, payment_js_1.tapLeafHash)(script, version);
|
|
185
|
+
const computedHex = base_1.hex.encode(computedLeafHash);
|
|
186
|
+
if (computedHex === leafHashHex) {
|
|
187
|
+
matchingScript = script;
|
|
188
|
+
matchingVersion = version;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!matchingScript || matchingVersion === undefined) {
|
|
193
|
+
throw new Error(`Input ${inputIndex}: No tapLeafScript found matching leafHash ${base_1.hex.encode(leafHash)}`);
|
|
194
|
+
}
|
|
195
|
+
// Reconstruct the message that was signed
|
|
196
|
+
// Note: preimageWitnessV1 requires ALL input prevout scripts and amounts
|
|
197
|
+
const message = tx.preimageWitnessV1(inputIndex, prevoutScripts, sighashType, prevoutAmounts, undefined, matchingScript, matchingVersion);
|
|
198
|
+
// Verify the schnorr signature
|
|
199
|
+
const isValid = secp256k1_js_1.schnorr.verify(sig, message, pubKey);
|
|
200
|
+
if (!isValid) {
|
|
201
|
+
throw new Error(`Invalid signature for input ${inputIndex}, pubkey ${pubKeyHex}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Verify we have signatures from all required signers (excluding those we're skipping)
|
|
205
|
+
const signedPubkeys = input.tapScriptSig.map(([data]) => base_1.hex.encode(data.pubKey));
|
|
206
|
+
const requiredNotExcluded = requiredSigners.filter((pk) => !excludePubkeys.includes(pk));
|
|
207
|
+
const missingSigners = requiredNotExcluded.filter((pk) => !signedPubkeys.includes(pk));
|
|
208
|
+
if (missingSigners.length > 0) {
|
|
209
|
+
throw new Error(`Missing signatures from: ${missingSigners.map((pk) => pk.slice(0, 16)).join(", ")}...`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Transaction = void 0;
|
|
4
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
5
|
+
/**
|
|
6
|
+
* Transaction is a wrapper around the @scure/btc-signer Transaction class.
|
|
7
|
+
* It adds the Ark protocol specific options to the transaction.
|
|
8
|
+
*/
|
|
9
|
+
class Transaction extends btc_signer_1.Transaction {
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
super(withArkOpts(opts));
|
|
12
|
+
}
|
|
13
|
+
static fromPSBT(psbt_, opts) {
|
|
14
|
+
return btc_signer_1.Transaction.fromPSBT(psbt_, withArkOpts(opts));
|
|
15
|
+
}
|
|
16
|
+
static fromRaw(raw, opts) {
|
|
17
|
+
return btc_signer_1.Transaction.fromRaw(raw, withArkOpts(opts));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.Transaction = Transaction;
|
|
21
|
+
Transaction.ARK_TX_OPTS = {
|
|
22
|
+
allowUnknown: true,
|
|
23
|
+
allowUnknownOutputs: true,
|
|
24
|
+
allowUnknownInputs: true,
|
|
25
|
+
};
|
|
26
|
+
function withArkOpts(opts) {
|
|
27
|
+
return { ...Transaction.ARK_TX_OPTS, ...opts };
|
|
28
|
+
}
|
|
@@ -37,7 +37,7 @@ exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.ConditionWitness =
|
|
|
37
37
|
exports.setArkPsbtField = setArkPsbtField;
|
|
38
38
|
exports.getArkPsbtFields = getArkPsbtFields;
|
|
39
39
|
const bip68 = __importStar(require("bip68"));
|
|
40
|
-
const
|
|
40
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
41
41
|
const base_1 = require("@scure/base");
|
|
42
42
|
/**
|
|
43
43
|
* ArkPsbtFieldKey is the key values for ark psbt fields.
|
|
@@ -51,9 +51,9 @@ var ArkPsbtFieldKey;
|
|
|
51
51
|
})(ArkPsbtFieldKey || (exports.ArkPsbtFieldKey = ArkPsbtFieldKey = {}));
|
|
52
52
|
/**
|
|
53
53
|
* ArkPsbtFieldKeyType is the type of the ark psbt field key.
|
|
54
|
-
* Every ark psbt field has key type
|
|
54
|
+
* Every ark psbt field has key type 222.
|
|
55
55
|
*/
|
|
56
|
-
exports.ArkPsbtFieldKeyType =
|
|
56
|
+
exports.ArkPsbtFieldKeyType = 222;
|
|
57
57
|
/**
|
|
58
58
|
* setArkPsbtField appends a new unknown field to the input at inputIndex
|
|
59
59
|
*
|
|
@@ -126,12 +126,12 @@ exports.ConditionWitness = {
|
|
|
126
126
|
type: exports.ArkPsbtFieldKeyType,
|
|
127
127
|
key: encodedPsbtFieldKey[ArkPsbtFieldKey.ConditionWitness],
|
|
128
128
|
},
|
|
129
|
-
|
|
129
|
+
btc_signer_1.RawWitness.encode(value),
|
|
130
130
|
],
|
|
131
131
|
decode: (value) => nullIfCatch(() => {
|
|
132
132
|
if (!checkKeyIncludes(value[0], ArkPsbtFieldKey.ConditionWitness))
|
|
133
133
|
return null;
|
|
134
|
-
return
|
|
134
|
+
return btc_signer_1.RawWitness.decode(value[1]);
|
|
135
135
|
}),
|
|
136
136
|
};
|
|
137
137
|
/**
|
|
@@ -176,12 +176,12 @@ exports.VtxoTreeExpiry = {
|
|
|
176
176
|
type: exports.ArkPsbtFieldKeyType,
|
|
177
177
|
key: encodedPsbtFieldKey[ArkPsbtFieldKey.VtxoTreeExpiry],
|
|
178
178
|
},
|
|
179
|
-
(0,
|
|
179
|
+
(0, btc_signer_1.ScriptNum)(6, true).encode(value.value === 0n ? 0n : value.value),
|
|
180
180
|
],
|
|
181
181
|
decode: (unknown) => nullIfCatch(() => {
|
|
182
182
|
if (!checkKeyIncludes(unknown[0], ArkPsbtFieldKey.VtxoTreeExpiry))
|
|
183
183
|
return null;
|
|
184
|
-
const v = (0,
|
|
184
|
+
const v = (0, btc_signer_1.ScriptNum)(6, true).decode(unknown[1]);
|
|
185
185
|
if (!v)
|
|
186
186
|
return null;
|
|
187
187
|
const { blocks, seconds } = bip68.decode(Number(v));
|
package/dist/cjs/wallet/index.js
CHANGED
|
@@ -10,7 +10,7 @@ var TxType;
|
|
|
10
10
|
TxType["TxReceived"] = "RECEIVED";
|
|
11
11
|
})(TxType || (exports.TxType = TxType = {}));
|
|
12
12
|
function isSpendable(vtxo) {
|
|
13
|
-
return vtxo.
|
|
13
|
+
return !vtxo.isSpent;
|
|
14
14
|
}
|
|
15
15
|
function isRecoverable(vtxo) {
|
|
16
16
|
return vtxo.virtualStatus.state === "swept" && isSpendable(vtxo);
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OnchainWallet = void 0;
|
|
4
4
|
exports.selectCoins = selectCoins;
|
|
5
|
-
const
|
|
5
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
6
6
|
const networks_1 = require("../networks");
|
|
7
7
|
const onchain_1 = require("../providers/onchain");
|
|
8
|
-
const transaction_js_1 = require("@scure/btc-signer/transaction.js");
|
|
9
8
|
const anchor_1 = require("../utils/anchor");
|
|
10
9
|
const txSizeEstimator_1 = require("../utils/txSizeEstimator");
|
|
10
|
+
const transaction_1 = require("../utils/transaction");
|
|
11
11
|
/**
|
|
12
12
|
* Onchain Bitcoin wallet implementation for traditional Bitcoin transactions.
|
|
13
13
|
*
|
|
@@ -39,7 +39,7 @@ class OnchainWallet {
|
|
|
39
39
|
}
|
|
40
40
|
const network = (0, networks_1.getNetwork)(networkName);
|
|
41
41
|
const onchainProvider = provider || new onchain_1.EsploraProvider(onchain_1.ESPLORA_URL[networkName]);
|
|
42
|
-
const onchainP2TR = (0,
|
|
42
|
+
const onchainP2TR = (0, btc_signer_1.p2tr)(pubkey, undefined, network);
|
|
43
43
|
return new OnchainWallet(identity, network, onchainP2TR, onchainProvider);
|
|
44
44
|
}
|
|
45
45
|
get address() {
|
|
@@ -80,7 +80,7 @@ class OnchainWallet {
|
|
|
80
80
|
// Select coins
|
|
81
81
|
const selected = selectCoins(coins, totalNeeded);
|
|
82
82
|
// Create transaction
|
|
83
|
-
let tx = new
|
|
83
|
+
let tx = new transaction_1.Transaction();
|
|
84
84
|
// Add inputs
|
|
85
85
|
for (const input of selected.inputs) {
|
|
86
86
|
tx.addInput({
|
|
@@ -108,10 +108,9 @@ class OnchainWallet {
|
|
|
108
108
|
}
|
|
109
109
|
async bumpP2A(parent) {
|
|
110
110
|
const parentVsize = parent.vsize;
|
|
111
|
-
let child = new
|
|
112
|
-
allowUnknownInputs: true,
|
|
113
|
-
allowLegacyWitnessUtxo: true,
|
|
111
|
+
let child = new transaction_1.Transaction({
|
|
114
112
|
version: 3,
|
|
113
|
+
allowLegacyWitnessUtxo: true,
|
|
115
114
|
});
|
|
116
115
|
child.addInput((0, anchor_1.findP2AOutput)(parent)); // throws if not found
|
|
117
116
|
const childVsize = txSizeEstimator_1.TxWeightEstimator.create()
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Response = void 0;
|
|
4
|
+
const base_1 = require("@scure/base");
|
|
5
|
+
function getRandomId() {
|
|
6
|
+
const randomValue = crypto.getRandomValues(new Uint8Array(16));
|
|
7
|
+
return base_1.hex.encode(randomValue);
|
|
8
|
+
}
|
|
4
9
|
/**
|
|
5
10
|
* Response is the namespace that contains the response types for the service worker.
|
|
6
11
|
*/
|
|
@@ -187,4 +192,31 @@ var Response;
|
|
|
187
192
|
};
|
|
188
193
|
}
|
|
189
194
|
Response.walletReloaded = walletReloaded;
|
|
195
|
+
function isVtxoUpdate(response) {
|
|
196
|
+
return response.type === "VTXO_UPDATE";
|
|
197
|
+
}
|
|
198
|
+
Response.isVtxoUpdate = isVtxoUpdate;
|
|
199
|
+
function vtxoUpdate(newVtxos, spentVtxos) {
|
|
200
|
+
return {
|
|
201
|
+
type: "VTXO_UPDATE",
|
|
202
|
+
id: getRandomId(), // spontaneous update, not tied to a request
|
|
203
|
+
success: true,
|
|
204
|
+
spentVtxos,
|
|
205
|
+
newVtxos,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
Response.vtxoUpdate = vtxoUpdate;
|
|
209
|
+
function isUtxoUpdate(response) {
|
|
210
|
+
return response.type === "UTXO_UPDATE";
|
|
211
|
+
}
|
|
212
|
+
Response.isUtxoUpdate = isUtxoUpdate;
|
|
213
|
+
function utxoUpdate(coins) {
|
|
214
|
+
return {
|
|
215
|
+
type: "UTXO_UPDATE",
|
|
216
|
+
id: getRandomId(), // spontaneous update, not tied to a request
|
|
217
|
+
success: true,
|
|
218
|
+
coins,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
Response.utxoUpdate = utxoUpdate;
|
|
190
222
|
})(Response || (exports.Response = Response = {}));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_DB_NAME = void 0;
|
|
3
4
|
exports.setupServiceWorker = setupServiceWorker;
|
|
4
|
-
exports.
|
|
5
|
+
exports.DEFAULT_DB_NAME = "arkade-service-worker";
|
|
5
6
|
/**
|
|
6
7
|
* setupServiceWorker sets up the service worker.
|
|
7
8
|
* @param path - the path to the service worker script
|
|
@@ -48,11 +49,3 @@ async function setupServiceWorker(path) {
|
|
|
48
49
|
navigator.serviceWorker.addEventListener("error", onError);
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
|
-
function extendVirtualCoin(wallet, vtxo) {
|
|
52
|
-
return {
|
|
53
|
-
...vtxo,
|
|
54
|
-
forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
|
|
55
|
-
intentTapLeafScript: wallet.offchainTapscript.exit(),
|
|
56
|
-
tapTree: wallet.offchainTapscript.encode(),
|
|
57
|
-
};
|
|
58
|
-
}
|
|
@@ -25,7 +25,7 @@ class ServiceWorkerWallet {
|
|
|
25
25
|
}
|
|
26
26
|
static async create(options) {
|
|
27
27
|
// Default to IndexedDB for service worker context
|
|
28
|
-
const storage = options.
|
|
28
|
+
const storage = new indexedDB_1.IndexedDBStorageAdapter(options.dbName || utils_1.DEFAULT_DB_NAME, options.dbVersion);
|
|
29
29
|
// Create repositories
|
|
30
30
|
const walletRepo = new walletRepository_1.WalletRepositoryImpl(storage);
|
|
31
31
|
const contractRepo = new contractRepository_1.ContractRepositoryImpl(storage);
|
|
@@ -34,7 +34,7 @@ class ServiceWorkerWallet {
|
|
|
34
34
|
? options.identity
|
|
35
35
|
: null;
|
|
36
36
|
if (!identity) {
|
|
37
|
-
throw new Error("ServiceWorkerWallet.create() requires a Identity that can expose
|
|
37
|
+
throw new Error("ServiceWorkerWallet.create() requires a Identity that can expose a single private key");
|
|
38
38
|
}
|
|
39
39
|
// Extract private key for service worker initialization
|
|
40
40
|
const privateKey = identity.toHex();
|
|
@@ -77,13 +77,9 @@ class ServiceWorkerWallet {
|
|
|
77
77
|
// Register and setup the service worker
|
|
78
78
|
const serviceWorker = await (0, utils_1.setupServiceWorker)(options.serviceWorkerPath);
|
|
79
79
|
// Use the existing create method
|
|
80
|
-
return
|
|
81
|
-
|
|
82
|
-
arkServerUrl: options.arkServerUrl,
|
|
83
|
-
esploraUrl: options.esploraUrl,
|
|
84
|
-
identity: options.identity,
|
|
80
|
+
return ServiceWorkerWallet.create({
|
|
81
|
+
...options,
|
|
85
82
|
serviceWorker,
|
|
86
|
-
storage: options.storage,
|
|
87
83
|
});
|
|
88
84
|
}
|
|
89
85
|
// send a message and wait for a response
|
|
@@ -260,6 +256,9 @@ class ServiceWorkerWallet {
|
|
|
260
256
|
return new Promise((resolve, reject) => {
|
|
261
257
|
const messageHandler = (event) => {
|
|
262
258
|
const response = event.data;
|
|
259
|
+
if (response.id !== message.id) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
263
262
|
if (!response.success) {
|
|
264
263
|
navigator.serviceWorker.removeEventListener("message", messageHandler);
|
|
265
264
|
reject(new Error(response.message));
|
|
@@ -13,15 +13,18 @@ const indexer_1 = require("../../providers/indexer");
|
|
|
13
13
|
const base_1 = require("@scure/base");
|
|
14
14
|
const indexedDB_1 = require("../../storage/indexedDB");
|
|
15
15
|
const walletRepository_1 = require("../../repositories/walletRepository");
|
|
16
|
-
const utils_1 = require("
|
|
16
|
+
const utils_1 = require("../utils");
|
|
17
|
+
const utils_2 = require("./utils");
|
|
17
18
|
/**
|
|
18
19
|
* Worker is a class letting to interact with ServiceWorkerWallet from the client
|
|
19
20
|
* it aims to be run in a service worker context
|
|
20
21
|
*/
|
|
21
22
|
class Worker {
|
|
22
|
-
constructor(messageCallback = () => { }) {
|
|
23
|
+
constructor(dbName = utils_2.DEFAULT_DB_NAME, dbVersion = 1, messageCallback = () => { }) {
|
|
24
|
+
this.dbName = dbName;
|
|
25
|
+
this.dbVersion = dbVersion;
|
|
23
26
|
this.messageCallback = messageCallback;
|
|
24
|
-
this.storage = new indexedDB_1.IndexedDBStorageAdapter(
|
|
27
|
+
this.storage = new indexedDB_1.IndexedDBStorageAdapter(dbName, dbVersion);
|
|
25
28
|
this.walletRepository = new walletRepository_1.WalletRepositoryImpl(this.storage);
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
@@ -57,6 +60,15 @@ class Worker {
|
|
|
57
60
|
spent: allVtxos.filter((vtxo) => !(0, __1.isSpendable)(vtxo)),
|
|
58
61
|
};
|
|
59
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Get all boarding utxos from wallet repository
|
|
65
|
+
*/
|
|
66
|
+
async getAllBoardingUtxos() {
|
|
67
|
+
if (!this.wallet)
|
|
68
|
+
return [];
|
|
69
|
+
const address = await this.wallet.getBoardingAddress();
|
|
70
|
+
return await this.walletRepository.getUtxos(address);
|
|
71
|
+
}
|
|
60
72
|
async start(withServiceWorkerUpdate = true) {
|
|
61
73
|
self.addEventListener("message", async (event) => {
|
|
62
74
|
await this.handleMessage(event);
|
|
@@ -77,6 +89,8 @@ class Worker {
|
|
|
77
89
|
this.incomingFundsSubscription();
|
|
78
90
|
// Clear storage - this replaces vtxoRepository.close()
|
|
79
91
|
await this.storage.clear();
|
|
92
|
+
// Reset in-memory caches by recreating the repository
|
|
93
|
+
this.walletRepository = new walletRepository_1.WalletRepositoryImpl(this.storage);
|
|
80
94
|
this.wallet = undefined;
|
|
81
95
|
this.arkProvider = undefined;
|
|
82
96
|
this.indexerProvider = undefined;
|
|
@@ -105,7 +119,7 @@ class Worker {
|
|
|
105
119
|
const txs = await this.wallet.getTransactionHistory();
|
|
106
120
|
if (txs)
|
|
107
121
|
await this.walletRepository.saveTransactions(address, txs);
|
|
108
|
-
//
|
|
122
|
+
// unsubscribe previous subscription if any
|
|
109
123
|
if (this.incomingFundsSubscription)
|
|
110
124
|
this.incomingFundsSubscription();
|
|
111
125
|
// subscribe for incoming funds and notify all clients when new funds arrive
|
|
@@ -125,11 +139,19 @@ class Worker {
|
|
|
125
139
|
...spentVtxos,
|
|
126
140
|
]);
|
|
127
141
|
// notify all clients about the vtxo update
|
|
128
|
-
this.sendMessageToAllClients(
|
|
142
|
+
this.sendMessageToAllClients(response_1.Response.vtxoUpdate(newVtxos, spentVtxos));
|
|
129
143
|
}
|
|
130
|
-
if (funds.type === "utxo"
|
|
144
|
+
if (funds.type === "utxo") {
|
|
145
|
+
const newUtxos = funds.coins.map((utxo) => (0, utils_1.extendCoin)(this.wallet, utxo));
|
|
146
|
+
if (newUtxos.length === 0) {
|
|
147
|
+
this.sendMessageToAllClients(response_1.Response.utxoUpdate([]));
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const boardingAddress = await this.wallet?.getBoardingAddress();
|
|
151
|
+
// save utxos using unified repository
|
|
152
|
+
await this.walletRepository.saveUtxos(boardingAddress, newUtxos);
|
|
131
153
|
// notify all clients about the utxo update
|
|
132
|
-
this.sendMessageToAllClients(
|
|
154
|
+
this.sendMessageToAllClients(response_1.Response.utxoUpdate(funds.coins));
|
|
133
155
|
}
|
|
134
156
|
});
|
|
135
157
|
}
|
|
@@ -286,7 +308,7 @@ class Worker {
|
|
|
286
308
|
}
|
|
287
309
|
try {
|
|
288
310
|
const [boardingUtxos, spendableVtxos, sweptVtxos] = await Promise.all([
|
|
289
|
-
this.
|
|
311
|
+
this.getAllBoardingUtxos(),
|
|
290
312
|
this.getSpendableVtxos(),
|
|
291
313
|
this.getSweptVtxos(),
|
|
292
314
|
]);
|
|
@@ -354,23 +376,21 @@ class Worker {
|
|
|
354
376
|
return;
|
|
355
377
|
}
|
|
356
378
|
try {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
event.source?.postMessage(response_1.Response.vtxos(message.id, vtxos));
|
|
379
|
+
const vtxos = await this.getSpendableVtxos();
|
|
380
|
+
const dustAmount = this.wallet.dustAmount;
|
|
381
|
+
const includeRecoverable = message.filter?.withRecoverable ?? false;
|
|
382
|
+
const filteredVtxos = includeRecoverable
|
|
383
|
+
? vtxos
|
|
384
|
+
: vtxos.filter((v) => {
|
|
385
|
+
if (dustAmount != null && (0, __1.isSubdust)(v, dustAmount)) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
if ((0, __1.isRecoverable)(v)) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
return true;
|
|
392
|
+
});
|
|
393
|
+
event.source?.postMessage(response_1.Response.vtxos(message.id, filteredVtxos));
|
|
374
394
|
}
|
|
375
395
|
catch (error) {
|
|
376
396
|
console.error("Error getting vtxos:", error);
|
|
@@ -393,7 +413,7 @@ class Worker {
|
|
|
393
413
|
return;
|
|
394
414
|
}
|
|
395
415
|
try {
|
|
396
|
-
const boardingUtxos = await this.
|
|
416
|
+
const boardingUtxos = await this.getAllBoardingUtxos();
|
|
397
417
|
event.source?.postMessage(response_1.Response.boardingUtxos(message.id, boardingUtxos));
|
|
398
418
|
}
|
|
399
419
|
catch (error) {
|
|
@@ -515,21 +535,17 @@ class Worker {
|
|
|
515
535
|
event.source?.postMessage(response_1.Response.error(message.id, "Unknown message type"));
|
|
516
536
|
}
|
|
517
537
|
}
|
|
518
|
-
async sendMessageToAllClients(
|
|
538
|
+
async sendMessageToAllClients(message) {
|
|
519
539
|
self.clients
|
|
520
540
|
.matchAll({ includeUncontrolled: true, type: "window" })
|
|
521
541
|
.then((clients) => {
|
|
522
542
|
clients.forEach((client) => {
|
|
523
|
-
client.postMessage(
|
|
524
|
-
type,
|
|
525
|
-
message,
|
|
526
|
-
});
|
|
543
|
+
client.postMessage(message);
|
|
527
544
|
});
|
|
528
545
|
});
|
|
529
546
|
}
|
|
530
547
|
async handleReloadWallet(event) {
|
|
531
548
|
const message = event.data;
|
|
532
|
-
console.log("RELOAD_WALLET message received", message);
|
|
533
549
|
if (!request_1.Request.isReloadWallet(message)) {
|
|
534
550
|
console.error("Invalid RELOAD_WALLET message format", message);
|
|
535
551
|
event.source?.postMessage(response_1.Response.error(message.id, "Invalid RELOAD_WALLET message format"));
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Unroll = void 0;
|
|
4
|
-
const transaction_js_1 = require("@scure/btc-signer/transaction.js");
|
|
5
|
-
const indexer_1 = require("../providers/indexer");
|
|
6
4
|
const base_1 = require("@scure/base");
|
|
5
|
+
const btc_signer_1 = require("@scure/btc-signer");
|
|
6
|
+
const indexer_1 = require("../providers/indexer");
|
|
7
7
|
const base_2 = require("../script/base");
|
|
8
|
-
const psbt_js_1 = require("@scure/btc-signer/psbt.js");
|
|
9
8
|
const txSizeEstimator_1 = require("../utils/txSizeEstimator");
|
|
10
9
|
const wallet_1 = require("./wallet");
|
|
10
|
+
const transaction_1 = require("../utils/transaction");
|
|
11
11
|
var Unroll;
|
|
12
12
|
(function (Unroll) {
|
|
13
13
|
let StepType;
|
|
@@ -106,9 +106,7 @@ var Unroll;
|
|
|
106
106
|
if (virtualTxs.txs.length === 0) {
|
|
107
107
|
throw new Error(`Tx ${nextTxToBroadcast.txid} not found`);
|
|
108
108
|
}
|
|
109
|
-
const tx =
|
|
110
|
-
allowUnknownInputs: true,
|
|
111
|
-
});
|
|
109
|
+
const tx = transaction_1.Transaction.fromPSBT(base_1.base64.decode(virtualTxs.txs[0]));
|
|
112
110
|
// finalize the tree transaction
|
|
113
111
|
if (nextTxToBroadcast.type === indexer_1.ChainTxType.TREE) {
|
|
114
112
|
const input = tx.getInput(0);
|
|
@@ -197,11 +195,11 @@ var Unroll;
|
|
|
197
195
|
amount: BigInt(vtxo.value),
|
|
198
196
|
script: base_2.VtxoScript.decode(vtxo.tapTree).pkScript,
|
|
199
197
|
},
|
|
200
|
-
sighashType:
|
|
198
|
+
sighashType: btc_signer_1.SigHash.DEFAULT,
|
|
201
199
|
});
|
|
202
|
-
txWeightEstimator.addTapscriptInput(64, spendingLeaf[1].length,
|
|
200
|
+
txWeightEstimator.addTapscriptInput(64, spendingLeaf[1].length, btc_signer_1.TaprootControlBlock.encode(spendingLeaf[0]).length);
|
|
203
201
|
}
|
|
204
|
-
const tx = new
|
|
202
|
+
const tx = new transaction_1.Transaction({ version: 2 });
|
|
205
203
|
for (const input of inputs) {
|
|
206
204
|
tx.addInput(input);
|
|
207
205
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extendVirtualCoin = extendVirtualCoin;
|
|
4
|
+
exports.extendCoin = extendCoin;
|
|
5
|
+
function extendVirtualCoin(wallet, vtxo) {
|
|
6
|
+
return {
|
|
7
|
+
...vtxo,
|
|
8
|
+
forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
|
|
9
|
+
intentTapLeafScript: wallet.offchainTapscript.exit(),
|
|
10
|
+
tapTree: wallet.offchainTapscript.encode(),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function extendCoin(wallet, utxo) {
|
|
14
|
+
return {
|
|
15
|
+
...utxo,
|
|
16
|
+
forfeitTapLeafScript: wallet.boardingTapscript.forfeit(),
|
|
17
|
+
intentTapLeafScript: wallet.boardingTapscript.exit(),
|
|
18
|
+
tapTree: wallet.boardingTapscript.encode(),
|
|
19
|
+
};
|
|
20
|
+
}
|