@arkade-os/sdk 0.2.3 → 0.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -43
- package/dist/cjs/adapters/asyncStorage.js +5 -0
- package/dist/cjs/adapters/fileSystem.js +5 -0
- package/dist/cjs/adapters/indexedDB.js +5 -0
- package/dist/cjs/adapters/localStorage.js +5 -0
- package/dist/cjs/arknote/index.js +4 -4
- package/dist/cjs/bip322/index.js +11 -9
- package/dist/cjs/forfeit.js +2 -2
- package/dist/cjs/identity/index.js +15 -0
- package/dist/cjs/identity/singleKey.js +24 -5
- package/dist/cjs/index.js +7 -5
- package/dist/cjs/musig2/keys.js +7 -7
- package/dist/cjs/musig2/nonces.js +1 -1
- package/dist/cjs/musig2/sign.js +6 -6
- package/dist/cjs/networks.js +6 -6
- package/dist/cjs/repositories/contractRepository.js +130 -0
- package/dist/cjs/repositories/index.js +18 -0
- package/dist/cjs/repositories/walletRepository.js +136 -0
- package/dist/cjs/script/address.js +3 -3
- package/dist/cjs/script/base.js +7 -7
- package/dist/cjs/script/tapscript.js +21 -21
- package/dist/cjs/script/vhtlc.js +2 -2
- package/dist/cjs/storage/asyncStorage.js +47 -0
- package/dist/cjs/storage/fileSystem.js +138 -0
- package/dist/cjs/storage/inMemory.js +21 -0
- package/dist/cjs/storage/indexedDB.js +97 -0
- package/dist/cjs/storage/localStorage.js +48 -0
- package/dist/cjs/tree/signingSession.js +12 -11
- package/dist/cjs/tree/txTree.js +6 -6
- package/dist/cjs/tree/validation.js +5 -5
- package/dist/cjs/utils/arkTransaction.js +5 -5
- package/dist/cjs/utils/unknownFields.js +5 -5
- package/dist/cjs/wallet/onchain.js +16 -10
- package/dist/cjs/wallet/serviceWorker/request.js +4 -14
- package/dist/cjs/wallet/serviceWorker/response.js +0 -13
- package/dist/cjs/wallet/serviceWorker/wallet.js +124 -130
- package/dist/cjs/wallet/serviceWorker/worker.js +84 -53
- package/dist/cjs/wallet/unroll.js +6 -6
- package/dist/cjs/wallet/wallet.js +37 -20
- package/dist/esm/adapters/asyncStorage.js +1 -0
- package/dist/esm/adapters/fileSystem.js +1 -0
- package/dist/esm/adapters/indexedDB.js +1 -0
- package/dist/esm/adapters/localStorage.js +1 -0
- package/dist/esm/arknote/index.js +2 -2
- package/dist/esm/bip322/index.js +4 -2
- package/dist/esm/forfeit.js +1 -1
- package/dist/esm/identity/index.js +1 -1
- package/dist/esm/identity/singleKey.js +22 -3
- package/dist/esm/index.js +5 -4
- package/dist/esm/musig2/keys.js +7 -7
- package/dist/esm/musig2/nonces.js +1 -1
- package/dist/esm/musig2/sign.js +5 -5
- package/dist/esm/networks.js +1 -1
- package/dist/esm/repositories/contractRepository.js +126 -0
- package/dist/esm/repositories/index.js +2 -0
- package/dist/esm/repositories/walletRepository.js +132 -0
- package/dist/esm/script/address.js +1 -1
- package/dist/esm/script/base.js +3 -3
- package/dist/esm/script/tapscript.js +2 -2
- package/dist/esm/script/vhtlc.js +1 -1
- package/dist/esm/storage/asyncStorage.js +43 -0
- package/dist/esm/storage/fileSystem.js +101 -0
- package/dist/esm/storage/inMemory.js +17 -0
- package/dist/esm/storage/indexedDB.js +93 -0
- package/dist/esm/storage/localStorage.js +44 -0
- package/dist/esm/tree/signingSession.js +4 -3
- package/dist/esm/tree/txTree.js +2 -2
- package/dist/esm/tree/validation.js +2 -2
- package/dist/esm/utils/arkTransaction.js +2 -2
- package/dist/esm/utils/unknownFields.js +1 -1
- package/dist/esm/wallet/onchain.js +14 -8
- package/dist/esm/wallet/serviceWorker/request.js +4 -14
- package/dist/esm/wallet/serviceWorker/response.js +0 -13
- package/dist/esm/wallet/serviceWorker/wallet.js +125 -131
- package/dist/esm/wallet/serviceWorker/worker.js +85 -54
- package/dist/esm/wallet/unroll.js +2 -2
- package/dist/esm/wallet/wallet.js +25 -8
- package/dist/types/adapters/asyncStorage.d.ts +2 -0
- package/dist/types/adapters/fileSystem.d.ts +2 -0
- package/dist/types/adapters/indexedDB.d.ts +2 -0
- package/dist/types/adapters/localStorage.d.ts +2 -0
- package/dist/types/arknote/index.d.ts +1 -1
- package/dist/types/bip322/index.d.ts +2 -2
- package/dist/types/forfeit.d.ts +1 -1
- package/dist/types/identity/index.d.ts +4 -2
- package/dist/types/identity/singleKey.d.ts +13 -2
- package/dist/types/index.d.ts +5 -5
- package/dist/types/repositories/contractRepository.d.ts +20 -0
- package/dist/types/repositories/index.d.ts +2 -0
- package/dist/types/repositories/walletRepository.d.ts +38 -0
- package/dist/types/script/address.d.ts +1 -1
- package/dist/types/script/base.d.ts +1 -1
- package/dist/types/script/default.d.ts +1 -1
- package/dist/types/script/tapscript.d.ts +1 -1
- package/dist/types/script/vhtlc.d.ts +1 -1
- package/dist/types/storage/asyncStorage.d.ts +9 -0
- package/dist/types/storage/fileSystem.d.ts +11 -0
- package/dist/types/storage/inMemory.d.ts +8 -0
- package/dist/types/storage/index.d.ts +6 -0
- package/dist/types/storage/indexedDB.d.ts +12 -0
- package/dist/types/storage/localStorage.d.ts +8 -0
- package/dist/types/tree/txTree.d.ts +1 -1
- package/dist/types/tree/validation.d.ts +1 -1
- package/dist/types/utils/anchor.d.ts +1 -1
- package/dist/types/utils/arkTransaction.d.ts +3 -3
- package/dist/types/utils/unknownFields.d.ts +1 -1
- package/dist/types/wallet/index.d.ts +4 -1
- package/dist/types/wallet/onchain.d.ts +5 -4
- package/dist/types/wallet/serviceWorker/request.d.ts +1 -7
- package/dist/types/wallet/serviceWorker/response.d.ts +1 -8
- package/dist/types/wallet/serviceWorker/wallet.d.ts +67 -21
- package/dist/types/wallet/serviceWorker/worker.d.ts +16 -4
- package/dist/types/wallet/unroll.d.ts +1 -1
- package/dist/types/wallet/wallet.d.ts +5 -1
- package/package.json +39 -15
- package/dist/cjs/wallet/serviceWorker/db/vtxo/idb.js +0 -185
- package/dist/esm/wallet/serviceWorker/db/vtxo/idb.js +0 -181
- package/dist/types/wallet/serviceWorker/db/vtxo/idb.d.ts +0 -20
- package/dist/types/wallet/serviceWorker/db/vtxo/index.d.ts +0 -14
- /package/dist/cjs/{wallet/serviceWorker/db/vtxo → storage}/index.js +0 -0
- /package/dist/esm/{wallet/serviceWorker/db/vtxo → storage}/index.js +0 -0
|
@@ -37,9 +37,9 @@ exports.Wallet = void 0;
|
|
|
37
37
|
exports.waitForIncomingFunds = waitForIncomingFunds;
|
|
38
38
|
const base_1 = require("@scure/base");
|
|
39
39
|
const bip68 = __importStar(require("bip68"));
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
40
|
+
const payment_js_1 = require("@scure/btc-signer/payment.js");
|
|
41
|
+
const transaction_js_1 = require("@scure/btc-signer/transaction.js");
|
|
42
|
+
const psbt_js_1 = require("@scure/btc-signer/psbt.js");
|
|
43
43
|
const transactionHistory_1 = require("../utils/transactionHistory");
|
|
44
44
|
const address_1 = require("../script/address");
|
|
45
45
|
const default_1 = require("../script/default");
|
|
@@ -49,7 +49,7 @@ const ark_1 = require("../providers/ark");
|
|
|
49
49
|
const forfeit_1 = require("../forfeit");
|
|
50
50
|
const validation_1 = require("../tree/validation");
|
|
51
51
|
const _1 = require(".");
|
|
52
|
-
const
|
|
52
|
+
const utils_js_1 = require("@scure/btc-signer/utils.js");
|
|
53
53
|
const base_2 = require("../script/base");
|
|
54
54
|
const tapscript_1 = require("../script/tapscript");
|
|
55
55
|
const arkTransaction_1 = require("../utils/arkTransaction");
|
|
@@ -57,6 +57,9 @@ const arknote_1 = require("../arknote");
|
|
|
57
57
|
const bip322_1 = require("../bip322");
|
|
58
58
|
const indexer_1 = require("../providers/indexer");
|
|
59
59
|
const txTree_1 = require("../tree/txTree");
|
|
60
|
+
const inMemory_1 = require("../storage/inMemory");
|
|
61
|
+
const walletRepository_1 = require("../repositories/walletRepository");
|
|
62
|
+
const contractRepository_1 = require("../repositories/contractRepository");
|
|
60
63
|
/**
|
|
61
64
|
* Main wallet implementation for Bitcoin transactions with Ark protocol support.
|
|
62
65
|
* The wallet does not store any data locally and relies on Ark and onchain
|
|
@@ -83,7 +86,7 @@ const txTree_1 = require("../tree/txTree");
|
|
|
83
86
|
* ```
|
|
84
87
|
*/
|
|
85
88
|
class Wallet {
|
|
86
|
-
constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, dustAmount) {
|
|
89
|
+
constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, dustAmount, walletRepository, contractRepository) {
|
|
87
90
|
this.identity = identity;
|
|
88
91
|
this.network = network;
|
|
89
92
|
this.networkName = networkName;
|
|
@@ -96,9 +99,11 @@ class Wallet {
|
|
|
96
99
|
this.serverUnrollScript = serverUnrollScript;
|
|
97
100
|
this.forfeitOutputScript = forfeitOutputScript;
|
|
98
101
|
this.dustAmount = dustAmount;
|
|
102
|
+
this.walletRepository = walletRepository;
|
|
103
|
+
this.contractRepository = contractRepository;
|
|
99
104
|
}
|
|
100
105
|
static async create(config) {
|
|
101
|
-
const pubkey = config.identity.xOnlyPublicKey();
|
|
106
|
+
const pubkey = await config.identity.xOnlyPublicKey();
|
|
102
107
|
if (!pubkey) {
|
|
103
108
|
throw new Error("Invalid configured public key");
|
|
104
109
|
}
|
|
@@ -136,9 +141,13 @@ class Wallet {
|
|
|
136
141
|
});
|
|
137
142
|
// parse the server forfeit address
|
|
138
143
|
// server is expecting funds to be sent to this address
|
|
139
|
-
const forfeitAddress = (0,
|
|
140
|
-
const forfeitOutputScript =
|
|
141
|
-
|
|
144
|
+
const forfeitAddress = (0, payment_js_1.Address)(network).decode(info.forfeitAddress);
|
|
145
|
+
const forfeitOutputScript = payment_js_1.OutScript.encode(forfeitAddress);
|
|
146
|
+
// Set up storage and repositories
|
|
147
|
+
const storage = config.storage || new inMemory_1.InMemoryStorageAdapter();
|
|
148
|
+
const walletRepository = new walletRepository_1.WalletRepositoryImpl(storage);
|
|
149
|
+
const contractRepository = new contractRepository_1.ContractRepositoryImpl(storage);
|
|
150
|
+
return new Wallet(config.identity, network, info.network, onchainProvider, arkProvider, indexerProvider, serverPubKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, info.dust, walletRepository, contractRepository);
|
|
142
151
|
}
|
|
143
152
|
get arkAddress() {
|
|
144
153
|
return this.offchainTapscript.address(this.network.hrp, this.arkServerPublicKey);
|
|
@@ -194,16 +203,24 @@ class Wallet {
|
|
|
194
203
|
};
|
|
195
204
|
}
|
|
196
205
|
async getVtxos(filter) {
|
|
206
|
+
const address = await this.getAddress();
|
|
207
|
+
// Try to get from cache first
|
|
208
|
+
const cachedVtxos = await this.walletRepository.getVtxos(address);
|
|
209
|
+
// For now, always fetch fresh data from provider and update cache
|
|
210
|
+
// In future, we can add cache invalidation logic based on timestamps
|
|
197
211
|
const spendableVtxos = await this.getVirtualCoins(filter);
|
|
198
212
|
const encodedOffchainTapscript = this.offchainTapscript.encode();
|
|
199
213
|
const forfeit = this.offchainTapscript.forfeit();
|
|
200
214
|
const exit = this.offchainTapscript.exit();
|
|
201
|
-
|
|
215
|
+
const extendedVtxos = spendableVtxos.map((vtxo) => ({
|
|
202
216
|
...vtxo,
|
|
203
217
|
forfeitTapLeafScript: forfeit,
|
|
204
218
|
intentTapLeafScript: exit,
|
|
205
219
|
tapTree: encodedOffchainTapscript,
|
|
206
220
|
}));
|
|
221
|
+
// Update cache with fresh data
|
|
222
|
+
await this.walletRepository.saveVtxos(address, extendedVtxos);
|
|
223
|
+
return extendedVtxos;
|
|
207
224
|
}
|
|
208
225
|
async getVirtualCoins(filter = { withRecoverable: true, withUnrolled: false }) {
|
|
209
226
|
const scripts = [base_1.hex.encode(this.offchainTapscript.pkScript)];
|
|
@@ -385,7 +402,7 @@ class Wallet {
|
|
|
385
402
|
// TODO persist final virtual tx and checkpoints to repository
|
|
386
403
|
// sign the checkpoints
|
|
387
404
|
const finalCheckpoints = await Promise.all(signedCheckpointTxs.map(async (c) => {
|
|
388
|
-
const tx =
|
|
405
|
+
const tx = transaction_js_1.Transaction.fromPSBT(base_1.base64.decode(c));
|
|
389
406
|
const signedCheckpoint = await this.identity.sign(tx);
|
|
390
407
|
return base_1.base64.encode(signedCheckpoint.toPSBT());
|
|
391
408
|
}));
|
|
@@ -441,8 +458,8 @@ class Wallet {
|
|
|
441
458
|
}
|
|
442
459
|
catch {
|
|
443
460
|
// onchain
|
|
444
|
-
const addr = (0,
|
|
445
|
-
script =
|
|
461
|
+
const addr = (0, payment_js_1.Address)(this.network).decode(output.address);
|
|
462
|
+
script = payment_js_1.OutScript.encode(addr);
|
|
446
463
|
onchainOutputIndexes.push(index);
|
|
447
464
|
}
|
|
448
465
|
outputs.push({
|
|
@@ -680,7 +697,7 @@ class Wallet {
|
|
|
680
697
|
}
|
|
681
698
|
async handleBatchStartedEvent(event, intentId, serverPubKey, forfeitOutputScript) {
|
|
682
699
|
const utf8IntentId = new TextEncoder().encode(intentId);
|
|
683
|
-
const intentIdHash = (0,
|
|
700
|
+
const intentIdHash = (0, utils_js_1.sha256)(utf8IntentId);
|
|
684
701
|
const intentIdHashStr = base_1.hex.encode(new Uint8Array(intentIdHash));
|
|
685
702
|
let skip = true;
|
|
686
703
|
// check if our intent ID hash matches any in the event
|
|
@@ -703,7 +720,7 @@ class Wallet {
|
|
|
703
720
|
},
|
|
704
721
|
pubkeys: [serverPubKey],
|
|
705
722
|
}).script;
|
|
706
|
-
const sweepTapTreeRoot = (0,
|
|
723
|
+
const sweepTapTreeRoot = (0, payment_js_1.tapLeafHash)(sweepTapscript);
|
|
707
724
|
return {
|
|
708
725
|
roundId: event.id,
|
|
709
726
|
sweepTapTreeRoot,
|
|
@@ -714,7 +731,7 @@ class Wallet {
|
|
|
714
731
|
// validates the vtxo tree, creates a signing session and generates the musig2 nonces
|
|
715
732
|
async handleSettlementSigningEvent(event, sweepTapTreeRoot, session, vtxoGraph) {
|
|
716
733
|
// validate the unsigned vtxo tree
|
|
717
|
-
const commitmentTx =
|
|
734
|
+
const commitmentTx = transaction_js_1.Transaction.fromPSBT(base_1.base64.decode(event.unsignedCommitmentTx));
|
|
718
735
|
(0, validation_1.validateVtxoTxGraph)(vtxoGraph, commitmentTx, sweepTapTreeRoot);
|
|
719
736
|
// TODO check if our registered outputs are in the vtxo tree
|
|
720
737
|
const sharedOutput = commitmentTx.getOutput(0);
|
|
@@ -733,7 +750,7 @@ class Wallet {
|
|
|
733
750
|
// the signed forfeits transactions to submit
|
|
734
751
|
const signedForfeits = [];
|
|
735
752
|
const vtxos = await this.getVirtualCoins();
|
|
736
|
-
let settlementPsbt =
|
|
753
|
+
let settlementPsbt = transaction_js_1.Transaction.fromPSBT(base_1.base64.decode(event.commitmentTx));
|
|
737
754
|
let hasBoardingUtxos = false;
|
|
738
755
|
let connectorIndex = 0;
|
|
739
756
|
const connectorsLeaves = connectorsGraph?.leaves() || [];
|
|
@@ -775,7 +792,7 @@ class Wallet {
|
|
|
775
792
|
throw new Error("not enough connectors received");
|
|
776
793
|
}
|
|
777
794
|
const connectorLeaf = connectorsLeaves[connectorIndex];
|
|
778
|
-
const connectorTxId = base_1.hex.encode((0,
|
|
795
|
+
const connectorTxId = base_1.hex.encode((0, utils_js_1.sha256x2)(connectorLeaf.toBytes(true)).reverse());
|
|
779
796
|
const connectorOutput = connectorLeaf.getOutput(0);
|
|
780
797
|
if (!connectorOutput) {
|
|
781
798
|
throw new Error("connector output not found");
|
|
@@ -794,7 +811,7 @@ class Wallet {
|
|
|
794
811
|
amount: BigInt(vtxo.value),
|
|
795
812
|
script: base_2.VtxoScript.decode(input.tapTree).pkScript,
|
|
796
813
|
},
|
|
797
|
-
sighashType:
|
|
814
|
+
sighashType: transaction_js_1.SigHash.DEFAULT,
|
|
798
815
|
tapLeafScript: [input.forfeitTapLeafScript],
|
|
799
816
|
},
|
|
800
817
|
{
|
|
@@ -900,7 +917,7 @@ function finalizeWithExtraWitnesses(inputExtraWitnesses) {
|
|
|
900
917
|
tx.updateInput(i, {
|
|
901
918
|
finalScriptWitness: [
|
|
902
919
|
script,
|
|
903
|
-
|
|
920
|
+
psbt_js_1.TaprootControlBlock.encode(cb),
|
|
904
921
|
],
|
|
905
922
|
});
|
|
906
923
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AsyncStorageAdapter } from '../storage/asyncStorage.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FileSystemStorageAdapter } from '../storage/fileSystem.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IndexedDBStorageAdapter } from '../storage/indexedDB.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LocalStorageAdapter } from '../storage/localStorage.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { base58, hex } from "@scure/base";
|
|
2
2
|
import { VtxoScript } from '../script/base.js';
|
|
3
|
-
import { sha256 } from "@scure/btc-signer/utils";
|
|
4
|
-
import { Script } from "@scure/btc-signer";
|
|
3
|
+
import { sha256 } from "@scure/btc-signer/utils.js";
|
|
4
|
+
import { Script } from "@scure/btc-signer/script.js";
|
|
5
5
|
/**
|
|
6
6
|
* ArkNotes are special virtual coins in the Ark protocol that can be created
|
|
7
7
|
* and spent without requiring any transactions. The server mints them, and they
|
package/dist/esm/bip322/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { OP
|
|
1
|
+
import { OP } from "@scure/btc-signer/script.js";
|
|
2
|
+
import { Transaction, SigHash } from "@scure/btc-signer/transaction.js";
|
|
3
|
+
import { Script } from "@scure/btc-signer/script.js";
|
|
2
4
|
import { ErrMissingData, ErrMissingInputs, ErrMissingWitnessUtxo, } from './errors.js';
|
|
3
|
-
import { schnorr } from "@noble/curves/secp256k1";
|
|
5
|
+
import { schnorr } from "@noble/curves/secp256k1.js";
|
|
4
6
|
import { base64 } from "@scure/base";
|
|
5
7
|
/**
|
|
6
8
|
* BIP-322 signature implementation for Bitcoin message signing.
|
package/dist/esm/forfeit.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './singleKey.js';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { pubSchnorr, randomPrivateKeyBytes } from "@scure/btc-signer/utils";
|
|
1
|
+
import { pubSchnorr, randomPrivateKeyBytes, sha256, } from "@scure/btc-signer/utils.js";
|
|
2
2
|
import { hex } from "@scure/base";
|
|
3
|
-
import { SigHash } from "@scure/btc-signer";
|
|
3
|
+
import { SigHash } from "@scure/btc-signer/transaction.js";
|
|
4
4
|
import { TreeSignerSession } from '../tree/signingSession.js';
|
|
5
|
+
import { schnorr } from "@noble/secp256k1";
|
|
5
6
|
const ZERO_32 = new Uint8Array(32).fill(0);
|
|
6
7
|
const ALL_SIGHASH = Object.values(SigHash).filter((x) => typeof x === "number");
|
|
7
8
|
/**
|
|
@@ -15,6 +16,9 @@ const ALL_SIGHASH = Object.values(SigHash).filter((x) => typeof x === "number");
|
|
|
15
16
|
* // Create from raw bytes
|
|
16
17
|
* const key = SingleKey.fromPrivateKey(privateKeyBytes);
|
|
17
18
|
*
|
|
19
|
+
* // Create random key
|
|
20
|
+
* const randomKey = SingleKey.fromRandomBytes();
|
|
21
|
+
*
|
|
18
22
|
* // Sign a transaction
|
|
19
23
|
* const signedTx = await key.sign(transaction);
|
|
20
24
|
* ```
|
|
@@ -29,6 +33,17 @@ export class SingleKey {
|
|
|
29
33
|
static fromHex(privateKeyHex) {
|
|
30
34
|
return new SingleKey(hex.decode(privateKeyHex));
|
|
31
35
|
}
|
|
36
|
+
static fromRandomBytes() {
|
|
37
|
+
return new SingleKey(randomPrivateKeyBytes());
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Export the private key as a hex string.
|
|
41
|
+
*
|
|
42
|
+
* @returns The private key as a hex string
|
|
43
|
+
*/
|
|
44
|
+
toHex() {
|
|
45
|
+
return hex.encode(this.key);
|
|
46
|
+
}
|
|
32
47
|
async sign(tx, inputIndexes) {
|
|
33
48
|
const txCpy = tx.clone();
|
|
34
49
|
if (!inputIndexes) {
|
|
@@ -56,9 +71,13 @@ export class SingleKey {
|
|
|
56
71
|
return txCpy;
|
|
57
72
|
}
|
|
58
73
|
xOnlyPublicKey() {
|
|
59
|
-
return pubSchnorr(this.key);
|
|
74
|
+
return Promise.resolve(pubSchnorr(this.key));
|
|
60
75
|
}
|
|
61
76
|
signerSession() {
|
|
62
77
|
return TreeSignerSession.random();
|
|
63
78
|
}
|
|
79
|
+
async signMessage(message) {
|
|
80
|
+
const msgBytes = new TextEncoder().encode(message);
|
|
81
|
+
return schnorr.sign(sha256(msgBytes), this.key);
|
|
82
|
+
}
|
|
64
83
|
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Transaction } from "@scure/btc-signer";
|
|
1
|
+
import { Transaction } from "@scure/btc-signer/transaction.js";
|
|
2
2
|
import { SingleKey } from './identity/singleKey.js';
|
|
3
3
|
import { ArkAddress } from './script/address.js';
|
|
4
4
|
import { VHTLC } from './script/vhtlc.js';
|
|
@@ -21,11 +21,12 @@ import { buildOffchainTx, } from './utils/arkTransaction.js';
|
|
|
21
21
|
import { VtxoTaprootTree, ConditionWitness, getArkPsbtFields, setArkPsbtField, ArkPsbtFieldKey, ArkPsbtFieldKeyType, CosignerPublicKey, VtxoTreeExpiry, } from './utils/unknownFields.js';
|
|
22
22
|
import { BIP322 } from './bip322/index.js';
|
|
23
23
|
import { ArkNote } from './arknote/index.js';
|
|
24
|
-
import { IndexedDBVtxoRepository } from './wallet/serviceWorker/db/vtxo/idb.js';
|
|
25
24
|
import { networks } from './networks.js';
|
|
26
25
|
import { RestIndexerProvider, IndexerTxType, ChainTxType, } from './providers/indexer.js';
|
|
27
26
|
import { P2A } from './utils/anchor.js';
|
|
28
27
|
import { Unroll } from './wallet/unroll.js';
|
|
28
|
+
import { WalletRepositoryImpl } from './repositories/walletRepository.js';
|
|
29
|
+
import { ContractRepositoryImpl } from './repositories/contractRepository.js';
|
|
29
30
|
export {
|
|
30
31
|
// Wallets
|
|
31
32
|
Wallet, SingleKey, OnchainWallet, Ramps,
|
|
@@ -47,8 +48,8 @@ buildOffchainTx, waitForIncomingFunds,
|
|
|
47
48
|
ArkNote,
|
|
48
49
|
// Network
|
|
49
50
|
networks,
|
|
50
|
-
//
|
|
51
|
-
|
|
51
|
+
// Repositories
|
|
52
|
+
WalletRepositoryImpl, ContractRepositoryImpl,
|
|
52
53
|
// BIP322
|
|
53
54
|
BIP322,
|
|
54
55
|
// TxTree
|
package/dist/esm/musig2/keys.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as musig from "@scure/btc-signer/musig2";
|
|
2
|
-
import { schnorr } from "@noble/curves/secp256k1";
|
|
1
|
+
import * as musig from "@scure/btc-signer/musig2.js";
|
|
2
|
+
import { schnorr } from "@noble/curves/secp256k1.js";
|
|
3
3
|
// Aggregates multiple public keys according to the MuSig2 algorithm
|
|
4
4
|
export function aggregateKeys(publicKeys, sort, options = {}) {
|
|
5
5
|
if (sort) {
|
|
@@ -8,14 +8,14 @@ export function aggregateKeys(publicKeys, sort, options = {}) {
|
|
|
8
8
|
const { aggPublicKey: preTweakedKey } = musig.keyAggregate(publicKeys);
|
|
9
9
|
if (!options.taprootTweak) {
|
|
10
10
|
return {
|
|
11
|
-
preTweakedKey: preTweakedKey.
|
|
12
|
-
finalKey: preTweakedKey.
|
|
11
|
+
preTweakedKey: preTweakedKey.toBytes(true),
|
|
12
|
+
finalKey: preTweakedKey.toBytes(true),
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
const tweakBytes = schnorr.utils.taggedHash("TapTweak", preTweakedKey.
|
|
15
|
+
const tweakBytes = schnorr.utils.taggedHash("TapTweak", preTweakedKey.toBytes(true).subarray(1), options.taprootTweak ?? new Uint8Array(0));
|
|
16
16
|
const { aggPublicKey: finalKey } = musig.keyAggregate(publicKeys, [tweakBytes], [true]);
|
|
17
17
|
return {
|
|
18
|
-
preTweakedKey: preTweakedKey.
|
|
19
|
-
finalKey: finalKey.
|
|
18
|
+
preTweakedKey: preTweakedKey.toBytes(true),
|
|
19
|
+
finalKey: finalKey.toBytes(true),
|
|
20
20
|
};
|
|
21
21
|
}
|
package/dist/esm/musig2/sign.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as musig from "@scure/btc-signer/musig2";
|
|
2
|
-
import { bytesToNumberBE } from "@noble/curves/
|
|
3
|
-
import {
|
|
1
|
+
import * as musig from "@scure/btc-signer/musig2.js";
|
|
2
|
+
import { bytesToNumberBE } from "@noble/curves/utils.js";
|
|
3
|
+
import { Point } from "@noble/secp256k1";
|
|
4
4
|
import { aggregateKeys } from './keys.js';
|
|
5
|
-
import { schnorr } from "@noble/curves/secp256k1";
|
|
5
|
+
import { schnorr } from "@noble/curves/secp256k1.js";
|
|
6
6
|
// Add this error type for decode failures
|
|
7
7
|
export class PartialSignatureError extends Error {
|
|
8
8
|
constructor(message) {
|
|
@@ -40,7 +40,7 @@ export class PartialSig {
|
|
|
40
40
|
}
|
|
41
41
|
// Verify s is less than curve order
|
|
42
42
|
const s = bytesToNumberBE(bytes);
|
|
43
|
-
if (s >= CURVE.n) {
|
|
43
|
+
if (s >= Point.CURVE().n) {
|
|
44
44
|
throw new PartialSignatureError("s value overflows curve order");
|
|
45
45
|
}
|
|
46
46
|
// For decode we don't have R, so we'll need to compute it later
|
package/dist/esm/networks.js
CHANGED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export class ContractRepositoryImpl {
|
|
2
|
+
constructor(storage) {
|
|
3
|
+
this.cache = new Map();
|
|
4
|
+
this.storage = storage;
|
|
5
|
+
}
|
|
6
|
+
async getContractData(contractId, key) {
|
|
7
|
+
const storageKey = `contract:${contractId}:${key}`;
|
|
8
|
+
const cached = this.cache.get(storageKey);
|
|
9
|
+
if (cached !== undefined)
|
|
10
|
+
return cached;
|
|
11
|
+
const stored = await this.storage.getItem(storageKey);
|
|
12
|
+
if (!stored)
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
const data = JSON.parse(stored);
|
|
16
|
+
this.cache.set(storageKey, data);
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.error(`Failed to parse contract data for ${contractId}:${key}:`, error);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async setContractData(contractId, key, data) {
|
|
25
|
+
const storageKey = `contract:${contractId}:${key}`;
|
|
26
|
+
try {
|
|
27
|
+
// First persist to storage, only update cache if successful
|
|
28
|
+
await this.storage.setItem(storageKey, JSON.stringify(data));
|
|
29
|
+
this.cache.set(storageKey, data);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
// Storage operation failed, cache remains unchanged
|
|
33
|
+
console.error(`Failed to persist contract data for ${contractId}:${key}:`, error);
|
|
34
|
+
throw error; // Rethrow to notify caller of failure
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async deleteContractData(contractId, key) {
|
|
38
|
+
const storageKey = `contract:${contractId}:${key}`;
|
|
39
|
+
try {
|
|
40
|
+
// First remove from persistent storage, only delete from cache if successful
|
|
41
|
+
await this.storage.removeItem(storageKey);
|
|
42
|
+
this.cache.delete(storageKey);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
// Storage operation failed, cache remains unchanged
|
|
46
|
+
console.error(`Failed to remove contract data for ${contractId}:${key}:`, error);
|
|
47
|
+
throw error; // Rethrow to notify caller of failure
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async getContractCollection(contractType) {
|
|
51
|
+
const storageKey = `collection:${contractType}`;
|
|
52
|
+
const cached = this.cache.get(storageKey);
|
|
53
|
+
if (cached !== undefined)
|
|
54
|
+
return cached;
|
|
55
|
+
const stored = await this.storage.getItem(storageKey);
|
|
56
|
+
if (!stored) {
|
|
57
|
+
this.cache.set(storageKey, []);
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const collection = JSON.parse(stored);
|
|
62
|
+
this.cache.set(storageKey, collection);
|
|
63
|
+
return collection;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(`Failed to parse contract collection ${contractType}:`, error);
|
|
67
|
+
this.cache.set(storageKey, []);
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async saveToContractCollection(contractType, item, idField) {
|
|
72
|
+
const collection = await this.getContractCollection(contractType);
|
|
73
|
+
// Validate that the item has the required id field
|
|
74
|
+
const itemId = item[idField];
|
|
75
|
+
if (itemId === undefined || itemId === null) {
|
|
76
|
+
throw new Error(`Item is missing required field '${String(idField)}'`);
|
|
77
|
+
}
|
|
78
|
+
// Find existing item index without mutating the original collection
|
|
79
|
+
const existingIndex = collection.findIndex((i) => i[idField] === itemId);
|
|
80
|
+
// Build new collection without mutating the cached one
|
|
81
|
+
let newCollection;
|
|
82
|
+
if (existingIndex !== -1) {
|
|
83
|
+
// Replace existing item
|
|
84
|
+
newCollection = [
|
|
85
|
+
...collection.slice(0, existingIndex),
|
|
86
|
+
item,
|
|
87
|
+
...collection.slice(existingIndex + 1),
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Add new item
|
|
92
|
+
newCollection = [...collection, item];
|
|
93
|
+
}
|
|
94
|
+
const storageKey = `collection:${contractType}`;
|
|
95
|
+
try {
|
|
96
|
+
// First persist to storage, only update cache if successful
|
|
97
|
+
await this.storage.setItem(storageKey, JSON.stringify(newCollection));
|
|
98
|
+
this.cache.set(storageKey, newCollection);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
// Storage operation failed, cache remains unchanged
|
|
102
|
+
console.error(`Failed to persist contract collection ${contractType}:`, error);
|
|
103
|
+
throw error; // Rethrow to notify caller of failure
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async removeFromContractCollection(contractType, id, idField) {
|
|
107
|
+
// Validate input parameters
|
|
108
|
+
if (id === undefined || id === null) {
|
|
109
|
+
throw new Error(`Invalid id provided for removal: ${String(id)}`);
|
|
110
|
+
}
|
|
111
|
+
const collection = await this.getContractCollection(contractType);
|
|
112
|
+
// Build new collection without the specified item
|
|
113
|
+
const filtered = collection.filter((item) => item[idField] !== id);
|
|
114
|
+
const storageKey = `collection:${contractType}`;
|
|
115
|
+
try {
|
|
116
|
+
// First persist to storage, only update cache if successful
|
|
117
|
+
await this.storage.setItem(storageKey, JSON.stringify(filtered));
|
|
118
|
+
this.cache.set(storageKey, filtered);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
// Storage operation failed, cache remains unchanged
|
|
122
|
+
console.error(`Failed to persist contract collection removal for ${contractType}:`, error);
|
|
123
|
+
throw error; // Rethrow to notify caller of failure
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export class WalletRepositoryImpl {
|
|
2
|
+
constructor(storage) {
|
|
3
|
+
this.storage = storage;
|
|
4
|
+
this.cache = {
|
|
5
|
+
vtxos: new Map(),
|
|
6
|
+
transactions: new Map(),
|
|
7
|
+
walletState: null,
|
|
8
|
+
initialized: new Set(),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
async getVtxos(address) {
|
|
12
|
+
const cacheKey = `vtxos:${address}`;
|
|
13
|
+
if (this.cache.vtxos.has(address)) {
|
|
14
|
+
return this.cache.vtxos.get(address);
|
|
15
|
+
}
|
|
16
|
+
const stored = await this.storage.getItem(cacheKey);
|
|
17
|
+
if (!stored) {
|
|
18
|
+
this.cache.vtxos.set(address, []);
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const vtxos = JSON.parse(stored);
|
|
23
|
+
this.cache.vtxos.set(address, vtxos);
|
|
24
|
+
return vtxos.slice();
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error(`Failed to parse VTXOs for address ${address}:`, error);
|
|
28
|
+
this.cache.vtxos.set(address, []);
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async saveVtxo(address, vtxo) {
|
|
33
|
+
const vtxos = await this.getVtxos(address);
|
|
34
|
+
const existing = vtxos.findIndex((v) => v.txid === vtxo.txid && v.vout === vtxo.vout);
|
|
35
|
+
if (existing !== -1) {
|
|
36
|
+
vtxos[existing] = vtxo;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
vtxos.push(vtxo);
|
|
40
|
+
}
|
|
41
|
+
this.cache.vtxos.set(address, vtxos);
|
|
42
|
+
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(vtxos));
|
|
43
|
+
}
|
|
44
|
+
async saveVtxos(address, vtxos) {
|
|
45
|
+
const storedVtxos = await this.getVtxos(address);
|
|
46
|
+
for (const vtxo of vtxos) {
|
|
47
|
+
const existing = storedVtxos.findIndex((v) => v.txid === vtxo.txid && v.vout === vtxo.vout);
|
|
48
|
+
if (existing !== -1) {
|
|
49
|
+
storedVtxos[existing] = vtxo;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
storedVtxos.push(vtxo);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.cache.vtxos.set(address, storedVtxos);
|
|
56
|
+
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(storedVtxos));
|
|
57
|
+
}
|
|
58
|
+
async removeVtxo(address, vtxoId) {
|
|
59
|
+
const vtxos = await this.getVtxos(address);
|
|
60
|
+
const [txid, vout] = vtxoId.split(":");
|
|
61
|
+
const filtered = vtxos.filter((v) => !(v.txid === txid && v.vout === parseInt(vout)));
|
|
62
|
+
this.cache.vtxos.set(address, filtered);
|
|
63
|
+
await this.storage.setItem(`vtxos:${address}`, JSON.stringify(filtered));
|
|
64
|
+
}
|
|
65
|
+
async clearVtxos(address) {
|
|
66
|
+
this.cache.vtxos.set(address, []);
|
|
67
|
+
await this.storage.removeItem(`vtxos:${address}`);
|
|
68
|
+
}
|
|
69
|
+
async getTransactionHistory(address) {
|
|
70
|
+
const cacheKey = `tx:${address}`;
|
|
71
|
+
if (this.cache.transactions.has(address)) {
|
|
72
|
+
return this.cache.transactions.get(address);
|
|
73
|
+
}
|
|
74
|
+
const stored = await this.storage.getItem(cacheKey);
|
|
75
|
+
if (!stored) {
|
|
76
|
+
this.cache.transactions.set(address, []);
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const transactions = JSON.parse(stored);
|
|
81
|
+
this.cache.transactions.set(address, transactions);
|
|
82
|
+
return transactions.slice();
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.error(`Failed to parse transactions for address ${address}:`, error);
|
|
86
|
+
this.cache.transactions.set(address, []);
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async saveTransaction(address, tx) {
|
|
91
|
+
const transactions = await this.getTransactionHistory(address);
|
|
92
|
+
const existing = transactions.findIndex((t) => t.id === tx.id);
|
|
93
|
+
if (existing !== -1) {
|
|
94
|
+
transactions[existing] = tx;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
transactions.push(tx);
|
|
98
|
+
// Sort by timestamp descending
|
|
99
|
+
transactions.sort((a, b) => b.timestamp - a.timestamp);
|
|
100
|
+
}
|
|
101
|
+
this.cache.transactions.set(address, transactions);
|
|
102
|
+
await this.storage.setItem(`tx:${address}`, JSON.stringify(transactions));
|
|
103
|
+
}
|
|
104
|
+
async getWalletState() {
|
|
105
|
+
if (this.cache.walletState !== null ||
|
|
106
|
+
this.cache.initialized.has("walletState")) {
|
|
107
|
+
return this.cache.walletState;
|
|
108
|
+
}
|
|
109
|
+
const stored = await this.storage.getItem("wallet:state");
|
|
110
|
+
if (!stored) {
|
|
111
|
+
this.cache.walletState = null;
|
|
112
|
+
this.cache.initialized.add("walletState");
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const state = JSON.parse(stored);
|
|
117
|
+
this.cache.walletState = state;
|
|
118
|
+
this.cache.initialized.add("walletState");
|
|
119
|
+
return state;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.error("Failed to parse wallet state:", error);
|
|
123
|
+
this.cache.walletState = null;
|
|
124
|
+
this.cache.initialized.add("walletState");
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async saveWalletState(state) {
|
|
129
|
+
this.cache.walletState = state;
|
|
130
|
+
await this.storage.setItem("wallet:state", JSON.stringify(state));
|
|
131
|
+
}
|
|
132
|
+
}
|
package/dist/esm/script/base.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Address, p2tr, TAP_LEAF_VERSION, taprootListToTree, } from "@scure/btc-signer/payment";
|
|
2
|
-
import { TAPROOT_UNSPENDABLE_KEY, } from "@scure/btc-signer/utils";
|
|
1
|
+
import { Address, p2tr, TAP_LEAF_VERSION, taprootListToTree, } from "@scure/btc-signer/payment.js";
|
|
2
|
+
import { TAPROOT_UNSPENDABLE_KEY, } from "@scure/btc-signer/utils.js";
|
|
3
3
|
import { ArkAddress } from './address.js';
|
|
4
|
-
import { Script } from "@scure/btc-signer";
|
|
4
|
+
import { Script } from "@scure/btc-signer/script.js";
|
|
5
5
|
import { hex } from "@scure/base";
|
|
6
6
|
import { ConditionCSVMultisigTapscript, CSVMultisigTapscript, } from './tapscript.js';
|
|
7
7
|
export function scriptFromTapLeafScript(leaf) {
|