@arkade-os/sdk 0.4.11 → 0.4.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/contracts/contractManager.js +8 -0
- package/dist/cjs/identity/seedIdentity.js +11 -12
- package/dist/cjs/wallet/wallet.js +14 -1
- package/dist/esm/contracts/contractManager.js +8 -0
- package/dist/esm/identity/seedIdentity.js +6 -7
- package/dist/esm/wallet/wallet.js +14 -1
- package/dist/types/wallet/wallet.d.ts +6 -0
- package/package.json +3 -4
|
@@ -149,7 +149,15 @@ class ContractManager {
|
|
|
149
149
|
// Persist
|
|
150
150
|
await this.config.contractRepository.saveContract(contract);
|
|
151
151
|
// fetch all VTXOs (including spent/swept) for this contract
|
|
152
|
+
const requestStartedAt = Date.now();
|
|
152
153
|
await this.fetchContractVxosFromIndexer([contract], true);
|
|
154
|
+
// Advance the sync cursor so that the watcher's vtxo_received
|
|
155
|
+
// event (triggered by addContract below) doesn't re-bootstrap
|
|
156
|
+
// the same script via deltaSyncContracts.
|
|
157
|
+
const cutoff = (0, syncCursors_1.cursorCutoff)(requestStartedAt);
|
|
158
|
+
await (0, syncCursors_1.advanceSyncCursors)(this.config.walletRepository, {
|
|
159
|
+
[contract.script]: cutoff,
|
|
160
|
+
});
|
|
153
161
|
// Add to watcher
|
|
154
162
|
await this.watcher.addContract(contract);
|
|
155
163
|
return contract;
|
|
@@ -7,8 +7,7 @@ const utils_js_1 = require("@scure/btc-signer/utils.js");
|
|
|
7
7
|
const btc_signer_1 = require("@scure/btc-signer");
|
|
8
8
|
const signingSession_1 = require("../tree/signingSession");
|
|
9
9
|
const secp256k1_1 = require("@noble/secp256k1");
|
|
10
|
-
const
|
|
11
|
-
const { expand } = bitcoin_descriptors_1.defaultFactory;
|
|
10
|
+
const descriptors_scure_1 = require("@bitcoinerlab/descriptors-scure");
|
|
12
11
|
const ALL_SIGHASH = Object.values(btc_signer_1.SigHash).filter((x) => typeof x === "number");
|
|
13
12
|
/**
|
|
14
13
|
* Detects the network from a descriptor string by checking for tpub (testnet)
|
|
@@ -16,7 +15,7 @@ const ALL_SIGHASH = Object.values(btc_signer_1.SigHash).filter((x) => typeof x =
|
|
|
16
15
|
* @internal
|
|
17
16
|
*/
|
|
18
17
|
function detectNetwork(descriptor) {
|
|
19
|
-
return descriptor.includes("tpub") ?
|
|
18
|
+
return descriptor.includes("tpub") ? descriptors_scure_1.networks.testnet : descriptors_scure_1.networks.bitcoin;
|
|
20
19
|
}
|
|
21
20
|
function hasDescriptor(opts) {
|
|
22
21
|
return "descriptor" in opts && typeof opts.descriptor === "string";
|
|
@@ -26,9 +25,9 @@ function hasDescriptor(opts) {
|
|
|
26
25
|
* @internal
|
|
27
26
|
*/
|
|
28
27
|
function buildDescriptor(seed, isMainnet) {
|
|
29
|
-
const network = isMainnet ?
|
|
30
|
-
const masterNode =
|
|
31
|
-
return
|
|
28
|
+
const network = isMainnet ? descriptors_scure_1.networks.bitcoin : descriptors_scure_1.networks.testnet;
|
|
29
|
+
const masterNode = descriptors_scure_1.HDKey.fromMasterSeed(seed, network.bip32);
|
|
30
|
+
return descriptors_scure_1.scriptExpressions.trBIP32({
|
|
32
31
|
masterNode,
|
|
33
32
|
network,
|
|
34
33
|
account: 0,
|
|
@@ -72,22 +71,22 @@ class SeedIdentity {
|
|
|
72
71
|
this.descriptor = descriptor;
|
|
73
72
|
const network = detectNetwork(descriptor);
|
|
74
73
|
// Parse and validate the descriptor using the library
|
|
75
|
-
const expansion = expand({ descriptor, network });
|
|
74
|
+
const expansion = (0, descriptors_scure_1.expand)({ descriptor, network });
|
|
76
75
|
const keyInfo = expansion.expansionMap?.["@0"];
|
|
77
76
|
if (!keyInfo?.originPath) {
|
|
78
77
|
throw new Error("Descriptor must include a key origin path");
|
|
79
78
|
}
|
|
80
79
|
// Verify the xpub in the descriptor matches our seed
|
|
81
|
-
const masterNode =
|
|
82
|
-
const accountNode = masterNode.
|
|
83
|
-
if (accountNode.
|
|
80
|
+
const masterNode = descriptors_scure_1.HDKey.fromMasterSeed(seed, network.bip32);
|
|
81
|
+
const accountNode = masterNode.derive(`m${keyInfo.originPath}`);
|
|
82
|
+
if (accountNode.publicExtendedKey !== keyInfo.bip32?.toBase58()) {
|
|
84
83
|
throw new Error("xpub mismatch: derived key does not match descriptor");
|
|
85
84
|
}
|
|
86
85
|
// Derive the private key using the full path from the descriptor
|
|
87
86
|
if (!keyInfo.path) {
|
|
88
87
|
throw new Error("Descriptor must specify a full derivation path");
|
|
89
88
|
}
|
|
90
|
-
const derivedNode = masterNode.
|
|
89
|
+
const derivedNode = masterNode.derive(keyInfo.path);
|
|
91
90
|
if (!derivedNode.privateKey) {
|
|
92
91
|
throw new Error("Failed to derive private key");
|
|
93
92
|
}
|
|
@@ -217,7 +216,7 @@ class ReadonlyDescriptorIdentity {
|
|
|
217
216
|
constructor(descriptor) {
|
|
218
217
|
this.descriptor = descriptor;
|
|
219
218
|
const network = detectNetwork(descriptor);
|
|
220
|
-
const expansion = expand({ descriptor, network });
|
|
219
|
+
const expansion = (0, descriptors_scure_1.expand)({ descriptor, network });
|
|
221
220
|
const keyInfo = expansion.expansionMap?.["@0"];
|
|
222
221
|
if (!keyInfo?.pubkey) {
|
|
223
222
|
throw new Error("Failed to derive public key from descriptor");
|
|
@@ -309,8 +309,21 @@ class ReadonlyWallet {
|
|
|
309
309
|
* Delta-sync wallet VTXOs: fetch only changed VTXOs since the last
|
|
310
310
|
* cursor, or do a full bootstrap when no cursor exists. Upserts
|
|
311
311
|
* the result into the cache and advances the sync cursors.
|
|
312
|
+
*
|
|
313
|
+
* Concurrent calls are deduplicated: if a sync is already in flight,
|
|
314
|
+
* subsequent callers receive the same promise instead of triggering
|
|
315
|
+
* a second network round-trip.
|
|
312
316
|
*/
|
|
313
|
-
|
|
317
|
+
syncVtxos() {
|
|
318
|
+
if (this._syncVtxosInflight)
|
|
319
|
+
return this._syncVtxosInflight;
|
|
320
|
+
const p = this.doSyncVtxos().finally(() => {
|
|
321
|
+
this._syncVtxosInflight = undefined;
|
|
322
|
+
});
|
|
323
|
+
this._syncVtxosInflight = p;
|
|
324
|
+
return p;
|
|
325
|
+
}
|
|
326
|
+
async doSyncVtxos() {
|
|
314
327
|
const address = await this.getAddress();
|
|
315
328
|
// Batch cursor read with script map to avoid extra async hops
|
|
316
329
|
// before the fetch (background operations may run between hops).
|
|
@@ -146,7 +146,15 @@ export class ContractManager {
|
|
|
146
146
|
// Persist
|
|
147
147
|
await this.config.contractRepository.saveContract(contract);
|
|
148
148
|
// fetch all VTXOs (including spent/swept) for this contract
|
|
149
|
+
const requestStartedAt = Date.now();
|
|
149
150
|
await this.fetchContractVxosFromIndexer([contract], true);
|
|
151
|
+
// Advance the sync cursor so that the watcher's vtxo_received
|
|
152
|
+
// event (triggered by addContract below) doesn't re-bootstrap
|
|
153
|
+
// the same script via deltaSyncContracts.
|
|
154
|
+
const cutoff = cursorCutoff(requestStartedAt);
|
|
155
|
+
await advanceSyncCursors(this.config.walletRepository, {
|
|
156
|
+
[contract.script]: cutoff,
|
|
157
|
+
});
|
|
150
158
|
// Add to watcher
|
|
151
159
|
await this.watcher.addContract(contract);
|
|
152
160
|
return contract;
|
|
@@ -4,8 +4,7 @@ import { pubECDSA, pubSchnorr } from "@scure/btc-signer/utils.js";
|
|
|
4
4
|
import { SigHash } from "@scure/btc-signer";
|
|
5
5
|
import { TreeSignerSession } from '../tree/signingSession.js';
|
|
6
6
|
import { schnorr, signAsync } from "@noble/secp256k1";
|
|
7
|
-
import {
|
|
8
|
-
const { expand } = defaultFactory;
|
|
7
|
+
import { HDKey, expand, networks, scriptExpressions, } from "@bitcoinerlab/descriptors-scure";
|
|
9
8
|
const ALL_SIGHASH = Object.values(SigHash).filter((x) => typeof x === "number");
|
|
10
9
|
/**
|
|
11
10
|
* Detects the network from a descriptor string by checking for tpub (testnet)
|
|
@@ -24,7 +23,7 @@ function hasDescriptor(opts) {
|
|
|
24
23
|
*/
|
|
25
24
|
function buildDescriptor(seed, isMainnet) {
|
|
26
25
|
const network = isMainnet ? networks.bitcoin : networks.testnet;
|
|
27
|
-
const masterNode =
|
|
26
|
+
const masterNode = HDKey.fromMasterSeed(seed, network.bip32);
|
|
28
27
|
return scriptExpressions.trBIP32({
|
|
29
28
|
masterNode,
|
|
30
29
|
network,
|
|
@@ -75,16 +74,16 @@ export class SeedIdentity {
|
|
|
75
74
|
throw new Error("Descriptor must include a key origin path");
|
|
76
75
|
}
|
|
77
76
|
// Verify the xpub in the descriptor matches our seed
|
|
78
|
-
const masterNode =
|
|
79
|
-
const accountNode = masterNode.
|
|
80
|
-
if (accountNode.
|
|
77
|
+
const masterNode = HDKey.fromMasterSeed(seed, network.bip32);
|
|
78
|
+
const accountNode = masterNode.derive(`m${keyInfo.originPath}`);
|
|
79
|
+
if (accountNode.publicExtendedKey !== keyInfo.bip32?.toBase58()) {
|
|
81
80
|
throw new Error("xpub mismatch: derived key does not match descriptor");
|
|
82
81
|
}
|
|
83
82
|
// Derive the private key using the full path from the descriptor
|
|
84
83
|
if (!keyInfo.path) {
|
|
85
84
|
throw new Error("Descriptor must specify a full derivation path");
|
|
86
85
|
}
|
|
87
|
-
const derivedNode = masterNode.
|
|
86
|
+
const derivedNode = masterNode.derive(keyInfo.path);
|
|
88
87
|
if (!derivedNode.privateKey) {
|
|
89
88
|
throw new Error("Failed to derive private key");
|
|
90
89
|
}
|
|
@@ -304,8 +304,21 @@ export class ReadonlyWallet {
|
|
|
304
304
|
* Delta-sync wallet VTXOs: fetch only changed VTXOs since the last
|
|
305
305
|
* cursor, or do a full bootstrap when no cursor exists. Upserts
|
|
306
306
|
* the result into the cache and advances the sync cursors.
|
|
307
|
+
*
|
|
308
|
+
* Concurrent calls are deduplicated: if a sync is already in flight,
|
|
309
|
+
* subsequent callers receive the same promise instead of triggering
|
|
310
|
+
* a second network round-trip.
|
|
307
311
|
*/
|
|
308
|
-
|
|
312
|
+
syncVtxos() {
|
|
313
|
+
if (this._syncVtxosInflight)
|
|
314
|
+
return this._syncVtxosInflight;
|
|
315
|
+
const p = this.doSyncVtxos().finally(() => {
|
|
316
|
+
this._syncVtxosInflight = undefined;
|
|
317
|
+
});
|
|
318
|
+
this._syncVtxosInflight = p;
|
|
319
|
+
return p;
|
|
320
|
+
}
|
|
321
|
+
async doSyncVtxos() {
|
|
309
322
|
const address = await this.getAddress();
|
|
310
323
|
// Batch cursor read with script map to avoid extra async hops
|
|
311
324
|
// before the fetch (background operations may run between hops).
|
|
@@ -43,6 +43,7 @@ export declare class ReadonlyWallet implements IReadonlyWallet {
|
|
|
43
43
|
private _contractManagerInitializing?;
|
|
44
44
|
protected readonly watcherConfig?: ReadonlyWalletConfig["watcherConfig"];
|
|
45
45
|
private readonly _assetManager;
|
|
46
|
+
private _syncVtxosInflight?;
|
|
46
47
|
get assetManager(): IReadonlyAssetManager;
|
|
47
48
|
protected constructor(identity: ReadonlyIdentity, network: Network, onchainProvider: OnchainProvider, indexerProvider: IndexerProvider, arkServerPublicKey: Bytes, offchainTapscript: DefaultVtxo.Script | DelegateVtxo.Script, boardingTapscript: DefaultVtxo.Script, dustAmount: bigint, walletRepository: WalletRepository, contractRepository: ContractRepository, delegatorProvider?: DelegatorProvider | undefined, watcherConfig?: ReadonlyWalletConfig["watcherConfig"]);
|
|
48
49
|
/**
|
|
@@ -80,8 +81,13 @@ export declare class ReadonlyWallet implements IReadonlyWallet {
|
|
|
80
81
|
* Delta-sync wallet VTXOs: fetch only changed VTXOs since the last
|
|
81
82
|
* cursor, or do a full bootstrap when no cursor exists. Upserts
|
|
82
83
|
* the result into the cache and advances the sync cursors.
|
|
84
|
+
*
|
|
85
|
+
* Concurrent calls are deduplicated: if a sync is already in flight,
|
|
86
|
+
* subsequent callers receive the same promise instead of triggering
|
|
87
|
+
* a second network round-trip.
|
|
83
88
|
*/
|
|
84
89
|
private syncVtxos;
|
|
90
|
+
private doSyncVtxos;
|
|
85
91
|
/**
|
|
86
92
|
* Clear all VTXO sync cursors, forcing a full re-bootstrap on next sync.
|
|
87
93
|
* Useful for recovery after indexer reprocessing or debugging.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkade-os/sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
4
4
|
"description": "Bitcoin wallet SDK with Taproot and Ark integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -80,12 +80,11 @@
|
|
|
80
80
|
"registry": "https://registry.npmjs.org/"
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"@
|
|
83
|
+
"@bitcoinerlab/descriptors-scure": "3.1.4",
|
|
84
84
|
"@marcbachmann/cel-js": "7.3.1",
|
|
85
|
-
"@noble/curves": "2.0.
|
|
85
|
+
"@noble/curves": "2.0.1",
|
|
86
86
|
"@noble/secp256k1": "3.0.0",
|
|
87
87
|
"@scure/base": "2.0.0",
|
|
88
|
-
"@scure/bip32": "2.0.0",
|
|
89
88
|
"@scure/bip39": "2.0.1",
|
|
90
89
|
"@scure/btc-signer": "2.0.1",
|
|
91
90
|
"bip68": "1.0.4"
|