@arkade-os/sdk 0.4.6 → 0.4.8
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/identity/seedIdentity.js +2 -2
- package/dist/cjs/index.js +9 -2
- package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +140 -7
- package/dist/cjs/wallet/serviceWorker/wallet.js +264 -34
- package/dist/cjs/wallet/vtxo-manager.js +62 -45
- package/dist/cjs/wallet/wallet.js +94 -34
- package/dist/cjs/worker/errors.js +17 -0
- package/dist/cjs/worker/messageBus.js +7 -2
- package/dist/esm/identity/seedIdentity.js +2 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +136 -6
- package/dist/esm/wallet/serviceWorker/wallet.js +264 -34
- package/dist/esm/wallet/vtxo-manager.js +62 -45
- package/dist/esm/wallet/wallet.js +94 -34
- package/dist/esm/worker/errors.js +12 -0
- package/dist/esm/worker/messageBus.js +7 -2
- package/dist/types/identity/seedIdentity.d.ts +5 -2
- package/dist/types/index.d.ts +5 -4
- package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +80 -3
- package/dist/types/wallet/serviceWorker/wallet.d.ts +8 -0
- package/dist/types/wallet/vtxo-manager.d.ts +24 -2
- package/dist/types/wallet/wallet.d.ts +10 -0
- package/dist/types/worker/errors.d.ts +6 -0
- package/package.json +1 -1
|
@@ -57,6 +57,19 @@ export class ReadonlyWallet {
|
|
|
57
57
|
this.walletRepository = walletRepository;
|
|
58
58
|
this.contractRepository = contractRepository;
|
|
59
59
|
this.delegatorProvider = delegatorProvider;
|
|
60
|
+
// Guard: detect identity/server network mismatch for descriptor-based identities.
|
|
61
|
+
// This duplicates the check in setupWalletConfig() so that subclasses
|
|
62
|
+
// bypassing the factory still get the safety net.
|
|
63
|
+
if ("descriptor" in identity) {
|
|
64
|
+
const descriptor = identity.descriptor;
|
|
65
|
+
const identityIsMainnet = !descriptor.includes("tpub");
|
|
66
|
+
const serverIsMainnet = network.bech32 === "bc";
|
|
67
|
+
if (identityIsMainnet !== serverIsMainnet) {
|
|
68
|
+
throw new Error(`Network mismatch: identity uses ${identityIsMainnet ? "mainnet" : "testnet"} derivation ` +
|
|
69
|
+
`but wallet network is ${serverIsMainnet ? "mainnet" : "testnet"}. ` +
|
|
70
|
+
`Create identity with { isMainnet: ${serverIsMainnet} } to match.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
60
73
|
this.watcherConfig = watcherConfig;
|
|
61
74
|
this._assetManager = new ReadonlyAssetManager(this.indexerProvider);
|
|
62
75
|
}
|
|
@@ -84,6 +97,24 @@ export class ReadonlyWallet {
|
|
|
84
97
|
const indexerProvider = config.indexerProvider || new RestIndexerProvider(indexerUrl);
|
|
85
98
|
const info = await arkProvider.getInfo();
|
|
86
99
|
const network = getNetwork(info.network);
|
|
100
|
+
// Guard: detect identity/server network mismatch for seed-based identities.
|
|
101
|
+
// A mainnet descriptor (xpub, coin type 0) connected to a testnet server
|
|
102
|
+
// (or vice versa) means wrong derivation path → wrong keys → potential fund loss.
|
|
103
|
+
if ("descriptor" in config.identity) {
|
|
104
|
+
const descriptor = config.identity.descriptor;
|
|
105
|
+
const identityIsMainnet = !descriptor.includes("tpub");
|
|
106
|
+
const serverIsMainnet = info.network === "bitcoin";
|
|
107
|
+
if (identityIsMainnet && !serverIsMainnet) {
|
|
108
|
+
throw new Error(`Network mismatch: identity uses mainnet derivation (coin type 0) ` +
|
|
109
|
+
`but Ark server is on ${info.network}. ` +
|
|
110
|
+
`Create identity with { isMainnet: false } to use testnet derivation.`);
|
|
111
|
+
}
|
|
112
|
+
if (!identityIsMainnet && serverIsMainnet) {
|
|
113
|
+
throw new Error(`Network mismatch: identity uses testnet derivation (coin type 1) ` +
|
|
114
|
+
`but Ark server is on mainnet. ` +
|
|
115
|
+
`Create identity with { isMainnet: true } or omit isMainnet (defaults to mainnet).`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
87
118
|
// Extract esploraUrl from provider if not explicitly provided
|
|
88
119
|
const esploraUrl = config.esploraUrl || ESPLORA_URL[info.network];
|
|
89
120
|
// Use provided onchainProvider instance or create a new one
|
|
@@ -658,6 +689,20 @@ export class ReadonlyWallet {
|
|
|
658
689
|
* ```
|
|
659
690
|
*/
|
|
660
691
|
export class Wallet extends ReadonlyWallet {
|
|
692
|
+
_withTxLock(fn) {
|
|
693
|
+
let release;
|
|
694
|
+
const lock = new Promise((r) => (release = r));
|
|
695
|
+
const prev = this._txLock;
|
|
696
|
+
this._txLock = lock;
|
|
697
|
+
return prev.then(async () => {
|
|
698
|
+
try {
|
|
699
|
+
return await fn();
|
|
700
|
+
}
|
|
701
|
+
finally {
|
|
702
|
+
release();
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
}
|
|
661
706
|
constructor(identity, network, networkName, onchainProvider, arkProvider, indexerProvider, arkServerPublicKey, offchainTapscript, boardingTapscript, serverUnrollScript, forfeitOutputScript, forfeitPubkey, dustAmount, walletRepository, contractRepository,
|
|
662
707
|
/** @deprecated Use settlementConfig */
|
|
663
708
|
renewalConfig, delegatorProvider, watcherConfig, settlementConfig) {
|
|
@@ -667,6 +712,13 @@ export class Wallet extends ReadonlyWallet {
|
|
|
667
712
|
this.serverUnrollScript = serverUnrollScript;
|
|
668
713
|
this.forfeitOutputScript = forfeitOutputScript;
|
|
669
714
|
this.forfeitPubkey = forfeitPubkey;
|
|
715
|
+
/**
|
|
716
|
+
* Async mutex that serializes all operations submitting VTXOs to the Ark
|
|
717
|
+
* server (`settle`, `send`, `sendBitcoin`). This prevents VtxoManager's
|
|
718
|
+
* background renewal from racing with user-initiated transactions for the
|
|
719
|
+
* same VTXO inputs.
|
|
720
|
+
*/
|
|
721
|
+
this._txLock = Promise.resolve();
|
|
670
722
|
this.identity = identity;
|
|
671
723
|
// Backwards-compatible: keep renewalConfig populated for any code reading it
|
|
672
724
|
this.renewalConfig = {
|
|
@@ -805,40 +857,42 @@ export class Wallet extends ReadonlyWallet {
|
|
|
805
857
|
throw new Error("Invalid Ark address " + params.address);
|
|
806
858
|
}
|
|
807
859
|
if (params.selectedVtxos && params.selectedVtxos.length > 0) {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
860
|
+
return this._withTxLock(async () => {
|
|
861
|
+
const selectedVtxoSum = params
|
|
862
|
+
.selectedVtxos.map((v) => v.value)
|
|
863
|
+
.reduce((a, b) => a + b, 0);
|
|
864
|
+
if (selectedVtxoSum < params.amount) {
|
|
865
|
+
throw new Error("Selected VTXOs do not cover specified amount");
|
|
866
|
+
}
|
|
867
|
+
const changeAmount = selectedVtxoSum - params.amount;
|
|
868
|
+
const selected = {
|
|
869
|
+
inputs: params.selectedVtxos,
|
|
870
|
+
changeAmount: BigInt(changeAmount),
|
|
871
|
+
};
|
|
872
|
+
const outputAddress = ArkAddress.decode(params.address);
|
|
873
|
+
const outputScript = BigInt(params.amount) < this.dustAmount
|
|
874
|
+
? outputAddress.subdustPkScript
|
|
875
|
+
: outputAddress.pkScript;
|
|
876
|
+
const outputs = [
|
|
877
|
+
{
|
|
878
|
+
script: outputScript,
|
|
879
|
+
amount: BigInt(params.amount),
|
|
880
|
+
},
|
|
881
|
+
];
|
|
882
|
+
// add change output if needed
|
|
883
|
+
if (selected.changeAmount > 0n) {
|
|
884
|
+
const changeOutputScript = selected.changeAmount < this.dustAmount
|
|
885
|
+
? this.arkAddress.subdustPkScript
|
|
886
|
+
: this.arkAddress.pkScript;
|
|
887
|
+
outputs.push({
|
|
888
|
+
script: changeOutputScript,
|
|
889
|
+
amount: BigInt(selected.changeAmount),
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
const { arkTxid, signedCheckpointTxs } = await this.buildAndSubmitOffchainTx(selected.inputs, outputs);
|
|
893
|
+
await this.updateDbAfterOffchainTx(selected.inputs, arkTxid, signedCheckpointTxs, params.amount, selected.changeAmount, selected.changeAmount > 0n ? outputs.length - 1 : 0);
|
|
894
|
+
return arkTxid;
|
|
895
|
+
});
|
|
842
896
|
}
|
|
843
897
|
return this.send({
|
|
844
898
|
address: params.address,
|
|
@@ -846,6 +900,9 @@ export class Wallet extends ReadonlyWallet {
|
|
|
846
900
|
});
|
|
847
901
|
}
|
|
848
902
|
async settle(params, eventCallback) {
|
|
903
|
+
return this._withTxLock(() => this._settleImpl(params, eventCallback));
|
|
904
|
+
}
|
|
905
|
+
async _settleImpl(params, eventCallback) {
|
|
849
906
|
if (params?.inputs) {
|
|
850
907
|
for (const input of params.inputs) {
|
|
851
908
|
// validate arknotes inputs
|
|
@@ -1347,6 +1404,9 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1347
1404
|
* ```
|
|
1348
1405
|
*/
|
|
1349
1406
|
async send(...args) {
|
|
1407
|
+
return this._withTxLock(() => this._sendImpl(...args));
|
|
1408
|
+
}
|
|
1409
|
+
async _sendImpl(...args) {
|
|
1350
1410
|
if (args.length === 0) {
|
|
1351
1411
|
throw new Error("At least one receiver is required");
|
|
1352
1412
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class MessageBusNotInitializedError extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super("MessageBus not initialized");
|
|
4
|
+
this.name = "MessageBusNotInitializedError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class ServiceWorkerTimeoutError extends Error {
|
|
8
|
+
constructor(detail) {
|
|
9
|
+
super(detail);
|
|
10
|
+
this.name = "ServiceWorkerTimeoutError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -5,6 +5,7 @@ import { RestDelegatorProvider } from '../providers/delegator.js';
|
|
|
5
5
|
import { ReadonlySingleKey, SingleKey } from '../identity/index.js';
|
|
6
6
|
import { ReadonlyWallet, Wallet } from '../wallet/wallet.js';
|
|
7
7
|
import { hex } from "@scure/base";
|
|
8
|
+
import { MessageBusNotInitializedError, ServiceWorkerTimeoutError, } from './errors.js';
|
|
8
9
|
export class MessageBus {
|
|
9
10
|
constructor(walletRepository, contractRepository, { messageHandlers, tickIntervalMs = 10000, messageTimeoutMs = 30000, debug = false, buildServices, }) {
|
|
10
11
|
this.walletRepository = walletRepository;
|
|
@@ -180,6 +181,10 @@ export class MessageBus {
|
|
|
180
181
|
}
|
|
181
182
|
async processMessage(event) {
|
|
182
183
|
const { id, tag, broadcast } = event.data;
|
|
184
|
+
if (tag === "PING") {
|
|
185
|
+
event.source?.postMessage({ id, tag: "PONG" });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
183
188
|
if (tag === "INITIALIZE_MESSAGE_BUS") {
|
|
184
189
|
if (this.debug) {
|
|
185
190
|
console.log("Init Command received");
|
|
@@ -204,7 +209,7 @@ export class MessageBus {
|
|
|
204
209
|
event.source?.postMessage({
|
|
205
210
|
id,
|
|
206
211
|
tag: tag ?? "unknown",
|
|
207
|
-
error: new
|
|
212
|
+
error: new MessageBusNotInitializedError(),
|
|
208
213
|
});
|
|
209
214
|
return;
|
|
210
215
|
}
|
|
@@ -275,7 +280,7 @@ export class MessageBus {
|
|
|
275
280
|
return promise;
|
|
276
281
|
return new Promise((resolve, reject) => {
|
|
277
282
|
const timer = self.setTimeout(() => {
|
|
278
|
-
reject(new
|
|
283
|
+
reject(new ServiceWorkerTimeoutError(`Message handler timed out after ${this.messageTimeoutMs}ms (${label})`));
|
|
279
284
|
}, this.messageTimeoutMs);
|
|
280
285
|
promise.then((val) => {
|
|
281
286
|
self.clearTimeout(timer);
|
|
@@ -3,8 +3,11 @@ import { Transaction } from "../utils/transaction";
|
|
|
3
3
|
import { SignerSession } from "../tree/signingSession";
|
|
4
4
|
/** Use default BIP86 derivation with network selection. */
|
|
5
5
|
export interface NetworkOptions {
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Mainnet (coin type 0) or testnet (coin type 1).
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
isMainnet?: boolean;
|
|
8
11
|
}
|
|
9
12
|
/** Use a custom output descriptor for derivation. */
|
|
10
13
|
export interface DescriptorOptions {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { TxTree, TxTreeNode } from "./tree/txTree";
|
|
|
16
16
|
import { SignerSession, TreeNonces, TreePartialSigs } from "./tree/signingSession";
|
|
17
17
|
import { Ramps } from "./wallet/ramps";
|
|
18
18
|
import { isVtxoExpiringSoon, VtxoManager } from "./wallet/vtxo-manager";
|
|
19
|
-
import type { SettlementConfig } from "./wallet/vtxo-manager";
|
|
19
|
+
import type { IVtxoManager, SettlementConfig } from "./wallet/vtxo-manager";
|
|
20
20
|
import { ServiceWorkerWallet, ServiceWorkerReadonlyWallet } from "./wallet/serviceWorker/wallet";
|
|
21
21
|
import { OnchainWallet } from "./wallet/onchain";
|
|
22
22
|
import { setupServiceWorker } from "./worker/browser/utils";
|
|
@@ -47,6 +47,7 @@ import { ContractManager, ContractWatcher, contractHandlers, DefaultContractHand
|
|
|
47
47
|
import type { Contract, ContractVtxo, ContractState, ContractEvent, ContractEventCallback, ContractBalance, ContractWithVtxos, ContractHandler, PathSelection, PathContext, ContractManagerConfig, CreateContractParams, ContractWatcherConfig, ParsedArkContract, DefaultContractParams, DelegateContractParams, VHTLCContractParams } from "./contracts";
|
|
48
48
|
import { IContractManager } from "./contracts/contractManager";
|
|
49
49
|
import { closeDatabase, openDatabase } from "./repositories/indexedDB/manager";
|
|
50
|
-
import { WalletMessageHandler } from "./wallet/serviceWorker/wallet-message-handler";
|
|
51
|
-
|
|
52
|
-
export
|
|
50
|
+
import { WalletMessageHandler, WalletNotInitializedError, ReadonlyWalletError, DelegatorNotConfiguredError } from "./wallet/serviceWorker/wallet-message-handler";
|
|
51
|
+
import { MessageBusNotInitializedError, ServiceWorkerTimeoutError } from "./worker/errors";
|
|
52
|
+
export { Wallet, ReadonlyWallet, SingleKey, ReadonlySingleKey, SeedIdentity, MnemonicIdentity, ReadonlyDescriptorIdentity, OnchainWallet, Ramps, VtxoManager, DelegatorManagerImpl, RestDelegatorProvider, ESPLORA_URL, EsploraProvider, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, DelegateVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, MessageBus, WalletMessageHandler, WalletNotInitializedError, ReadonlyWalletError, DelegatorNotConfiguredError, MessageBusNotInitializedError, ServiceWorkerTimeoutError, ServiceWorkerWallet, ServiceWorkerReadonlyWallet, decodeTapscript, MultisigTapscript, CSVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CLTVMultisigTapscript, TapTreeCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, setArkPsbtField, getArkPsbtFields, CosignerPublicKey, VtxoTreeExpiry, VtxoTaprootTree, ConditionWitness, buildOffchainTx, verifyTapscriptSignatures, waitForIncomingFunds, hasBoardingTxExpired, combineTapscriptSigs, isVtxoExpiringSoon, isValidArkAddress, ArkNote, networks, closeDatabase, openDatabase, IndexedDBWalletRepository, IndexedDBContractRepository, InMemoryWalletRepository, InMemoryContractRepository, MIGRATION_KEY, migrateWalletRepository, requiresMigration, getMigrationStatus, rollbackMigration, WalletRepositoryImpl, ContractRepositoryImpl, Intent, BIP322, TxTree, P2A, Unroll, Transaction, ArkError, maybeArkError, Batch, validateVtxoTxGraph, validateConnectorsTxGraph, buildForfeitTx, isRecoverable, isSpendable, isSubdust, isExpired, getSequence, ContractManager, ContractWatcher, contractHandlers, DefaultContractHandler, DelegateContractHandler, VHTLCContractHandler, encodeArkContract, decodeArkContract, contractFromArkContract, contractFromArkContractWithAddress, isArkContract, };
|
|
53
|
+
export type { Identity, ReadonlyIdentity, IWallet, IReadonlyWallet, BaseWalletConfig, WalletConfig, ReadonlyWalletConfig, ProviderClass, ArkTransaction, Coin, ExtendedCoin, ExtendedVirtualCoin, WalletBalance, SendBitcoinParams, SettleParams, Status, VirtualStatus, Outpoint, VirtualCoin, TxKey, TapscriptType, ArkTxInput, OffchainTx, TapLeaves, IncomingFunds, SeedIdentityOptions, MnemonicOptions, NetworkOptions, DescriptorOptions, IndexerProvider, PageResponse, BatchInfo, ChainTx, CommitmentTx, TxHistoryRecord, Vtxo, VtxoChain, Tx, OnchainProvider, ArkProvider, SettlementEvent, FeeInfo, ArkInfo, SignedIntent, Output, TxNotification, ExplorerTransaction, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, ScheduledSession, PaginationOptions, SubscriptionResponse, SubscriptionHeartbeat, SubscriptionEvent, Network, NetworkName, ArkTapscript, RelativeTimelock, EncodedVtxoScript, TapLeafScript, SignerSession, TreeNonces, TreePartialSigs, GetVtxosFilter, SettlementConfig, IVtxoManager, Asset, Recipient, IssuanceParams, IssuanceResult, ReissuanceParams, BurnParams, AssetDetails, AssetMetadata, KnownMetadata, Nonces, PartialSig, ArkPsbtFieldCoder, TxTreeNode, AnchorBumper, StorageConfig, Contract, ContractVtxo, ContractState, ContractEvent, ContractEventCallback, ContractBalance, ContractWithVtxos, ContractHandler, IContractManager, PathSelection, PathContext, ContractManagerConfig, CreateContractParams, ContractWatcherConfig, ParsedArkContract, DefaultContractParams, DelegateContractParams, VHTLCContractParams, MessageHandler, RequestEnvelope, ResponseEnvelope, IDelegatorManager, DelegatorProvider, DelegateInfo, DelegateOptions, WalletRepository, ContractRepository, MigrationStatus, };
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { SettlementEvent } from "../../providers/ark";
|
|
2
2
|
import type { Contract, ContractEvent, ContractWithVtxos, GetContractsFilter, PathSelection } from "../../contracts";
|
|
3
3
|
import type { CreateContractParams, GetAllSpendingPathsOptions, GetSpendablePathsOptions } from "../../contracts/contractManager";
|
|
4
|
-
import { ArkTransaction, AssetDetails, BurnParams, ExtendedCoin, GetVtxosFilter, IssuanceParams, IssuanceResult, IWallet, Recipient, ReissuanceParams, SendBitcoinParams, SettleParams, WalletBalance } from "../index";
|
|
4
|
+
import { ArkTransaction, AssetDetails, BurnParams, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter, IssuanceParams, IssuanceResult, IWallet, Recipient, ReissuanceParams, SendBitcoinParams, SettleParams, WalletBalance } from "../index";
|
|
5
5
|
import { DelegateInfo } from "../../providers/delegator";
|
|
6
6
|
import { MessageHandler, RequestEnvelope, ResponseEnvelope } from "../../worker/messageBus";
|
|
7
7
|
import { Transaction } from "../../utils/transaction";
|
|
8
|
+
export declare class WalletNotInitializedError extends Error {
|
|
9
|
+
constructor();
|
|
10
|
+
}
|
|
11
|
+
export declare class ReadonlyWalletError extends Error {
|
|
12
|
+
constructor();
|
|
13
|
+
}
|
|
14
|
+
export declare class DelegatorNotConfiguredError extends Error {
|
|
15
|
+
constructor();
|
|
16
|
+
}
|
|
8
17
|
export declare const DEFAULT_MESSAGE_TAG = "WALLET_UPDATER";
|
|
9
18
|
export type RequestInitWallet = RequestEnvelope & {
|
|
10
19
|
type: "INIT_WALLET";
|
|
@@ -236,6 +245,14 @@ export type ResponseSettleEvent = ResponseEnvelope & {
|
|
|
236
245
|
type: "SETTLE_EVENT";
|
|
237
246
|
payload: SettlementEvent;
|
|
238
247
|
};
|
|
248
|
+
export type ResponseRecoverVtxosEvent = ResponseEnvelope & {
|
|
249
|
+
type: "RECOVER_VTXOS_EVENT";
|
|
250
|
+
payload: SettlementEvent;
|
|
251
|
+
};
|
|
252
|
+
export type ResponseRenewVtxosEvent = ResponseEnvelope & {
|
|
253
|
+
type: "RENEW_VTXOS_EVENT";
|
|
254
|
+
payload: SettlementEvent;
|
|
255
|
+
};
|
|
239
256
|
export type ResponseUtxoUpdate = ResponseEnvelope & {
|
|
240
257
|
broadcast: true;
|
|
241
258
|
type: "UTXO_UPDATE";
|
|
@@ -355,8 +372,68 @@ export type ResponseGetDelegateInfo = ResponseEnvelope & {
|
|
|
355
372
|
info: DelegateInfo;
|
|
356
373
|
};
|
|
357
374
|
};
|
|
358
|
-
export type
|
|
359
|
-
|
|
375
|
+
export type RequestRecoverVtxos = RequestEnvelope & {
|
|
376
|
+
type: "RECOVER_VTXOS";
|
|
377
|
+
};
|
|
378
|
+
export type ResponseRecoverVtxos = ResponseEnvelope & {
|
|
379
|
+
type: "RECOVER_VTXOS_SUCCESS";
|
|
380
|
+
payload: {
|
|
381
|
+
txid: string;
|
|
382
|
+
};
|
|
383
|
+
};
|
|
384
|
+
export type RequestGetRecoverableBalance = RequestEnvelope & {
|
|
385
|
+
type: "GET_RECOVERABLE_BALANCE";
|
|
386
|
+
};
|
|
387
|
+
export type ResponseGetRecoverableBalance = ResponseEnvelope & {
|
|
388
|
+
type: "RECOVERABLE_BALANCE";
|
|
389
|
+
payload: {
|
|
390
|
+
recoverable: string;
|
|
391
|
+
subdust: string;
|
|
392
|
+
includesSubdust: boolean;
|
|
393
|
+
vtxoCount: number;
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
export type RequestGetExpiringVtxos = RequestEnvelope & {
|
|
397
|
+
type: "GET_EXPIRING_VTXOS";
|
|
398
|
+
payload: {
|
|
399
|
+
thresholdMs?: number;
|
|
400
|
+
};
|
|
401
|
+
};
|
|
402
|
+
export type ResponseGetExpiringVtxos = ResponseEnvelope & {
|
|
403
|
+
type: "EXPIRING_VTXOS";
|
|
404
|
+
payload: {
|
|
405
|
+
vtxos: ExtendedVirtualCoin[];
|
|
406
|
+
};
|
|
407
|
+
};
|
|
408
|
+
export type RequestRenewVtxos = RequestEnvelope & {
|
|
409
|
+
type: "RENEW_VTXOS";
|
|
410
|
+
};
|
|
411
|
+
export type ResponseRenewVtxos = ResponseEnvelope & {
|
|
412
|
+
type: "RENEW_VTXOS_SUCCESS";
|
|
413
|
+
payload: {
|
|
414
|
+
txid: string;
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
export type RequestGetExpiredBoardingUtxos = RequestEnvelope & {
|
|
418
|
+
type: "GET_EXPIRED_BOARDING_UTXOS";
|
|
419
|
+
};
|
|
420
|
+
export type ResponseGetExpiredBoardingUtxos = ResponseEnvelope & {
|
|
421
|
+
type: "EXPIRED_BOARDING_UTXOS";
|
|
422
|
+
payload: {
|
|
423
|
+
utxos: ExtendedCoin[];
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
export type RequestSweepExpiredBoardingUtxos = RequestEnvelope & {
|
|
427
|
+
type: "SWEEP_EXPIRED_BOARDING_UTXOS";
|
|
428
|
+
};
|
|
429
|
+
export type ResponseSweepExpiredBoardingUtxos = ResponseEnvelope & {
|
|
430
|
+
type: "SWEEP_EXPIRED_BOARDING_UTXOS_SUCCESS";
|
|
431
|
+
payload: {
|
|
432
|
+
txid: string;
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
export type WalletUpdaterRequest = RequestInitWallet | RequestSettle | RequestSendBitcoin | RequestGetAddress | RequestGetBoardingAddress | RequestGetBalance | RequestGetVtxos | RequestGetBoardingUtxos | RequestGetTransactionHistory | RequestGetStatus | RequestClear | RequestReloadWallet | RequestSignTransaction | RequestCreateContract | RequestGetContracts | RequestGetContractsWithVtxos | RequestUpdateContract | RequestDeleteContract | RequestGetSpendablePaths | RequestGetAllSpendingPaths | RequestIsContractManagerWatching | RequestSend | RequestGetAssetDetails | RequestIssue | RequestReissue | RequestBurn | RequestDelegate | RequestGetDelegateInfo | RequestRecoverVtxos | RequestGetRecoverableBalance | RequestGetExpiringVtxos | RequestRenewVtxos | RequestGetExpiredBoardingUtxos | RequestSweepExpiredBoardingUtxos;
|
|
436
|
+
export type WalletUpdaterResponse = ResponseEnvelope & (ResponseInitWallet | ResponseSettle | ResponseSettleEvent | ResponseSendBitcoin | ResponseGetAddress | ResponseGetBoardingAddress | ResponseGetBalance | ResponseGetVtxos | ResponseGetBoardingUtxos | ResponseGetTransactionHistory | ResponseGetStatus | ResponseClear | ResponseReloadWallet | ResponseUtxoUpdate | ResponseVtxoUpdate | ResponseSignTransaction | ResponseCreateContract | ResponseGetContracts | ResponseGetContractsWithVtxos | ResponseUpdateContract | ResponseDeleteContract | ResponseGetSpendablePaths | ResponseGetAllSpendingPaths | ResponseIsContractManagerWatching | ResponseContractEvent | ResponseSend | ResponseGetAssetDetails | ResponseIssue | ResponseReissue | ResponseBurn | ResponseDelegate | ResponseGetDelegateInfo | ResponseRecoverVtxos | ResponseRecoverVtxosEvent | ResponseGetRecoverableBalance | ResponseGetExpiringVtxos | ResponseRenewVtxos | ResponseRenewVtxosEvent | ResponseGetExpiredBoardingUtxos | ResponseSweepExpiredBoardingUtxos);
|
|
360
437
|
export declare class WalletMessageHandler implements MessageHandler<WalletUpdaterRequest, WalletUpdaterResponse> {
|
|
361
438
|
readonly messageTag: string;
|
|
362
439
|
private wallet;
|
|
@@ -6,6 +6,7 @@ import { ContractRepository } from "../../repositories/contractRepository";
|
|
|
6
6
|
import { RequestInitWallet, ResponseGetStatus, WalletUpdaterRequest, WalletUpdaterResponse } from "./wallet-message-handler";
|
|
7
7
|
import type { IContractManager } from "../../contracts/contractManager";
|
|
8
8
|
import type { IDelegatorManager } from "../delegator";
|
|
9
|
+
import type { IVtxoManager } from "../vtxo-manager";
|
|
9
10
|
type PrivateKeyIdentity = Identity & {
|
|
10
11
|
toHex(): string;
|
|
11
12
|
};
|
|
@@ -82,6 +83,8 @@ export declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet {
|
|
|
82
83
|
protected initWalletPayload: RequestInitWallet["payload"] | null;
|
|
83
84
|
protected messageBusTimeoutMs?: number;
|
|
84
85
|
private reinitPromise;
|
|
86
|
+
private pingPromise;
|
|
87
|
+
private inflightRequests;
|
|
85
88
|
get assetManager(): IReadonlyAssetManager;
|
|
86
89
|
protected constructor(serviceWorker: ServiceWorker, identity: ReadonlyIdentity, walletRepository: WalletRepository, contractRepository: ContractRepository, messageTag: string);
|
|
87
90
|
static create(options: ServiceWorkerWalletCreateOptions): Promise<ServiceWorkerReadonlyWallet>;
|
|
@@ -108,7 +111,11 @@ export declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet {
|
|
|
108
111
|
*/
|
|
109
112
|
static setup(options: ServiceWorkerWalletSetupOptions): Promise<ServiceWorkerReadonlyWallet>;
|
|
110
113
|
private sendMessageDirect;
|
|
114
|
+
private sendMessageStreaming;
|
|
111
115
|
protected sendMessage(request: WalletUpdaterRequest): Promise<WalletUpdaterResponse>;
|
|
116
|
+
private pingServiceWorker;
|
|
117
|
+
private sendMessageWithRetry;
|
|
118
|
+
protected sendMessageWithEvents(request: WalletUpdaterRequest, onEvent: (response: WalletUpdaterResponse) => void, isComplete: (response: WalletUpdaterResponse) => boolean): Promise<WalletUpdaterResponse>;
|
|
112
119
|
private reinitialize;
|
|
113
120
|
clear(): Promise<void>;
|
|
114
121
|
getAddress(): Promise<string>;
|
|
@@ -157,5 +164,6 @@ export declare class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet imp
|
|
|
157
164
|
settle(params?: SettleParams, callback?: (event: SettlementEvent) => void): Promise<string>;
|
|
158
165
|
send(...recipients: Recipient[]): Promise<string>;
|
|
159
166
|
getDelegatorManager(): Promise<IDelegatorManager | undefined>;
|
|
167
|
+
getVtxoManager(): Promise<IVtxoManager>;
|
|
160
168
|
}
|
|
161
169
|
export {};
|
|
@@ -145,7 +145,21 @@ export declare function getExpiringAndRecoverableVtxos(vtxos: ExtendedVirtualCoi
|
|
|
145
145
|
* }
|
|
146
146
|
* ```
|
|
147
147
|
*/
|
|
148
|
-
export
|
|
148
|
+
export interface IVtxoManager {
|
|
149
|
+
recoverVtxos(eventCallback?: (event: SettlementEvent) => void): Promise<string>;
|
|
150
|
+
getRecoverableBalance(): Promise<{
|
|
151
|
+
recoverable: bigint;
|
|
152
|
+
subdust: bigint;
|
|
153
|
+
includesSubdust: boolean;
|
|
154
|
+
vtxoCount: number;
|
|
155
|
+
}>;
|
|
156
|
+
getExpiringVtxos(thresholdMs?: number): Promise<ExtendedVirtualCoin[]>;
|
|
157
|
+
renewVtxos(eventCallback?: (event: SettlementEvent) => void): Promise<string>;
|
|
158
|
+
getExpiredBoardingUtxos(): Promise<ExtendedCoin[]>;
|
|
159
|
+
sweepExpiredBoardingUtxos(): Promise<string>;
|
|
160
|
+
dispose(): Promise<void>;
|
|
161
|
+
}
|
|
162
|
+
export declare class VtxoManager implements AsyncDisposable, IVtxoManager {
|
|
149
163
|
readonly wallet: IWallet;
|
|
150
164
|
/** @deprecated Use settlementConfig instead */
|
|
151
165
|
readonly renewalConfig?: RenewalConfig | undefined;
|
|
@@ -153,10 +167,12 @@ export declare class VtxoManager implements AsyncDisposable {
|
|
|
153
167
|
private contractEventsSubscription?;
|
|
154
168
|
private readonly contractEventsSubscriptionReady;
|
|
155
169
|
private disposePromise?;
|
|
156
|
-
private
|
|
170
|
+
private pollTimeoutId?;
|
|
157
171
|
private knownBoardingUtxos;
|
|
158
172
|
private sweptBoardingUtxos;
|
|
159
173
|
private pollInProgress;
|
|
174
|
+
private consecutivePollFailures;
|
|
175
|
+
private static readonly MAX_BACKOFF_MS;
|
|
160
176
|
constructor(wallet: IWallet,
|
|
161
177
|
/** @deprecated Use settlementConfig instead */
|
|
162
178
|
renewalConfig?: RenewalConfig | undefined, settlementConfig?: SettlementConfig | false);
|
|
@@ -323,12 +339,18 @@ export declare class VtxoManager implements AsyncDisposable {
|
|
|
323
339
|
/** Returns the wallet's identity for transaction signing. */
|
|
324
340
|
private getIdentity;
|
|
325
341
|
private initializeSubscription;
|
|
342
|
+
/** Computes the next poll delay, applying exponential backoff on failures. */
|
|
343
|
+
private getNextPollDelay;
|
|
326
344
|
/**
|
|
327
345
|
* Starts a polling loop that:
|
|
328
346
|
* 1. Auto-settles new boarding UTXOs into Ark
|
|
329
347
|
* 2. Sweeps expired boarding UTXOs (when boardingUtxoSweep is enabled)
|
|
348
|
+
*
|
|
349
|
+
* Uses setTimeout chaining (not setInterval) so a slow/blocked poll
|
|
350
|
+
* cannot stack up and the next delay can incorporate backoff.
|
|
330
351
|
*/
|
|
331
352
|
private startBoardingUtxoPoll;
|
|
353
|
+
private schedulePoll;
|
|
332
354
|
private pollBoardingUtxos;
|
|
333
355
|
/**
|
|
334
356
|
* Auto-settle new (unexpired) boarding UTXOs into the Ark.
|
|
@@ -171,6 +171,14 @@ export declare class Wallet extends ReadonlyWallet implements IWallet {
|
|
|
171
171
|
private _vtxoManager?;
|
|
172
172
|
private _vtxoManagerInitializing?;
|
|
173
173
|
private _walletAssetManager?;
|
|
174
|
+
/**
|
|
175
|
+
* Async mutex that serializes all operations submitting VTXOs to the Ark
|
|
176
|
+
* server (`settle`, `send`, `sendBitcoin`). This prevents VtxoManager's
|
|
177
|
+
* background renewal from racing with user-initiated transactions for the
|
|
178
|
+
* same VTXO inputs.
|
|
179
|
+
*/
|
|
180
|
+
private _txLock;
|
|
181
|
+
private _withTxLock;
|
|
174
182
|
/** @deprecated Use settlementConfig instead */
|
|
175
183
|
readonly renewalConfig: Required<Omit<WalletConfig["renewalConfig"], "enabled">> & {
|
|
176
184
|
enabled: boolean;
|
|
@@ -209,6 +217,7 @@ export declare class Wallet extends ReadonlyWallet implements IWallet {
|
|
|
209
217
|
*/
|
|
210
218
|
sendBitcoin(params: SendBitcoinParams): Promise<string>;
|
|
211
219
|
settle(params?: SettleParams, eventCallback?: (event: SettlementEvent) => void): Promise<string>;
|
|
220
|
+
private _settleImpl;
|
|
212
221
|
private handleSettlementFinalizationEvent;
|
|
213
222
|
/**
|
|
214
223
|
* @implements Batch.Handler interface.
|
|
@@ -247,6 +256,7 @@ export declare class Wallet extends ReadonlyWallet implements IWallet {
|
|
|
247
256
|
* ```
|
|
248
257
|
*/
|
|
249
258
|
send(...args: Recipient[]): Promise<string>;
|
|
259
|
+
private _sendImpl;
|
|
250
260
|
/**
|
|
251
261
|
* Build an offchain transaction from the given inputs and outputs,
|
|
252
262
|
* sign it, submit to the ark provider, and finalize.
|