@arkade-os/sdk 0.4.18 → 0.4.20

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.
Files changed (60) hide show
  1. package/dist/cjs/contracts/contractWatcher.js +7 -1
  2. package/dist/cjs/contracts/handlers/default.js +10 -3
  3. package/dist/cjs/contracts/handlers/helpers.js +47 -5
  4. package/dist/cjs/contracts/handlers/vhtlc.js +4 -2
  5. package/dist/cjs/identity/descriptor.js +98 -0
  6. package/dist/cjs/identity/descriptorProvider.js +2 -0
  7. package/dist/cjs/identity/index.js +15 -1
  8. package/dist/cjs/identity/seedIdentity.js +91 -6
  9. package/dist/cjs/identity/serialize.js +166 -0
  10. package/dist/cjs/identity/staticDescriptorProvider.js +65 -0
  11. package/dist/cjs/index.js +6 -3
  12. package/dist/cjs/providers/ark.js +45 -34
  13. package/dist/cjs/providers/electrum.js +663 -0
  14. package/dist/cjs/providers/indexer.js +5 -1
  15. package/dist/cjs/providers/utils.js +4 -0
  16. package/dist/cjs/wallet/ramps.js +1 -1
  17. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +10 -0
  18. package/dist/cjs/wallet/serviceWorker/wallet.js +137 -91
  19. package/dist/cjs/wallet/vtxo-manager.js +133 -17
  20. package/dist/cjs/wallet/wallet.js +80 -19
  21. package/dist/cjs/worker/messageBus.js +200 -56
  22. package/dist/esm/contracts/contractWatcher.js +7 -1
  23. package/dist/esm/contracts/handlers/default.js +10 -3
  24. package/dist/esm/contracts/handlers/helpers.js +47 -5
  25. package/dist/esm/contracts/handlers/vhtlc.js +4 -2
  26. package/dist/esm/identity/descriptor.js +92 -0
  27. package/dist/esm/identity/descriptorProvider.js +1 -0
  28. package/dist/esm/identity/index.js +6 -1
  29. package/dist/esm/identity/seedIdentity.js +89 -6
  30. package/dist/esm/identity/serialize.js +159 -0
  31. package/dist/esm/identity/staticDescriptorProvider.js +61 -0
  32. package/dist/esm/index.js +2 -1
  33. package/dist/esm/providers/ark.js +46 -35
  34. package/dist/esm/providers/electrum.js +658 -0
  35. package/dist/esm/providers/indexer.js +6 -2
  36. package/dist/esm/providers/utils.js +3 -0
  37. package/dist/esm/wallet/ramps.js +1 -1
  38. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +10 -0
  39. package/dist/esm/wallet/serviceWorker/wallet.js +137 -91
  40. package/dist/esm/wallet/vtxo-manager.js +133 -17
  41. package/dist/esm/wallet/wallet.js +80 -19
  42. package/dist/esm/worker/messageBus.js +201 -57
  43. package/dist/types/contracts/handlers/default.d.ts +1 -1
  44. package/dist/types/contracts/handlers/helpers.d.ts +1 -1
  45. package/dist/types/contracts/types.d.ts +11 -3
  46. package/dist/types/identity/descriptor.d.ts +35 -0
  47. package/dist/types/identity/descriptorProvider.d.ts +28 -0
  48. package/dist/types/identity/index.d.ts +7 -1
  49. package/dist/types/identity/seedIdentity.d.ts +41 -4
  50. package/dist/types/identity/serialize.d.ts +84 -0
  51. package/dist/types/identity/staticDescriptorProvider.d.ts +18 -0
  52. package/dist/types/index.d.ts +4 -2
  53. package/dist/types/providers/electrum.d.ts +212 -0
  54. package/dist/types/providers/utils.d.ts +1 -0
  55. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +11 -2
  56. package/dist/types/wallet/serviceWorker/wallet.d.ts +27 -10
  57. package/dist/types/wallet/vtxo-manager.d.ts +15 -0
  58. package/dist/types/wallet/wallet.d.ts +3 -0
  59. package/dist/types/worker/messageBus.d.ts +68 -8
  60. package/package.json +3 -2
@@ -0,0 +1,28 @@
1
+ import { Transaction } from "../utils/transaction";
2
+ /** A signing request that pairs a descriptor with a transaction. */
3
+ export interface DescriptorSigningRequest {
4
+ /** Descriptor identifying which key to sign with */
5
+ descriptor: string;
6
+ /** Transaction to sign */
7
+ tx: Transaction;
8
+ /** Specific input indexes to sign (signs all if omitted) */
9
+ inputIndexes?: number[];
10
+ }
11
+ /**
12
+ * Provider interface for descriptor-based signing.
13
+ *
14
+ * Implementations include:
15
+ * - {@link StaticDescriptorProvider}: wraps a legacy {@link Identity} with a single key.
16
+ * - HD-wallet provider: signs with keys derived from an xpub-based descriptor
17
+ * (planned — tracked separately from this interface).
18
+ */
19
+ export interface DescriptorProvider {
20
+ /** Returns the current signing descriptor. */
21
+ getSigningDescriptor(): string;
22
+ /** Checks if a descriptor belongs to this provider. */
23
+ isOurs(descriptor: string): boolean;
24
+ /** Signs transactions, each with its own descriptor-derived key. */
25
+ signWithDescriptor(requests: DescriptorSigningRequest[]): Promise<Transaction[]>;
26
+ /** Signs a message using the key derived from the descriptor. */
27
+ signMessageWithDescriptor(descriptor: string, message: Uint8Array, type?: "schnorr" | "ecdsa"): Promise<Uint8Array>;
28
+ }
@@ -46,4 +46,10 @@ export interface BatchSignableIdentity extends Identity {
46
46
  /** Type guard for identities that support batch signing. */
47
47
  export declare function isBatchSignable(identity: Identity): identity is BatchSignableIdentity;
48
48
  export * from "./singleKey";
49
- export * from "./seedIdentity";
49
+ export type { NetworkOptions, DescriptorOptions, SeedIdentityOptions, MnemonicOptions, } from "./seedIdentity";
50
+ export { SeedIdentity, MnemonicIdentity, ReadonlyDescriptorIdentity, } from "./seedIdentity";
51
+ export * from "./serialize";
52
+ export { isDescriptor, normalizeToDescriptor, extractPubKey, parseHDDescriptor, } from "./descriptor";
53
+ export type { ParsedHDDescriptor } from "./descriptor";
54
+ export type { DescriptorProvider, DescriptorSigningRequest, } from "./descriptorProvider";
55
+ export { StaticDescriptorProvider } from "./staticDescriptorProvider";
@@ -1,6 +1,7 @@
1
1
  import { Identity, ReadonlyIdentity } from ".";
2
2
  import { Transaction } from "../utils/transaction";
3
3
  import { SignerSession } from "../tree/signingSession";
4
+ import type { SerializedSigningIdentity, SerializedReadonlyIdentity } from "./serialize";
4
5
  /** Used for default BIP86 derivation with network selection. */
5
6
  export interface NetworkOptions {
6
7
  /**
@@ -27,14 +28,14 @@ export type MnemonicOptions = SeedIdentityOptions & {
27
28
  *
28
29
  * This is the recommended identity type for most applications. It uses
29
30
  * standard BIP86 (Taproot) derivation by default and stores an output
30
- * descriptor for interoperability with other wallets. The descriptor
31
- * format is HD-ready, allowing future support for multiple addresses
32
- * and change derivation.
31
+ * descriptor for interoperability with other wallets.
33
32
  *
34
33
  * Prefer this (or @see MnemonicIdentity) over `SingleKey` for new
35
34
  * integrations — `SingleKey` exists for backward compatibility with
36
35
  * raw nsec-style keys.
37
36
  *
37
+ * For descriptor-based signing, wrap with {@link StaticDescriptorProvider}.
38
+ *
38
39
  * @example
39
40
  * ```typescript
40
41
  * const seed = mnemonicToSeedSync(mnemonic);
@@ -50,7 +51,6 @@ export type MnemonicOptions = SeedIdentityOptions & {
50
51
  * ```
51
52
  */
52
53
  export declare class SeedIdentity implements Identity {
53
- protected readonly seed: Uint8Array;
54
54
  private readonly derivedKey;
55
55
  readonly descriptor: string;
56
56
  constructor(seed: Uint8Array, descriptor: string);
@@ -131,3 +131,40 @@ export declare class ReadonlyDescriptorIdentity implements ReadonlyIdentity {
131
131
  xOnlyPublicKey(): Promise<Uint8Array>;
132
132
  compressedPublicKey(): Promise<Uint8Array>;
133
133
  }
134
+ /**
135
+ * Serialize a seed-backed signing identity into a
136
+ * {@link SerializedSigningIdentity} envelope without exposing the
137
+ * underlying secret material on the public instance surface.
138
+ *
139
+ * Called by {@link serializeSigningIdentity}; application code should
140
+ * prefer that public dispatcher instead of calling this directly. This
141
+ * helper is deliberately kept out of the `src/identity` barrel so it is
142
+ * not part of the package's public export surface.
143
+ *
144
+ * Secret-surface trade-off: the resulting envelope carries master-seed
145
+ * material — the BIP39 mnemonic (+ optional passphrase) for
146
+ * `MnemonicIdentity` or the raw 64-byte seed for `SeedIdentity`. A party
147
+ * that reads this envelope can derive any key under the HD tree, not
148
+ * just the key currently in use. The pre-change `SingleKey` flow only
149
+ * shipped one derived private key and therefore had a smaller blast
150
+ * radius. This is an intentional design trade to preserve class and
151
+ * descriptor identity across the page / service-worker boundary; the
152
+ * page already holds the same material so that it can re-initialize a
153
+ * killed worker. Transport is same-origin `postMessage` only. See the
154
+ * threat-model note in `src/worker/browser/README.md`.
155
+ *
156
+ * @internal
157
+ */
158
+ export declare function serializeSeedOwnedSigningIdentity(identity: SeedIdentity): SerializedSigningIdentity;
159
+ /**
160
+ * Downgrade a seed-backed or descriptor-backed identity into a readonly
161
+ * descriptor envelope. Always produces a descriptor-only shape — secret
162
+ * material never crosses this path, even if the input is a signing
163
+ * identity.
164
+ *
165
+ * Deliberately kept out of the `src/identity` barrel; consumers should go
166
+ * through {@link serializeReadonlyIdentity}.
167
+ *
168
+ * @internal
169
+ */
170
+ export declare function serializeSeedOwnedReadonlyIdentity(identity: SeedIdentity | ReadonlyDescriptorIdentity): SerializedReadonlyIdentity;
@@ -0,0 +1,84 @@
1
+ import type { Identity, ReadonlyIdentity } from ".";
2
+ /**
3
+ * Tagged envelope for a signing identity transported across the
4
+ * service-worker boundary. All variants are structured-clone safe
5
+ * (plain strings only — no functions or prototypes).
6
+ *
7
+ * Adding a new variant is a source change in every worker build; keep
8
+ * old variants around until all deployed workers handle them.
9
+ */
10
+ export type SerializedSigningIdentity = {
11
+ type: "single-key";
12
+ privateKey: string;
13
+ } | {
14
+ type: "seed";
15
+ seed: string;
16
+ descriptor: string;
17
+ } | {
18
+ type: "mnemonic";
19
+ mnemonic: string;
20
+ descriptor: string;
21
+ passphrase?: string;
22
+ };
23
+ /**
24
+ * Tagged envelope for a readonly identity transported across the
25
+ * service-worker boundary. All variants are structured-clone safe.
26
+ */
27
+ export type SerializedReadonlyIdentity = {
28
+ type: "readonly-single-key";
29
+ publicKey: string;
30
+ } | {
31
+ type: "readonly-descriptor";
32
+ descriptor: string;
33
+ };
34
+ export type SerializedIdentity = SerializedSigningIdentity | SerializedReadonlyIdentity;
35
+ /** Type guard — true for signing envelopes, false for readonly envelopes. */
36
+ export declare function isSigningSerialized(s: SerializedIdentity): s is SerializedSigningIdentity;
37
+ /**
38
+ * Serialize a signing identity into a structured-clone safe envelope for
39
+ * transport across the service-worker boundary.
40
+ *
41
+ * Supports SDK-owned signing identities directly. For custom identities, a
42
+ * duck-typed `toHex()` fallback preserves compatibility with existing
43
+ * `SingleKey`-like implementations.
44
+ */
45
+ export declare function serializeSigningIdentity(identity: Identity): SerializedSigningIdentity;
46
+ /**
47
+ * Serialize a readonly identity into a structured-clone safe envelope.
48
+ *
49
+ * Works for any `ReadonlyIdentity` via `compressedPublicKey()`. When called
50
+ * with a signing identity, produces a readonly envelope (never ships signing
51
+ * material) — callers that need to preserve signing capability across the
52
+ * boundary must use {@link serializeSigningIdentity}.
53
+ */
54
+ export declare function serializeReadonlyIdentity(identity: ReadonlyIdentity): Promise<SerializedReadonlyIdentity>;
55
+ /**
56
+ * Rehydrate a serialized identity envelope back into an identity instance.
57
+ * The return type is the union of signing and readonly; use
58
+ * {@link isSigningSerialized} on the envelope before hydration if the caller
59
+ * needs to know which side it ends up on.
60
+ */
61
+ export declare function hydrateIdentity(s: SerializedIdentity): Identity | ReadonlyIdentity;
62
+ /**
63
+ * Legacy untagged shape emitted by page builds prior to the tagged
64
+ * SerializedIdentity envelope. Retained so newer workers can still accept
65
+ * older pages during a rolling upgrade. Slated for removal in the next major.
66
+ *
67
+ * @deprecated Use {@link SerializedIdentity}.
68
+ */
69
+ export type LegacySerializedIdentity = {
70
+ privateKey: string;
71
+ } | {
72
+ publicKey: string;
73
+ };
74
+ /**
75
+ * Accept either a modern {@link SerializedIdentity} envelope or a legacy
76
+ * `{ privateKey }` / `{ publicKey }` shape and normalize to a
77
+ * {@link SerializedIdentity}. Emits a one-time deprecation warning when a
78
+ * legacy shape is seen.
79
+ *
80
+ * Intended for the worker-side boundary; new page builds always emit tagged
81
+ * envelopes via {@link serializeSigningIdentity} /
82
+ * {@link serializeReadonlyIdentity}.
83
+ */
84
+ export declare function normalizeSerializedIdentity(shape: SerializedIdentity | LegacySerializedIdentity): SerializedIdentity;
@@ -0,0 +1,18 @@
1
+ import { Identity } from ".";
2
+ import { DescriptorProvider, DescriptorSigningRequest } from "./descriptorProvider";
3
+ import { Transaction } from "../utils/transaction";
4
+ /**
5
+ * Wraps a legacy Identity (single-key) as a DescriptorProvider.
6
+ * The descriptor is always a simple tr(pubkey) format.
7
+ */
8
+ export declare class StaticDescriptorProvider implements DescriptorProvider {
9
+ private readonly identity;
10
+ private readonly descriptor;
11
+ private readonly pubKeyHex;
12
+ constructor(identity: Identity, pubKeyHex: string);
13
+ static create(identity: Identity): Promise<StaticDescriptorProvider>;
14
+ getSigningDescriptor(): string;
15
+ isOurs(descriptor: string): boolean;
16
+ signWithDescriptor(requests: DescriptorSigningRequest[]): Promise<Transaction[]>;
17
+ signMessageWithDescriptor(descriptor: string, message: Uint8Array, type?: "schnorr" | "ecdsa"): Promise<Uint8Array>;
18
+ }
@@ -22,6 +22,8 @@ import type { MessageTimeouts } from "./wallet/serviceWorker/wallet";
22
22
  import { OnchainWallet } from "./wallet/onchain";
23
23
  import { setupServiceWorker } from "./worker/browser/utils";
24
24
  import { ESPLORA_URL, EsploraProvider, OnchainProvider, ExplorerTransaction } from "./providers/onchain";
25
+ import { ElectrumOnchainProvider, WsElectrumChainSource } from "./providers/electrum";
26
+ import type { TransactionHistory as ElectrumTransactionHistory, BlockHeader as ElectrumBlockHeader, Unspent as ElectrumUnspent } from "./providers/electrum";
25
27
  import { RestArkProvider, ArkProvider, SettlementEvent, SettlementEventType, ArkInfo, SignedIntent, Output, TxNotification, BatchFinalizationEvent, BatchFinalizedEvent, BatchFailedEvent, TreeSigningStartedEvent, TreeNoncesEvent, BatchStartedEvent, TreeTxEvent, TreeSignatureEvent, ScheduledSession, FeeInfo } from "./providers/ark";
26
28
  import { DelegatorProvider, DelegateInfo, DelegateOptions, RestDelegatorProvider } from "./providers/delegator";
27
29
  import { CLTVMultisigTapscript, ConditionCSVMultisigTapscript, ConditionMultisigTapscript, CSVMultisigTapscript, decodeTapscript, MultisigTapscript, TapscriptType, ArkTapscript, RelativeTimelock } from "./script/tapscript";
@@ -50,5 +52,5 @@ import { IContractManager } from "./contracts/contractManager";
50
52
  import { closeDatabase, openDatabase } from "./repositories/indexedDB/manager";
51
53
  import { WalletMessageHandler, WalletNotInitializedError, ReadonlyWalletError, DelegatorNotConfiguredError } from "./wallet/serviceWorker/wallet-message-handler";
52
54
  import { MESSAGE_BUS_NOT_INITIALIZED, MessageBusNotInitializedError, ServiceWorkerTimeoutError } from "./worker/errors";
53
- export { Wallet, ReadonlyWallet, SingleKey, ReadonlySingleKey, SeedIdentity, MnemonicIdentity, ReadonlyDescriptorIdentity, isBatchSignable, 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, MESSAGE_BUS_NOT_INITIALIZED, MessageBusNotInitializedError, ServiceWorkerTimeoutError, ServiceWorkerWallet, ServiceWorkerReadonlyWallet, DEFAULT_MESSAGE_TIMEOUTS, 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, };
54
- export type { Identity, ReadonlyIdentity, BatchSignableIdentity, SignRequest, 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, MessageTimeouts, IDelegatorManager, DelegatorProvider, DelegateInfo, DelegateOptions, WalletRepository, ContractRepository, MigrationStatus, };
55
+ export { Wallet, ReadonlyWallet, SingleKey, ReadonlySingleKey, SeedIdentity, MnemonicIdentity, ReadonlyDescriptorIdentity, isBatchSignable, OnchainWallet, Ramps, VtxoManager, DelegatorManagerImpl, RestDelegatorProvider, ESPLORA_URL, EsploraProvider, ElectrumOnchainProvider, WsElectrumChainSource, RestArkProvider, RestIndexerProvider, ArkAddress, DefaultVtxo, DelegateVtxo, VtxoScript, VHTLC, TxType, IndexerTxType, ChainTxType, SettlementEventType, setupServiceWorker, MessageBus, WalletMessageHandler, WalletNotInitializedError, ReadonlyWalletError, DelegatorNotConfiguredError, MESSAGE_BUS_NOT_INITIALIZED, MessageBusNotInitializedError, ServiceWorkerTimeoutError, ServiceWorkerWallet, ServiceWorkerReadonlyWallet, DEFAULT_MESSAGE_TIMEOUTS, 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, };
56
+ export type { Identity, ReadonlyIdentity, BatchSignableIdentity, SignRequest, 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, ElectrumTransactionHistory, ElectrumBlockHeader, ElectrumUnspent, 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, MessageTimeouts, IDelegatorManager, DelegatorProvider, DelegateInfo, DelegateOptions, WalletRepository, ContractRepository, MigrationStatus, };
@@ -0,0 +1,212 @@
1
+ import type { ElectrumWS } from "ws-electrumx-client";
2
+ import type { Network } from "../networks";
3
+ import type { Coin } from "../wallet";
4
+ import type { ExplorerTransaction, OnchainProvider } from "./onchain";
5
+ export type TransactionHistory = {
6
+ tx_hash: string;
7
+ height: number;
8
+ fee?: number;
9
+ };
10
+ export type BlockHeader = {
11
+ height: number;
12
+ hex: string;
13
+ };
14
+ export type Unspent = {
15
+ txid: string;
16
+ vout: number;
17
+ witnessUtxo: {
18
+ script: Uint8Array;
19
+ value: bigint;
20
+ };
21
+ };
22
+ type VerboseTransaction = {
23
+ txid: string;
24
+ confirmations: number;
25
+ blockhash?: string;
26
+ blocktime?: number;
27
+ time?: number;
28
+ /** Raw transaction hex. Bitcoin Core's getrawtransaction <tx> 1 always
29
+ * includes this; we use it to derive exact satoshi amounts instead of
30
+ * multiplying the floating-point `value` field by 1e8. */
31
+ hex?: string;
32
+ vout: {
33
+ n: number;
34
+ value: number;
35
+ scriptPubKey: {
36
+ addresses?: string[];
37
+ address?: string;
38
+ hex: string;
39
+ };
40
+ }[];
41
+ vin: {
42
+ txid: string;
43
+ vout: number;
44
+ }[];
45
+ };
46
+ type HeaderSubscribeResult = {
47
+ height: number;
48
+ hex: string;
49
+ };
50
+ /**
51
+ * WebSocket-based Electrum chain source using ws-electrumx-client.
52
+ * Provides low-level methods for the Electrum protocol.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * import { ElectrumWS } from "ws-electrumx-client";
57
+ * import { WsElectrumChainSource } from "./providers/electrum";
58
+ * import { networks } from "./networks";
59
+ *
60
+ * const ws = new ElectrumWS("wss://electrum.blockstream.info:50004");
61
+ * const chain = new WsElectrumChainSource(ws, networks.bitcoin);
62
+ *
63
+ * const history = await chain.fetchHistories([script]);
64
+ * await chain.close();
65
+ * ```
66
+ */
67
+ export declare class WsElectrumChainSource {
68
+ private ws;
69
+ private network;
70
+ private cachedTip;
71
+ private headersSubscribePromise;
72
+ constructor(ws: ElectrumWS, network: Network);
73
+ fetchTransactions(txids: string[]): Promise<{
74
+ txID: string;
75
+ hex: string;
76
+ }[]>;
77
+ fetchVerboseTransaction(txid: string): Promise<VerboseTransaction>;
78
+ fetchVerboseTransactions(txids: string[]): Promise<VerboseTransaction[]>;
79
+ unsubscribeScriptStatus(script: Uint8Array): Promise<void>;
80
+ subscribeScriptStatus(script: Uint8Array, callback: (scripthash: string, status: string | null) => void): Promise<void>;
81
+ fetchHistories(scripts: Uint8Array[]): Promise<TransactionHistory[][]>;
82
+ fetchHistory(script: Uint8Array): Promise<TransactionHistory[]>;
83
+ fetchBlockHeaders(heights: number[]): Promise<BlockHeader[]>;
84
+ fetchBlockHeader(height: number): Promise<BlockHeader>;
85
+ /**
86
+ * Returns the current chain tip and keeps it fresh via a single
87
+ * server-side subscription. Subsequent calls return the cached tip
88
+ * (updated by background notifications) without round-tripping to the
89
+ * server. Previously each call issued `blockchain.headers.subscribe` as
90
+ * a regular request, leaving a stale subscription on the server every
91
+ * time — under polling that adds up. ws-electrumx-client deduplicates
92
+ * `subscribe()` by method+params, so registering once is enough.
93
+ */
94
+ subscribeHeaders(): Promise<HeaderSubscribeResult>;
95
+ estimateFees(targetNumberBlocks: number): Promise<number>;
96
+ broadcastTransaction(txHex: string): Promise<string>;
97
+ /**
98
+ * Submit a package of raw transactions atomically via Fulcrum's
99
+ * `blockchain.transaction.broadcast_package` method, the on-the-wire
100
+ * equivalent of bitcoind's `submitpackage` RPC.
101
+ *
102
+ * Required for TRUC (BIP 431) 1P1C relay where the parent has zero
103
+ * (or below-minfee) fee and depends on the child to pay for both via
104
+ * CPFP — sequential broadcast cannot work in that case because the
105
+ * parent would be rejected from the mempool on its own.
106
+ *
107
+ * @param txHexes - Topologically sorted raw transactions; child must
108
+ * be the last element. Currently must be a 1P1C pair
109
+ * (length 2). Parents may not depend on each other.
110
+ * @returns The child transaction id (the last entry in the array),
111
+ * computed locally — `broadcast_package` itself returns
112
+ * `{success, errors}` rather than a txid.
113
+ * @throws If the server does not implement `broadcast_package` (e.g.
114
+ * ElectrumX, or older Fulcrum, or Fulcrum backed by bitcoind
115
+ * < v28.0.0). Callers must surface this clearly to users —
116
+ * this method does NOT silently fall back to sequential
117
+ * broadcasts because doing so would let TRUC packages fail
118
+ * in subtle ways.
119
+ * @throws If the server returns `success=false`, surfacing the
120
+ * underlying mempool rejection in the error message.
121
+ */
122
+ broadcastPackage(txHexes: string[]): Promise<string>;
123
+ getRelayFee(): Promise<number>;
124
+ close(): Promise<void>;
125
+ waitForAddressReceivesTx(addr: string): Promise<void>;
126
+ listUnspents(addr: string): Promise<Unspent[]>;
127
+ /**
128
+ * Get the address string for a script output, if decodable.
129
+ */
130
+ addressForScript(scriptHex: string): string | undefined;
131
+ }
132
+ /**
133
+ * Electrum-based implementation of the OnchainProvider interface.
134
+ * Replaces esplora polling with electrum subscriptions where possible.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * import { ElectrumWS } from "ws-electrumx-client";
139
+ * import { ElectrumOnchainProvider } from "./providers/electrum";
140
+ * import { networks } from "./networks";
141
+ *
142
+ * const ws = new ElectrumWS("wss://electrum.blockstream.info:50004");
143
+ * const provider = new ElectrumOnchainProvider(ws, networks.bitcoin);
144
+ *
145
+ * const coins = await provider.getCoins("bc1q...");
146
+ * ```
147
+ */
148
+ export declare class ElectrumOnchainProvider implements OnchainProvider {
149
+ private ws;
150
+ private network;
151
+ private chain;
152
+ constructor(ws: ElectrumWS, network: Network);
153
+ getCoins(address: string): Promise<Coin[]>;
154
+ getFeeRate(): Promise<number | undefined>;
155
+ /**
156
+ * Broadcast a single transaction or a TRUC (BIP 431) 1P1C package
157
+ * atomically.
158
+ *
159
+ * **Server requirements for 1P1C packages:** the backing Electrum
160
+ * server must implement `blockchain.transaction.broadcast_package`
161
+ * (Fulcrum ≥ 1.10) and be backed by bitcoind ≥ v28.0.0. ElectrumX
162
+ * does not implement this method. There is **no fallback** to
163
+ * sequential parent-then-child broadcast: TRUC packages typically
164
+ * have a zero-fee parent and would be rejected from the mempool on
165
+ * their own, so a fallback would silently fail in subtle ways.
166
+ * Callers receiving a "method not found" error here should route
167
+ * through a different provider for that submission.
168
+ *
169
+ * @param txs - One transaction (single broadcast) or two
170
+ * topologically-sorted transactions (parent first,
171
+ * child last) for 1P1C package relay.
172
+ * @returns The broadcast txid (or the child txid for 1P1C packages).
173
+ */
174
+ broadcastTransaction(...txs: string[]): Promise<string>;
175
+ getTxOutspends(txid: string): Promise<{
176
+ spent: boolean;
177
+ txid: string;
178
+ }[]>;
179
+ getTransactions(address: string): Promise<ExplorerTransaction[]>;
180
+ /**
181
+ * Map an electrum verbose transaction to the ExplorerTransaction shape.
182
+ *
183
+ * Output values are derived from the raw transaction hex when available,
184
+ * never from the floating-point `value` field returned by the daemon.
185
+ * That field has 8 decimal places and `Math.round(value * 1e8)` is safe
186
+ * in the common case but a footgun for protocol-level money handling —
187
+ * the raw bytes are exact.
188
+ */
189
+ private verboseToExplorer;
190
+ /**
191
+ * Decode `address` into its scriptPubKey, throwing a clear error if the
192
+ * input is malformed. @scure/btc-signer raises a generic decode error
193
+ * which is hard to map back to user input — this wraps it.
194
+ */
195
+ private encodeAddress;
196
+ getTxStatus(txid: string): Promise<{
197
+ confirmed: false;
198
+ } | {
199
+ confirmed: true;
200
+ blockTime: number;
201
+ blockHeight: number;
202
+ }>;
203
+ getChainTip(): Promise<{
204
+ height: number;
205
+ time: number;
206
+ hash: string;
207
+ }>;
208
+ watchAddresses(addresses: string[], eventCallback: (txs: ExplorerTransaction[]) => void): Promise<() => void>;
209
+ /** Close the underlying WebSocket connection. */
210
+ close(): Promise<void>;
211
+ }
212
+ export {};
@@ -5,3 +5,4 @@
5
5
  * where events arrive before iteration begins.
6
6
  */
7
7
  export declare function eventSourceIterator(eventSource: EventSource): AsyncGenerator<MessageEvent, void, unknown>;
8
+ export declare function isEventSourceError(error: unknown): error is Error;
@@ -18,11 +18,19 @@ export declare const DEFAULT_MESSAGE_TAG = "WALLET_UPDATER";
18
18
  export type RequestInitWallet = RequestEnvelope & {
19
19
  type: "INIT_WALLET";
20
20
  payload: {
21
- key: {
21
+ /**
22
+ * Legacy per-request key material. Ignored by the current handler —
23
+ * identity hydration happens during INITIALIZE_MESSAGE_BUS. Retained
24
+ * for wire compatibility with older workers that may still read it.
25
+ * Slated for removal in the next major.
26
+ *
27
+ * @deprecated Identity is now carried by INITIALIZE_MESSAGE_BUS.
28
+ */
29
+ key?: {
22
30
  privateKey: string;
23
31
  } | {
24
32
  publicKey: string;
25
- };
33
+ } | {};
26
34
  arkServerUrl: string;
27
35
  arkServerPublicKey?: string;
28
36
  };
@@ -481,6 +489,7 @@ export declare class WalletMessageHandler implements MessageHandler<WalletUpdate
481
489
  private scheduleForNextTick;
482
490
  private requireWallet;
483
491
  private tagged;
492
+ isLongRunning(message: WalletUpdaterRequest): boolean;
484
493
  handleMessage(message: WalletUpdaterRequest): Promise<WalletUpdaterResponse>;
485
494
  private handleInitWallet;
486
495
  private handleGetBalance;
@@ -1,6 +1,6 @@
1
1
  import { IWallet, WalletBalance, SendBitcoinParams, SettleParams, ArkTransaction, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter, StorageConfig, IReadonlyWallet, IReadonlyAssetManager, IAssetManager, Recipient } from "..";
2
2
  import { SettlementEvent } from "../../providers/ark";
3
- import { Identity, ReadonlyIdentity } from "../../identity";
3
+ import { Identity, ReadonlyIdentity, type SerializedIdentity, type LegacySerializedIdentity } from "../../identity";
4
4
  import { WalletRepository } from "../../repositories/walletRepository";
5
5
  import { ContractRepository } from "../../repositories/contractRepository";
6
6
  import { RequestInitWallet, ResponseGetStatus, WalletUpdaterRequest, WalletUpdaterResponse } from "./wallet-message-handler";
@@ -11,9 +11,6 @@ import type { ContractWatcherConfig } from "../../contracts/contractWatcher";
11
11
  type RequestType = WalletUpdaterRequest["type"];
12
12
  export type MessageTimeouts = Partial<Record<RequestType, number>>;
13
13
  export declare const DEFAULT_MESSAGE_TIMEOUTS: Readonly<Record<RequestType, number>>;
14
- type PrivateKeyIdentity = Identity & {
15
- toHex(): string;
16
- };
17
14
  /**
18
15
  * Service Worker-based wallet implementation for browser environments.
19
16
  *
@@ -102,11 +99,7 @@ export type ServiceWorkerWalletSetupOptions = ServiceWorkerWalletOptions & {
102
99
  serviceWorkerActivationTimeoutMs?: number;
103
100
  };
104
101
  type MessageBusInitConfig = {
105
- wallet: {
106
- privateKey: string;
107
- } | {
108
- publicKey: string;
109
- };
102
+ wallet: SerializedIdentity | LegacySerializedIdentity;
110
103
  arkServer: {
111
104
  url: string;
112
105
  publicKey?: string;
@@ -117,6 +110,7 @@ type MessageBusInitConfig = {
117
110
  timeoutMs?: number;
118
111
  settlementConfig?: SettlementConfig | false;
119
112
  watcherConfig?: Partial<Omit<ContractWatcherConfig, "indexerProvider">>;
113
+ messageTimeouts?: Record<string, number>;
120
114
  };
121
115
  export declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet {
122
116
  readonly serviceWorker: ServiceWorker;
@@ -129,6 +123,13 @@ export declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet {
129
123
  protected initWalletPayload: RequestInitWallet["payload"] | null;
130
124
  protected messageBusTimeoutMs?: number;
131
125
  protected messageTimeouts: Record<RequestType, number>;
126
+ protected arkServerUrl?: string;
127
+ protected arkServerPublicKey?: string;
128
+ protected delegatorUrl?: string;
129
+ protected indexerUrl?: string;
130
+ protected esploraUrl?: string;
131
+ protected watcherConfig?: Partial<Omit<ContractWatcherConfig, "indexerProvider">>;
132
+ protected settlementConfig?: SettlementConfig | false;
132
133
  private reinitPromise;
133
134
  private pingPromise;
134
135
  private inflightRequests;
@@ -165,6 +166,21 @@ export declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet {
165
166
  private pingServiceWorker;
166
167
  private sendMessageWithRetry;
167
168
  protected sendMessageWithEvents(request: WalletUpdaterRequest, onEvent: (response: WalletUpdaterResponse) => void, isComplete: (response: WalletUpdaterResponse) => boolean): Promise<WalletUpdaterResponse>;
169
+ /**
170
+ * Produce a serialized envelope for the wallet's identity. The base
171
+ * class always emits a readonly envelope; `ServiceWorkerWallet`
172
+ * overrides to emit a signing envelope.
173
+ */
174
+ protected serializeIdentity(): Promise<SerializedIdentity>;
175
+ /**
176
+ * Return the cached init config, or rebuild one from live instance
177
+ * state when the cache was never populated. Recovery path for
178
+ * SDK-factory-created wallets; manual constructor bypasses do not
179
+ * retain enough state here and will hit the "never initialized" throw.
180
+ */
181
+ protected buildInitConfig(): Promise<MessageBusInitConfig>;
182
+ /** Minimal INIT_WALLET payload used on reinitialize when the cache is gone. */
183
+ protected buildInitWalletPayload(): RequestInitWallet["payload"];
168
184
  private reinitialize;
169
185
  /** Clear cached wallet state from both the page and service worker storage. */
170
186
  clear(): Promise<void>;
@@ -195,8 +211,9 @@ export declare class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet imp
195
211
  readonly identity: Identity;
196
212
  private readonly _assetManager;
197
213
  private readonly hasDelegator;
198
- protected constructor(serviceWorker: ServiceWorker, identity: PrivateKeyIdentity, walletRepository: WalletRepository, contractRepository: ContractRepository, messageTag: string, hasDelegator: boolean);
214
+ protected constructor(serviceWorker: ServiceWorker, identity: Identity, walletRepository: WalletRepository, contractRepository: ContractRepository, messageTag: string, hasDelegator: boolean);
199
215
  get assetManager(): IAssetManager;
216
+ protected serializeIdentity(): Promise<SerializedIdentity>;
200
217
  static create(options: ServiceWorkerWalletCreateOptions): Promise<ServiceWorkerWallet>;
201
218
  /**
202
219
  * Simplified setup method that handles service worker registration
@@ -224,6 +224,9 @@ export declare class VtxoManager implements AsyncDisposable, IVtxoManager {
224
224
  private consecutivePeriodicSettleFailures;
225
225
  private static readonly PERIODIC_SETTLE_COOLDOWN_MS;
226
226
  private static readonly PERIODIC_SETTLE_MAX_BACKOFF_MS;
227
+ private lastVtxoSpentRefreshTimestamp;
228
+ private vtxoSpentRefreshPromise?;
229
+ private static readonly VTXO_SPENT_REFRESH_COOLDOWN_MS;
227
230
  constructor(wallet: IWallet,
228
231
  /** @deprecated Use settlementConfig instead */
229
232
  renewalConfig?: RenewalConfig | undefined, settlementConfig?: SettlementConfig | false);
@@ -397,11 +400,23 @@ export declare class VtxoManager implements AsyncDisposable, IVtxoManager {
397
400
  private getBoardingOutputScript;
398
401
  /** Returns the onchain provider for fee estimation and broadcasting. */
399
402
  private getOnchainProvider;
403
+ /** Returns the Ark provider for intent fee and server info lookups. */
404
+ private getArkProvider;
400
405
  /** Returns the Bitcoin network configuration from the wallet. */
401
406
  private getNetwork;
402
407
  /** Returns the wallet's identity for transaction signing. */
403
408
  private getIdentity;
404
409
  private initializeSubscription;
410
+ /**
411
+ * VTXO_ALREADY_SPENT means the server's authoritative view of VTXO state
412
+ * is ahead of ours — cross-instance race, pre-lock snapshot drift, or an
413
+ * SSE gap left stale data in the local cache. Silent-swallowing guarantees
414
+ * the same error on the next cycle because nothing reconciles the cache,
415
+ * so instead we trigger a full refreshVtxos() to advance the global sync
416
+ * cursor. Throttled to prevent a buggy indexer from causing a refresh
417
+ * storm.
418
+ */
419
+ private maybeRefreshAfterVtxoSpent;
405
420
  /** Computes the next poll delay, applying exponential backoff on failures. */
406
421
  private getNextPollDelay;
407
422
  /**
@@ -44,6 +44,7 @@ export declare class ReadonlyWallet implements IReadonlyWallet {
44
44
  protected readonly watcherConfig?: ReadonlyWalletConfig["watcherConfig"];
45
45
  private readonly _assetManager;
46
46
  private _syncVtxosInflight?;
47
+ protected _pendingSpendOutpoints: Set<string>;
47
48
  get assetManager(): IReadonlyAssetManager;
48
49
  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"]);
49
50
  /**
@@ -217,6 +218,8 @@ export declare class Wallet extends ReadonlyWallet implements IWallet {
217
218
  * same VTXO inputs.
218
219
  */
219
220
  private _txLock;
221
+ private _addPendingSpends;
222
+ private _removePendingSpends;
220
223
  private _withTxLock;
221
224
  /** @deprecated Use settlementConfig instead */
222
225
  readonly renewalConfig: Required<Omit<WalletConfig["renewalConfig"], "enabled">> & {