import { a5 as ReadonlyIdentity, a as Identity, at as DescriptorSigningRequest, ap as Transaction, ao as SignerSession, a3 as DescriptorProvider, au as IntentFeeConfig, av as OffchainInput, aw as FeeAmount, ax as OnchainInput, ay as FeeOutput, D as IntentRepository, F as ArkIntent, H as IntentFilter, O as Outpoint, J as VirtualTxRepository, K as VirtualTx, L as VtxoBranch, W as WalletRepository, x as ExtendedVirtualCoin, y as VtxoRepositoryKey, E as ExtendedCoin, A as ArkTransaction, z as WalletState, w as Contract, C as ContractRepository, B as ContractFilter, I as IWallet, r as ArkProvider, am as SettlementConfig, az as ContractWatcherConfig, aA as Asset, R as Recipient, aB as FeeInfo, l as SettlementEvent, N as NormalizedExtendedVirtualCoin, k as SettleParams, S as SendBitcoinParams, G as GetVtxosFilter, d as GetSpendableVtxosFilter, aC as CreateContractParams, aD as GetContractsFilter, v as VirtualCoin, aE as GetSpendablePathsOptions, aF as GetAllSpendingPathsOptions, h as GetNewAddressesOptions, aG as IssuanceParams, aH as ReissuanceParams, aI as BurnParams, aJ as RenewVtxosOptions, c as WalletBalance, ac as ContractSyncState, ah as ContractWithVtxos, aK as WatchedScript, aL as PathSelection, i as NewAddress, aM as ContractEvent, aN as AssetDetails, aO as IssuanceResult, aP as DelegateInfo, aQ as MigrationGlobalSkipReason, aR as MigrationLegSkipReason, aS as SignerStatus, a4 as IReadonlyWallet, e as ActivityRegistry, ab as IReadonlyAssetManager, aT as StorageConfig, f as Activity, g as IContractManager, n as IAssetManager, m as SendParams, j as IDelegateManager, aU as IVtxoManager, a6 as OnchainProvider, P as Network, ae as NetworkName, aj as Coin, aV as ExplorerTransaction, af as ArkInfo, aq as SignedIntent, ar as Intent, aW as TreeNonces, aX as TreePartialSigs, T as TxNotificationEvent, aY as PendingTx, u as TapLeafScript, aZ as EncodedVtxoScript, Q as CSVMultisigTapscript, t as RelativeTimelock, V as VtxoScript, a_ as Status, ag as ArkAddress, a$ as ChainTx, s as IndexerProvider, Y as TxTree, b0 as PathContext } from './index-Ctul0N3Y.js'; export { b1 as ActivityIntent, b2 as ActivityResolver, b3 as ArkIntentState, b4 as ArkTapscript, b5 as AssetMetadata, b6 as BITCOIN_EMULATOR_PUBKEY, b7 as BaseWalletConfig, a0 as BatchFailedEvent, _ as BatchFinalizationEvent, $ as BatchFinalizedEvent, b8 as BatchInfo, b9 as BatchSignableIdentity, U as BatchStartedEvent, ba as CLTVMultisigTapscript, bb as CandidateDeps, bc as ChainTxType, M as ChainedTxType, bd as CommitmentTx, be as ConditionCSVMultisigTapscript, bf as ConditionMultisigTapscript, bg as ContractBalance, bh as ContractEventCallback, bi as ContractHandler, ak as ContractManager, al as ContractManagerConfig, bj as ContractState, bk as ContractVtxo, bl as ContractVtxoEvent, bm as ContractWatcher, bn as DEFAULT_VTXO_PAGE_SIZE, bo as DelegateManagerImpl, bp as DelegateOptions, a7 as DelegateProvider, bq as DelegatorManagerImpl, br as DelegatorProvider, bs as DeprecatedSignerMigrationReport, bt as DeprecatedSignerReport, bu as DigestMismatchError, bv as Discoverable, bw as DiscoveredContract, bx as DiscoveryDeps, by as ESPLORA_URL, bz as EsploraProvider, bA as EventSourceCapable, bB as EventSourceFactory, bC as EventSourceLike, bD as EventSourceUnavailableError, a9 as ExitCaptureMode, bE as ExitChainResolver, aa as ExitDataSource, bF as ExtendedContractVtxo, bG as GroupMembership, bH as HandlerError, bI as IDelegatorManager, bJ as INTENT_TERMINAL_STATES, bK as IndexerTxType, bL as IntentCoin, bM as KnownMetadata, bN as MUTINYNET_EMULATOR_PUBKEY, bO as MigrateDeprecatedSignerOptions, bP as MigrationLegReport, bQ as MigrationVtxoRef, bR as MultisigTapscript, bS as NewAddressType, bT as Nonces, bU as Output, bV as PageResponse, bW as PaginationOptions, bX as PartialSig, bY as PrevTxSource, bZ as PrevTxUnavailableError, b_ as ProviderClass, b$ as REGTEST_EMULATOR_PUBKEY, c0 as RawTxSource, a8 as ReadonlyWalletConfig, o as RestArkProvider, c1 as RestDelegateProvider, c2 as RestDelegatorProvider, p as RestIndexerProvider, c3 as ScanContractsOptions, c4 as ScanResult, c5 as ScheduledSession, c6 as SettlementEventType, c7 as SignRequest, c8 as SignerClassification, c9 as SignerSet, ca as SubscriptionEvent, cb as SubscriptionHeartbeat, q as SubscriptionResponse, cc as SweepTxNotification, cd as TapLeaves, ce as TapTreeCoder, cf as TapscriptType, as as TimeHeight, Z as TreeNoncesEvent, a2 as TreeSignatureEvent, X as TreeSigningStartedEvent, a1 as TreeTxEvent, cg as Tx, ch as TxHistoryRecord, ci as TxKey, cj as TxNotification, ck as TxTag, cl as TxTreeNode, cm as TxType, cn as VirtualStatus, co as Vtxo, cp as VtxoChain, an as VtxoManager, cq as VtxoType, b as WalletConfig, cr as WalletMode, cs as WithPrevTx, ct as assertAllowedSighashTypes, cu as assetMintResolver, cv as attachPrevArkTxs, cw as attachPrevoutTxs, cx as boardingResolver, cy as canRecoverOnchain, cz as canSpendOffchain, cA as canSweepOnchain, cB as classifyAgainstSignerSet, cC as classifyContractSigner, cD as collabExitResolver, cE as configureEventSource, cF as createDefaultActivityRegistry, cG as createExitChainResolver, cH as decodeTapscript, cI as defaultEmulatorPubkey, cJ as getConfiguredEventSource, cK as getNetwork, cL as getRandomId, cM as getSequence, cN as hasCandidates, cO as hasTerminalSpend, cP as isBatchSignable, cQ as isContractVtxoEvent, cR as isCooperativelyMigratable, cS as isDiscoverable, cT as isEventSourceUnavailableError, cU as isExpired, cV as isPastExpiry, cW as isRecoverable, cX as isSigningIdentity, cY as isSpendable, cZ as isSubdust, c_ as isTerminalIntentState, c$ as isVirtualCoin, d0 as isVtxoExpiringSoon, d1 as isVtxoSpent, d2 as networks, d3 as resolveEmulatorPubkey, d4 as resolveEventSource, d5 as resolvePrevTxs, d6 as scriptFromTapLeafScript, d7 as signerSetFromInfo, d8 as toXOnlySignerHex, d9 as withPrevTxs } from './index-Ctul0N3Y.js'; import { W as Wallet, R as ReadonlyWallet, b as ReceiveRotatorFactory, c as ReceiveRotatorBootOpts, d as ReceiveRotatorBoot, P as ProviderConnectionState, H as HDWalletCapable, a as HDAllocationCapable, A as AddressAllocationCapable, B as BatchExpiryPolicy, e as Batch } from './hdWalletCapable-DJClhYMC.js'; export { f as AbortError, g as ArkError, h as ArkErrorName, i as ArkadeCashClaimResult, j as ArkadeCashCreateError, k as ArkadeCashUnclaimedReason, l as ArkadeCashUnclaimedVtxo, m as BoardingUtxoGroup, C as CheckpointExitDelayPolicy, D as DEFAULT_MIN_BATCH_EXPIRY_SECONDS, n as DEFAULT_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, F as ForeignDescriptorError, I as IncomingFunds, M as MAX_USED_SIGNING_DESCRIPTORS_LOOK_AHEAD, o as MUTINYNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, p as ProviderKind, q as ProviderUnavailableError, r as REGTEST_MIN_BATCH_EXPIRY_SECONDS, s as REGTEST_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, S as SIGNET_MIN_CHECKPOINT_EXIT_DELAY_SECONDS, t as ServerInfoSource, u as ServerResponseMismatchError, v as WaitForIncomingFundsOptions, w as WalletCannotAllocateAddressError, x as assertValidBatchExpiry, y as assertValidServerUnrollScript, z as defaultBatchExpiryPolicy, E as defaultCheckpointExitDelayPolicy, G as isAddressAllocationCapable, J as isArkError, K as isHDAllocationCapable, L as isHDWalletCapable, N as maybeArkError, O as resolveBatchExpiryPolicy, Q as resolveCheckpointExitDelayPolicy, T as resolveDescriptorSigner, U as selectVirtualCoins, V as signingDescriptorIndex, X as strictSigningDescriptorIndex, Y as waitForIncomingFunds } from './hdWalletCapable-DJClhYMC.js'; import { P as Program, A as ArkadeParamValue, a as ArkadeProgramScript, b as ProgramKeys, c as ArkadeArgValue, C as CompiledProgramFunction, d as ArkadeFunction, I as InputRef, e as InputDef, f as ArkadeValueType, g as ArkadeArgType, h as ArkadeContractParams, i as ArkadeSegment, j as AsmToken, S as SUPPORTED_PROGRAM_VERSION, k as SignerRef, T as TapscriptSegment, l as TweakedSigner, W as WitnessRef, m as deserializeArkadeContractParams, p as parseArtifact, r as resolveAsm, s as serializeArkadeContractParams, n as stringifyArtifact, v as validateProgram, w as witnessRefToBytes } from './index-D0qe2qVY.js'; export { o as ArkadeContractHandler, B as BoardingContractHandler, q as BoardingContractParams, D as DefaultContractHandler, t as DefaultContractParams, u as DelegateContractHandler, x as DelegateContractParams, V as VHTLC, y as VHTLCContractHandler, z as VHTLCContractParams, E as VHTLCV2ContractHandler, F as VHTLCV2ContractParams, G as contractHandlers } from './index-D0qe2qVY.js'; import { D as DefaultVtxo } from './delegate-C9lpdrb0.js'; export { a as DelegateVtxo } from './delegate-C9lpdrb0.js'; import { Bytes, BTC_NETWORK } from '@scure/btc-signer/utils.js'; import { P2TR } from '@scure/btc-signer/payment.js'; import { Transaction as Transaction$1, OP, Script, ScriptType } from '@scure/btc-signer'; import { ElectrumWS } from 'ws-electrumx-client'; import { TransactionOutput, TransactionInputUpdate } from '@scure/btc-signer/psbt.js'; import { S as StorageAdapter } from './index-C0IanN1m.js'; import * as P from 'micro-packed'; import '@scure/btc-signer/transaction.js'; /** * Tagged envelope for a signing identity transported across the * service-worker boundary. All variants are structured-clone safe * (plain strings only — no functions or prototypes). * * `descriptor` carries the wildcard *template* (e.g. * `tr([fp/86'/0'/0']xpub.../0/*)`), not a concrete index — the * receiving factories require a template, and storing it directly * means nothing here has to convert concrete → template on rehydrate. * * Adding a new variant is a source change in every worker build; keep * old variants around until all deployed workers handle them. */ type SerializedSigningIdentity = { type: "single-key"; privateKey: string; } | { type: "seed"; seed: string; descriptor: string; } | { type: "mnemonic"; mnemonic: string; descriptor: string; passphrase?: string; }; /** * Tagged envelope for a readonly identity transported across the * service-worker boundary. All variants are structured-clone safe. * `descriptor` is the wildcard template (see * {@link SerializedSigningIdentity}). */ type SerializedReadonlyIdentity = { type: "readonly-single-key"; publicKey: string; } | { type: "readonly-descriptor"; descriptor: string; }; type SerializedIdentity = SerializedSigningIdentity | SerializedReadonlyIdentity; /** * Legacy untagged shape emitted by page builds prior to the tagged * SerializedIdentity envelope. Retained so newer workers can still accept * older pages during a rolling upgrade. Slated for removal in the next major. * * @deprecated Use {@link SerializedIdentity}. */ type LegacySerializedIdentity = { privateKey: string; } | { publicKey: string; }; /** * Read-side HD capability marker. Exposes the wildcard-suffixed account * descriptor *template* and the descriptor-membership predicate, but no * signing primitives — suitable for watch-only identities backed by an * xpub. * * Extracted from {@link HDCapableIdentity} so that * `ReadonlyDescriptorIdentity` can stand in for an HD wallet's read-only * surface (template-aware, derives pubkeys at any index) without having * to claim signing capability it cannot honour. */ interface ReadonlyHDCapableIdentity extends ReadonlyIdentity { /** * The wildcard-suffixed account descriptor template * (e.g. `tr([fp/86'/0'/0']xpub/0/*)`). Consumers materialize a * concrete descriptor by replacing the `*` with a derivation index. */ readonly descriptor: string; /** * True iff `descriptor` derives from this identity's xpub/seed. * * @deprecated Prefer `DescriptorProvider.isOurs()` via * `HDDescriptorProvider` for rotating HD wallets or * `StaticDescriptorProvider` for legacy single-key wallets. */ isOurs(descriptor: string): boolean; } /** * Capability marker for identities that can be rotated through an HD * derivation tree AND can sign at each rotated index. * * Deliberately does NOT extend `DescriptorProvider`: if an HD-capable * identity were silently usable as a concrete descriptor source, callers * could bypass receive rotation and unknowingly reuse a single address * forever. To use this identity as a wallet's descriptor source, wrap * it explicitly: * * - `HDDescriptorProvider` — rotating, recommended for new wallets. * - `StaticDescriptorProvider` — pinned to a single key, for legacy or * explicitly-non-rotating use cases. */ interface HDCapableIdentity extends ReadonlyHDCapableIdentity, Identity { /** * Signs each request with the key derived from its descriptor. * * @deprecated Prefer `DescriptorProvider.signWithDescriptor()` via * `HDDescriptorProvider` or `StaticDescriptorProvider`. Identities keep * this method only as backing implementation for descriptor providers. */ signWithDescriptor(requests: DescriptorSigningRequest[]): Promise; /** * Signs a message using the key derived from `descriptor`. * * @deprecated Prefer `DescriptorProvider.signMessageWithDescriptor()` via * `HDDescriptorProvider` or `StaticDescriptorProvider`. Identities keep * this method only as backing implementation for descriptor providers. */ signMessageWithDescriptor(descriptor: string, message: Uint8Array, signatureType?: "schnorr" | "ecdsa"): Promise; } /** Used for default BIP86 derivation with network selection. */ interface NetworkOptions { /** * Mainnet (coin type 0) or testnet (coin type 1). * * @defaultValue `true` */ isMainnet?: boolean; } /** Used for a caller-supplied account-descriptor template. */ interface DescriptorOptions { /** * Account-descriptor *template* — must end with the BIP-32 wildcard * suffix `/*)`. Stored as-is on {@link SeedIdentity.descriptor} and * read by HD providers to rotate through derivation indices. */ descriptor: string; } /** Either default BIP86 derivation (with optional network selection) or a caller-supplied template. */ type SeedIdentityOptions = NetworkOptions | DescriptorOptions; /** Used for deriving an identity from a BIP39 mnemonic. */ type MnemonicOptions = SeedIdentityOptions & { /** Optional BIP39 passphrase for additional seed entropy. */ passphrase?: string; }; /** * Seed-based identity derived from a raw seed and an account descriptor * *template*. * * This is the recommended identity type for most applications. It uses * standard BIP86 (Taproot) derivation by default; callers that need a * different path supply the wildcard template directly. * * Prefer this (or @see MnemonicIdentity) over `SingleKey` for new * integrations — `SingleKey` exists for backward compatibility with * raw nsec-style keys. * * The identity holds the wildcard *template* (e.g. * `tr([fp/86'/0'/0']xpub/0/*)`) on its public {@link descriptor} * field. HD rotation reads it directly; consumers that need a * concrete descriptor at a specific index materialize it themselves * (see `HDDescriptorProvider` in the wallet layer). * * Exposes seed-level primitives (signing, derivation, the template) * but is deliberately NOT a `DescriptorProvider`. Wrap it explicitly * to get one: * - `HDDescriptorProvider` for rotating receive addresses. * - {@link StaticDescriptorProvider} for legacy, single-key behaviour. * * The split prevents a SeedIdentity from being silently used as a * concrete descriptor source, which would defeat HD rotation without * any compile-time signal that something was wrong. * * @example * ```typescript * const seed = mnemonicToSeedSync(mnemonic); * * // Testnet (BIP86 wildcard descriptor m/86'/1'/0'/0/*) * const identity = SeedIdentity.fromSeed(seed, { isMainnet: false }); * * // Mainnet (BIP86 wildcard descriptor m/86'/0'/0'/0/*) * const identity = SeedIdentity.fromSeed(seed, { isMainnet: true }); * * // Caller-supplied wildcard descriptor (must end in `/*)`). * const identity = SeedIdentity.fromSeed(seed, { descriptor }); * ``` */ declare class SeedIdentity implements HDCapableIdentity { private readonly derivedKey; /** * Wildcard account-descriptor template (e.g. * `tr([fp/86'/0'/0']xpub/0/*)`). The canonical thing to pass * through the system; consumers materialize a concrete descriptor * at a specific index themselves (see `HDDescriptorProvider` in * the wallet layer for the rotating-counter use case). */ readonly descriptor: string; /** * Constructs a SeedIdentity from a 64-byte seed and either a * caller-supplied wildcard descriptor (`{ descriptor }`) or the * default BIP86 path at the requested network (`{ isMainnet }`). * Prefer the {@link fromSeed} factory for symmetry with * {@link MnemonicIdentity.fromMnemonic}. * * Throws on a non-wildcard descriptor, an xpub mismatch with the * seed, or a missing derivation path. */ constructor(seed: Uint8Array, opts?: SeedIdentityOptions); /** * Creates a SeedIdentity from a raw 64-byte seed. * * Pass `{ isMainnet }` for default BIP86 derivation, or * `{ descriptor }` for a caller-supplied account-descriptor * template (the option's value must end with `/*)`). * * @param seed - 64-byte seed (typically from mnemonicToSeedSync) * @param opts - Network selection or descriptor template. */ static fromSeed(seed: Uint8Array, opts?: SeedIdentityOptions): SeedIdentity; xOnlyPublicKey(): Promise; compressedPublicKey(): Promise; sign(tx: Transaction, inputIndexes?: number[]): Promise; signMessage(message: Uint8Array, signatureType?: "schnorr" | "ecdsa"): Promise; /** * BIP-340 sign `messageHash` with this identity's own key, aux_rand = 0. * * The descriptor-less sibling of * {@link signSchnorrDeterministicWithDescriptor}, for an `auto`-mode wallet * whose bare `tr(pubkey)` resolves to the identity itself rather than to a * {@link DescriptorIdentity}. Deliberately NOT routed through * `signMessage`, whose schnorr branch draws a random aux_rand and would * make the "deterministic" signature unreproducible. */ signSchnorrDeterministic(messageHash: Uint8Array): Promise; signerSession(): SignerSession; /** * Converts to a watch-only identity that cannot sign. Carries the * template forward, so the readonly side stays HD-capable (can * derive descriptors at any index without seed access). */ toReadonly(): Promise; /** * Returns true when `descriptor` is derived from this identity's seed. * HD descriptors match by account xpub; bare `tr(pubkey)` descriptors * match by raw pubkey. See {@link descriptorIsOurs}. * * @deprecated Prefer `DescriptorProvider.isOurs()` via * `HDDescriptorProvider` for rotating HD wallets or * `StaticDescriptorProvider` for legacy single-key wallets. */ isOurs(descriptor: string): boolean; /** * Signs each request with the key derived from its descriptor. * Each descriptor must share this identity's seed ({@link isOurs}). * * @deprecated Prefer `DescriptorProvider.signWithDescriptor()` via * `HDDescriptorProvider` or `StaticDescriptorProvider`. Identities keep * this method only as backing implementation for descriptor providers. */ signWithDescriptor(requests: DescriptorSigningRequest[]): Promise; /** * Signs a message with the key derived from `descriptor`. * * @deprecated Prefer `DescriptorProvider.signMessageWithDescriptor()` via * `HDDescriptorProvider` or `StaticDescriptorProvider`. Identities keep * this method only as backing implementation for descriptor providers. */ signMessageWithDescriptor(descriptor: string, message: Uint8Array, signatureType?: "schnorr" | "ecdsa"): Promise; /** * BIP-340 sign `messageHash` with the key derived from `descriptor`, * aux_rand = 0. Backs {@link DescriptorIdentity.signSchnorrDeterministic}, * which swap preimage derivation reads through. */ signSchnorrDeterministicWithDescriptor(descriptor: string, messageHash: Uint8Array): Promise; private derivePrivateKeyForDescriptor; private signTxWithKey; private signMessageWithKey; } /** * Mnemonic-based identity derived from a BIP39 phrase. * * This is the most user-friendly identity type — recommended for wallet * applications where users manage their own backup phrase. Extends * @see SeedIdentity with mnemonic validation and optional passphrase * support. * * @example * ```typescript * const identity = MnemonicIdentity.fromMnemonic( * 'abandon abandon abandon ...', * { isMainnet: true, passphrase: 'secret' } * ); * ``` */ declare class MnemonicIdentity extends SeedIdentity { private constructor(); /** * Creates a MnemonicIdentity from a BIP39 mnemonic phrase. * * Pass `{ isMainnet }` for default BIP86 derivation, or * `{ descriptor }` for a caller-supplied account-descriptor * template (the option's value must end with `/*)`). * * @param phrase - BIP39 mnemonic phrase (12 or 24 words) * @param opts - Network selection or descriptor template, plus optional passphrase */ static fromMnemonic(phrase: string, opts?: MnemonicOptions): MnemonicIdentity; } /** * Watch-only HD identity from a descriptor *template*. * * Can derive public keys but cannot sign transactions. Use this for * watch-only wallets — given just an xpub-based template, the readonly * side still rotates through HD indices. * * Constructed from a wildcard template (e.g. * `tr([fp/86'/0'/0']xpub.../0/*)`); the {@link descriptor} field * holds it for HD providers to consume. * * @example * ```typescript * const ro = ReadonlyDescriptorIdentity.fromDescriptor( * "tr([fp/86'/0'/0']xpub.../0/*)" * ); * ro.descriptor; * // => "tr([fp/86'/0'/0']xpub.../0/*)" — the template * ``` */ declare class ReadonlyDescriptorIdentity implements ReadonlyHDCapableIdentity { /** * Index-0 expansion of {@link descriptor}. Both the x-only pubkey * (taproot, returned by the library as 32 bytes) and the compressed * pubkey (derived through the bip32 node when needed) are read off * this on demand — no separate caches. */ private readonly indexZero; /** * Wildcard account-descriptor template (e.g. * `tr([fp/86'/0'/0']xpub/0/*)`). HD rotation consumers materialize * a concrete descriptor at a specific index themselves. */ readonly descriptor: string; private constructor(); /** * Creates a ReadonlyDescriptorIdentity from an account-descriptor * *template* (must end with the BIP-32 wildcard suffix `/*)`). * * @param descriptor - Wildcard-suffixed Taproot template * (`tr([fp/path']xpub.../child/*)`). */ static fromDescriptor(descriptor: string): ReadonlyDescriptorIdentity; xOnlyPublicKey(): Promise; compressedPublicKey(): Promise; /** * Returns true when `descriptor` derives from this identity's xpub. * HD descriptors match by account xpub; bare `tr(pubkey)` descriptors * fall back to comparing against the index-0 x-only pubkey. See * {@link descriptorIsOurs}. * * @deprecated Prefer `DescriptorProvider.isOurs()` via * `HDDescriptorProvider` for rotating HD wallets or * `StaticDescriptorProvider` for legacy single-key wallets. */ isOurs(descriptor: string): boolean; } /** * Normalize a value to descriptor format. * If already a descriptor, return as-is. If hex pubkey, wrap as tr(pubkey). * Throws when the value is empty or not a string so we never produce * malformed descriptors like `tr()` that downstream parsers would reject. */ declare function normalizeToDescriptor(value: string): string; /** * Extract the x-only (32-byte) pubkey from a materialized descriptor. * Handles both static `tr(pubkey)` and materialized HD * `tr([fp/..]xpub/0/)` shapes via the descriptors-scure expansion map. * Throws a plain Error when the descriptor is non-rangeable / unparseable; * callers that need a typed error wrap this. */ declare function deriveDescriptorLeafPubKey(descriptor: string): Uint8Array; /** * Compressed (33-byte) counterpart of {@link deriveDescriptorLeafPubKey}, for * protocols that key on the parity byte (Boltz swap keys). * * Only HD descriptors are supported: a bare `tr(xonly)` has lost its parity and * synthesizing `02||xonly` would silently produce the wrong key half the time. */ declare function deriveDescriptorLeafCompressedPubKey(descriptor: string): Uint8Array; /** Parsed HD descriptor components. */ interface ParsedHDDescriptor { fingerprint: string; basePath: string; xpub: string; derivationPath: string; } /** * Parse an HD descriptor into its components. * HD descriptors have the format: tr([fingerprint/path']xpub/derivation) * Returns null if the descriptor is not in HD format. */ declare function parseHDDescriptor(descriptor: string): ParsedHDDescriptor | null; /** * The descriptor that names an identity's own key: the whole of a static * wallet's key policy, and the descriptor a record bound before an `auto` * wallet became HD carries. * * One spelling, deliberately. Every site that mints this must produce * byte-identical output for one identity, or a stored descriptor stops * round-tripping through `signerForDescriptor` and `contractSigner`'s * pubkey-equality check — a record the wallet can sign for, refused as * foreign. */ declare function identityDescriptor(identity: { xOnlyPublicKey(): Promise; }): Promise; /** * Descriptor-scoped signing surface — the two members every * {@link DescriptorProvider} and every `HDCapableIdentity` share. */ type DescriptorSigner = Pick; /** * Per-descriptor deterministic Schnorr signing. Separate from the baseline * `DeterministicSignCapable` marker because the seed holder — not the * descriptor — is the only party that can derive the key at an index. */ interface HDDeterministicSignCapable { /** BIP-340 sign `messageHash` with the descriptor's key and aux_rand = 0. */ signSchnorrDeterministicWithDescriptor(descriptor: string, messageHash: Uint8Array): Promise; } /** Type guard for {@link HDDeterministicSignCapable}. */ declare function isHDDeterministicSignCapable(value: unknown): value is HDDeterministicSignCapable; /** Constructor arguments for {@link DescriptorIdentity}. */ interface DescriptorIdentityOptions { /** Materialized descriptor (no wildcard) this identity is pinned to. */ descriptor: string; /** Descriptor-aware signer — an `HDDescriptorProvider` or an HD identity. */ signer: DescriptorSigner; /** * Seed-holding identity behind `signer`. Backs the members that are not * descriptor-scoped in the provider contract: musig2 sessions and * deterministic Schnorr. */ base: Identity; } /** * {@link Identity} pinned to one materialized HD descriptor: every key and * signature it produces belongs to that index rather than to the wallet's * baseline key. * * Lets components that take a plain `Identity` (VHTLC claim/refund, batch * joins) operate on a rotated index without knowing about descriptors. * * Deliberately does NOT satisfy `isBatchSignable`, `isHDCapableIdentity`, or * `hasToReadonly` — it is a leaf signer, not another HD root. */ declare class DescriptorIdentity implements Identity { private readonly descriptor; private readonly signer; private readonly base; constructor({ descriptor, signer, base }: DescriptorIdentityOptions); xOnlyPublicKey(): Promise; compressedPublicKey(): Promise; sign(tx: Transaction, inputIndexes?: number[]): Promise; signMessage(message: Uint8Array, signatureType?: "schnorr" | "ecdsa"): Promise; /** * Delegated: `TreeSignerSession.random()` consults no key material, so the * session is index-independent. */ signerSession(): SignerSession; /** * Throws rather than falling back to the baseline key: a preimage derived * from the wrong index is unrecoverable, and callers probe this member * through a structural guard that cannot tell the two apart. */ signSchnorrDeterministic(messageHash: Uint8Array): Promise; } /** * In-memory single key implementation for Bitcoin transaction signing. * * @example * ```typescript * // Create from hex string * const key = SingleKey.fromHex('your_private_key_hex'); * * // Create from raw bytes * const key = SingleKey.fromPrivateKey(privateKeyBytes); * * // Create random key * const randomKey = SingleKey.fromRandomBytes(); * * // Sign a transaction * const signedTx = await key.sign(transaction); * ``` */ declare class SingleKey implements Identity { private key; private constructor(); /** Create a signing identity from raw private key bytes. */ static fromPrivateKey(privateKey: Uint8Array): SingleKey; /** Create a signing identity from a hex-encoded private key. */ static fromHex(privateKeyHex: string): SingleKey; /** Create a signing identity with a freshly generated random private key. */ static fromRandomBytes(): SingleKey; /** * Export the private key as a hex string. * * @returns The private key as a hex string */ toHex(): string; sign(tx: Transaction, inputIndexes?: number[]): Promise; compressedPublicKey(): Promise; xOnlyPublicKey(): Promise; signerSession(): SignerSession; signMessage(message: Uint8Array, signatureType?: "schnorr" | "ecdsa"): Promise; /** * BIP-340 sign `messageHash` with aux_rand = 0, so the signature — and * anything derived from it — is reproducible from the key alone. * * What lets a static wallet derive a swap preimage instead of storing one * (see `wallet/contractSecrets.ts`). Deliberately NOT `signMessage`, whose * schnorr branch draws a random aux_rand: a preimage derived from that is * unrecoverable, and the loss would only surface at claim time. */ signSchnorrDeterministic(messageHash: Uint8Array): Promise; toReadonly(): Promise; } declare class ReadonlySingleKey implements ReadonlyIdentity { private readonly publicKey; /** Create a readonly identity from a compressed public key. */ constructor(publicKey: Uint8Array); /** * Create a ReadonlySingleKey from a compressed public key. * * @param publicKey - 33-byte compressed public key (02/03 prefix + 32-byte x coordinate) * @returns A new ReadonlySingleKey instance * @example * ```typescript * const pubkey = new Uint8Array(33); // your compressed public key * const readonlyKey = ReadonlySingleKey.fromPublicKey(pubkey); * ``` */ static fromPublicKey(publicKey: Uint8Array): ReadonlySingleKey; xOnlyPublicKey(): Promise; compressedPublicKey(): Promise; } /** * Estimator evaluates CEL expressions to calculate fees for Arkade intents */ declare class Estimator { readonly config: IntentFeeConfig; private intentOffchainInput?; private intentOnchainInput?; private intentOffchainOutput?; private intentOnchainOutput?; /** * Creates a new Estimator with the given config * @param config - Configuration containing CEL programs for fee calculation */ constructor(config: IntentFeeConfig); /** * Evaluates the fee for a given vtxo input * @param input - The offchain input to evaluate * @returns The fee amount for this input */ evalOffchainInput(input: OffchainInput): FeeAmount; /** * Evaluates the fee for a given boarding input * @param input - The onchain input to evaluate * @returns The fee amount for this input */ evalOnchainInput(input: OnchainInput): FeeAmount; /** * Evaluates the fee for a given vtxo output * @param output - The output to evaluate * @returns The fee amount for this output */ evalOffchainOutput(output: FeeOutput): FeeAmount; /** * Evaluates the fee for a given collaborative exit output * @param output - The output to evaluate * @returns The fee amount for this output */ evalOnchainOutput(output: FeeOutput): FeeAmount; /** * Evaluates the fee for a given set of inputs and outputs * @param offchainInputs - Array of offchain inputs to evaluate * @param onchainInputs - Array of onchain inputs to evaluate * @param offchainOutputs - Array of offchain outputs to evaluate * @param onchainOutputs - Array of onchain outputs to evaluate * @returns The total fee amount */ evaluate(offchainInputs: OffchainInput[], onchainInputs: OnchainInput[], offchainOutputs: FeeOutput[], onchainOutputs: FeeOutput[]): FeeAmount; } declare class InMemoryIntentRepository implements IntentRepository { readonly version: 1; private byId; clear(): Promise; saveIntent(intent: ArkIntent): Promise; getIntents(filter?: IntentFilter): Promise; getLockedVtxoOutpoints(): Promise; [Symbol.asyncDispose](): Promise; } declare class InMemoryVirtualTxRepository implements VirtualTxRepository { readonly version: 1; private txs; private branches; clear(): Promise; upsertVirtualTxs(txs: VirtualTx[]): Promise; getVirtualTx(txid: string): Promise; setBranch(vtxo: Outpoint, branch: VtxoBranch[]): Promise; getBranch(vtxo: Outpoint): Promise; hasBranch(vtxo: Outpoint): Promise; pruneForSpentVtxo(vtxo: Outpoint): Promise; [Symbol.asyncDispose](): Promise; } /** * @experimental Virtual-tx persistence is inert by default. This repository * opens its DB at {@link INTENT_DB_VERSION} and creates the virtualtx stores, * so it must be given a *dedicated* `dbName` distinct from the wallet/contract * DB until this persistence is activated in the shared schema — otherwise the * shared DB's version-conflict guard rejects the mismatched open. */ declare class IndexedDBVirtualTxRepository implements VirtualTxRepository { readonly version: 1; private readonly connection; constructor(dbName?: string); private getDB; clear(): Promise; upsertVirtualTxs(txs: VirtualTx[]): Promise; getVirtualTx(txid: string): Promise; setBranch(vtxo: Outpoint, branch: VtxoBranch[]): Promise; getBranch(vtxo: Outpoint): Promise; hasBranch(vtxo: Outpoint): Promise; pruneForSpentVtxo(vtxo: Outpoint): Promise; [Symbol.asyncDispose](): Promise; } /** * @experimental Intent persistence is inert by default. This repository opens * its DB at {@link INTENT_DB_VERSION} and creates the intent stores, so it must * be given a *dedicated* `dbName` distinct from the wallet/contract DB until * intent persistence is activated in the shared schema — otherwise the shared * DB's version-conflict guard rejects the mismatched open. */ declare class IndexedDBIntentRepository implements IntentRepository { readonly version: 1; private readonly connection; constructor(dbName?: string); private getDB; clear(): Promise; saveIntent(intent: ArkIntent): Promise; getIntents(filter?: IntentFilter): Promise; getLockedVtxoOutpoints(): Promise; [Symbol.asyncDispose](): Promise; } /** * In-memory implementation of WalletRepository. * Data is ephemeral and scoped to the instance. */ declare class InMemoryWalletRepository implements WalletRepository { readonly version: 1; private readonly vtxosByAddress; private readonly utxosByAddress; private readonly txsByAddress; private walletState; getVtxos(address: string): Promise; saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise; deleteVtxos(address: string): Promise; getVtxosForScript(script: string): Promise; getVtxosForScripts(scripts: string[], options?: { unspentOnly?: boolean; }): Promise; saveVtxosForScript(key: VtxoRepositoryKey, vtxos: ExtendedVirtualCoin[]): Promise; deleteVtxosForScript(script: string): Promise; getUtxos(address: string): Promise; saveUtxos(address: string, utxos: ExtendedCoin[]): Promise; deleteUtxos(address: string): Promise; getTransactionHistory(address: string): Promise; saveTransactions(address: string, txs: ArkTransaction[]): Promise; deleteTransactions(address: string): Promise; getWalletState(): Promise; saveWalletState(state: WalletState): Promise; clear(): Promise; [Symbol.asyncDispose](): Promise; } /** * Whether generic wallet spending may select this contract's VTXOs. * * Default closed: a type whose handler declares nothing — a custom type, a * plugin type, a row whose handler is not registered in this context — is not * spendable. Every shipped handler answers explicitly. * * @see ContractHandler.isGenericallySpendable */ declare function isContractGenericallySpendable(contract: Contract): boolean; /** * The contracts generic spending must skip, as `script → type`. VTXOs are keyed * to their contract by `script`, so membership answers the per-VTXO question too. */ declare function gatedContracts(contracts: readonly Contract[]): Map; /** The minimum a VTXO must carry to be matched against an exclusion set. */ type ExcludableVtxo = { txid: string; vout: number; script?: string; }; /** {@link gatedContracts}' answer, as callers that only read it should take it. */ type GatedContracts = ReadonlyMap; /** * Why generic spending skips a VTXO, or `undefined` when it does not — phrased * to follow the outpoint in a log line. * * Generic spending applies three exclusions (the contract gate, pending * recovery, intent locks) that key on different things — script, script, * outpoint. Stating each as a reason-or-undefined lets all three report through * one path, so no exclusion can drop a coin silently while another logs. */ type VtxoExclusion = (vtxo: ExcludableVtxo) => string | undefined; /** * The contract gate as an exclusion, naming why it closed: a handler ran and * declined, or this build has no handler for the type at all. * * The two want opposite reactions from whoever reads the log. A decline is * the gate working as designed — nothing to do. A missing handler means a * contract row this build cannot interpret, e.g. a vendoring or version * mismatch, which is worth a reader's attention. `contractHandlers` is * already available here, so the message can tell them apart instead of * collapsing both into one sentence that reads as the same case either way. */ declare function gateExclusion(gated: GatedContracts): VtxoExclusion; /** Outpoints excluded for a shared reason, e.g. an intent-lock set. */ declare function outpointExclusion(outpoints: ReadonlySet, reason: string): VtxoExclusion; /** * Outpoints excluded for individually-named reasons — a per-input timelock, * where one shared reason would be wrong because the inputs differ. * * Unlike {@link gateExclusion}, the reasons are the handlers' own sentences and * are not rephrased to follow the outpoint: that text is what tells the reader * when to retry, and a paraphrase here would be a second spelling to keep in * step with it. * * @see outpointExclusion for the shared-reason form. */ declare function outpointReasons(reasons: ReadonlyMap): VtxoExclusion; /** * Report VTXOs an exclusion dropped — the only field-diagnosable signal for why * a coin is missing from a spend, or why an explicitly named one will fail. * Debug level, one line per VTXO per reason. */ declare function logExcludedVtxos(source: string, vtxos: readonly ExcludableVtxo[], exclusions: readonly VtxoExclusion[]): void; /** * Thrown when a spend names VTXOs whose contract can no longer be annotated — * its handler is not registered here, its handler rejects the stored params, or * it has no contract row at all. * * Raised before submission on purpose. Spending reads the tapscripts stored on * each coin rather than re-deriving them, so such a spend would otherwise build * and broadcast normally and only fail in the bookkeeping afterwards, leaving * the transaction on the network and the local state behind it. */ declare class UnannotatableInputError extends Error { readonly name = "UnannotatableInputError"; } /** * Encode a contract to the arkcontract string format. * * Format: arkcontract={type}&{key1}={value1}&{key2}={value2}... * * This format is compatible with NArk and allows contracts to be * shared/imported across different Arkade SDKs. * * @example * ```typescript * const contract: Contract = { * type: "vhtlc", * params: { sender: "ab12...", receiver: "cd34...", ... }, * // ... * }; * * const encoded = encodeArkContract(contract); * // "arkcontract=vhtlc&sender=ab12...&receiver=cd34...&..." * ``` */ declare function encodeArkContract(contract: Contract): string; /** * Parsed result from decoding an arkcontract string. * * This is a low-level representation. For type-safe contract creation, * use `contractFromArkContract` or `contractFromArkContractWithAddress` * which validate params through the handler system. */ interface ParsedArkContract { /** Contract type (e.g., "vhtlc", "default") */ type: string; /** All key-value pairs from the string */ data: Record; } /** * Decode an arkcontract string into raw type and data. * * This is a low-level function that parses the URL-encoded format. * For creating typed Contract objects, use `contractFromArkContract` * or `contractFromArkContractWithAddress` instead. * * @param encoded - The arkcontract string * @returns Parsed type and key-value data * @throws If the string is not a valid arkcontract * * @example * ```typescript * const parsed = decodeArkContract("arkcontract=vhtlc&sender=ab12..."); * // { type: "vhtlc", data: { sender: "ab12...", ... } } * ``` */ declare function decodeArkContract(encoded: string): ParsedArkContract; /** * Create a Contract from an arkcontract string. * * This requires a handler to be registered for the contract type. * * @param encoded - The arkcontract string * @param options - Additional options for the contract * @returns A Contract object * @throws If the string is invalid or no handler exists for the type * * @example * ```typescript * const contract = contractFromArkContract( * "arkcontract=vhtlc&sender=ab12...", * { * label: "Lightning Receive", * } * ); * ``` */ declare function contractFromArkContract(encoded: string, options?: { label?: string; state?: "active" | "inactive"; metadata?: Record; }): Omit & { script?: string; address?: string; }; /** * Create a full Contract with derived script and address. * * @param encoded - The arkcontract string * @param serverPubKey - Server public key (for address derivation) * @param addressPrefix - Address prefix (e.g., "tark" for testnet) * @param options - Additional options * @returns A complete Contract object */ declare function contractFromArkContractWithAddress(encoded: string, serverPubKey: Uint8Array, addressPrefix?: string, options?: { label?: string; state?: "active" | "inactive"; metadata?: Record; }): Contract; /** * Check if a string is an arkcontract. */ declare function isArkContract(str: string): boolean; /** * Thrown when a descriptor-capable contract (default, delegate, or boarding) * cannot be routed to any signer: its owner key (`params.pubKey`) is not this * wallet's baseline signing key, and the record carries no * `metadata.signingDescriptor` to send it to a descriptor-aware signer. * * Note that this can arise also when a contract belongs to a different identity (storage reuse). */ declare class MissingSigningDescriptorError extends Error { readonly contractScript: string; readonly contractType: "default" | "delegate" | "boarding"; readonly name = "MissingSigningDescriptorError"; constructor(contractScript: string, contractType: "default" | "delegate" | "boarding"); } /** * Thrown when an input needs descriptor-aware signing but no * DescriptorProvider was wired into the wallet. */ declare class DescriptorSigningProviderMissingError extends Error { readonly name = "DescriptorSigningProviderMissingError"; constructor(); } /** * In-memory implementation of ContractRepository. * Data is ephemeral and scoped to the instance. */ declare class InMemoryContractRepository implements ContractRepository { readonly version: 2; private readonly contractData; private readonly collections; private readonly contractsByScript; clear(): Promise; getContracts(filter?: ContractFilter): Promise; saveContract(contract: Contract): Promise; deleteContract(script: string): Promise; [Symbol.asyncDispose](): Promise; } /** * IndexedDB-based implementation of ContractRepository. * * Data is stored as JSON strings in key/value stores. */ declare class IndexedDBContractRepository implements ContractRepository { readonly version: 2; private readonly connection; constructor(dbName?: string); clear(): Promise; getContracts(filter?: ContractFilter): Promise; saveContract(contract: Contract): Promise; deleteContract(script: string): Promise; private applyContractFilter; private getDB; [Symbol.asyncDispose](): Promise; } /** * IndexedDB-based implementation of WalletRepository. */ declare class IndexedDBWalletRepository implements WalletRepository { readonly version: 1; private readonly connection; constructor(dbName?: string); clear(): Promise; [Symbol.asyncDispose](): Promise; getVtxos(address: string): Promise; saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise; deleteVtxos(address: string): Promise; getVtxosForScript(script: string): Promise; getVtxosForScripts(scripts: string[], options?: { unspentOnly?: boolean; }): Promise; private vtxoStore; saveVtxosForScript(key: VtxoRepositoryKey, vtxos: ExtendedVirtualCoin[]): Promise; deleteVtxosForScript(script: string): Promise; getUtxos(address: string): Promise; saveUtxos(address: string, utxos: ExtendedCoin[]): Promise; deleteUtxos(address: string): Promise; getTransactionHistory(address: string): Promise; saveTransactions(address: string, txs: ArkTransaction[]): Promise; deleteTransactions(address: string): Promise; getWalletState(): Promise; saveWalletState(state: WalletState): Promise; private getDB; } declare const MIGRATION_KEY: (repoType: "wallet" | "contract") => string; type MigrationStatus = "pending" | "in-progress" | "done" | "not-needed"; declare function getMigrationStatus(repoType: "wallet" | "contract", storageAdapter: StorageAdapter): Promise; declare function requiresMigration(repoType: "wallet" | "contract", storageAdapter: StorageAdapter): Promise; declare function rollbackMigration(repoType: "wallet" | "contract", storageAdapter: StorageAdapter): Promise; /** * Migrate wallet data from the legacy storage adapter to the new one. * It accepts both onchain and offchain addresses, make sure to pass both. * * @param storageAdapter * @param fresh * @param addresses */ declare function migrateWalletRepository(storageAdapter: StorageAdapter, fresh: WalletRepository, addresses: { onchain: string[]; offchain: string[]; }): Promise; /** * @deprecated This is only to be used in migration from storage V1 */ declare class WalletRepositoryImpl implements WalletRepository { readonly version: 1; private storage; constructor(storage: StorageAdapter); getVtxos(address: string): Promise; saveVtxos(address: string, vtxos: ExtendedVirtualCoin[]): Promise; clearVtxos(address: string): Promise; deleteVtxos(address: string): Promise; getUtxos(address: string): Promise; saveUtxos(address: string, utxos: ExtendedCoin[]): Promise; clearUtxos(address: string): Promise; deleteUtxos(address: string): Promise; getTransactionHistory(address: string): Promise; saveTransactions(address: string, txs: ArkTransaction[]): Promise; clearTransactions(address: string): Promise; deleteTransactions(address: string): Promise; getWalletState(): Promise; saveWalletState(state: WalletState): Promise; clear(): Promise; [Symbol.asyncDispose](): Promise; } /** * @deprecated This is only to be used in migration from storage V1 */ declare class ContractRepositoryImpl implements ContractRepository { private storage; constructor(storage: StorageAdapter); get version(): 2; getContractData(contractId: string, key: string): Promise; setContractData(contractId: string, key: string, data: T): Promise; deleteContractData(contractId: string, key: string): Promise; getContractCollection(contractType: string): Promise>; saveToContractCollection(contractType: string, item: T, idField: K): Promise; removeFromContractCollection(contractType: string, id: T[K], idField: K): Promise; getContracts(_?: ContractFilter): Promise; saveContract(_: Contract): Promise; deleteContract(_: string): Promise; clear(): Promise; [Symbol.asyncDispose](): Promise; } interface WalletRestoreHook { id: string; restore(wallet: IWallet): Promise; } declare function registerWalletRestoreHook(wallet: IWallet, hook: WalletRestoreHook): () => void; /** * Domain separator for the preimage derivation. * * Protocol-scoped and versioned, mirroring NArk's `Arkade-Boltz-Preimage-v1` * (`SwapsManagementService.cs:128`), so any Arkade SDK implementing this * scheme reproduces the same preimage and can recover an artifact another * SDK created. Deliberately distinct from the Boltz tag: a shared tag would * make one wallet key derive one preimage for both corridors. */ declare const ARKADE_SWAP_PREIMAGE_TAG = "Arkade-RFQ-Preimage-v1"; /** * Domain separator for the **salted** preimage derivation, used when the * descriptor repeats across artifacts. * * Corridor-generic where {@link ARKADE_SWAP_PREIMAGE_TAG} is not, and that * asymmetry is deliberate. The v1 tags must be per-corridor * (`Arkade-RFQ-Preimage-v1` here, NArk's `Arkade-Boltz-Preimage-v1` there) * because v1 pins its message index, so the tag is the only thing separating * two corridors that reach the same key. v2 mints a salt per artifact, so no * two artifacts share a message within a corridor, let alone across two — the * salt carries the separation, and the tag names the layer it belongs to. */ declare const ARKADE_SALTED_PREIMAGE_TAG = "Arkade-Contract-Preimage-Salted-v1"; /** * `TAG ‖ xonly(32) ‖ u32le(index)` — the message that gets BIP-340 signed. * * Anchored on the canonical x-only key rather than the descriptor string: a * restore reconstructs a bare descriptor that serialises differently from the * signing descriptor used at creation, and only the key agrees across both. */ declare function buildPreimageMessage(xonly: Uint8Array, index: number): Uint8Array; /** * `TAG ‖ xonly(32) ‖ salt(32)` — the salted message that gets BIP-340 signed. * * The salt replaces v1's pinned index as the source of per-artifact * uniqueness, which is what lets a key that repeats across artifacts still * derive a distinct preimage for each. It is public: knowing it yields nothing * without the seed. */ declare function buildSaltedPreimageMessage(xonly: Uint8Array, salt: Uint8Array): Uint8Array; /** * True iff `descriptor` names one artifact — an HD child. A bare `tr(pubkey)` * is the same key every time it is handed out, so anything deriving * per-artifact secrets must branch on this, never on the wallet's type. */ declare function isPerArtifactDescriptor(descriptor: string): boolean; /** A key the wallet provisioned for a contract we must be able to spend. */ interface ProvisionedKey { /** x-only pubkey to bind into the covenant. */ pubkey: Uint8Array; /** * The wallet descriptor it came from. Public — persist it with the * artifact; {@link contractSigner} resolves it back to a signer. */ descriptor: string; /** * The Ark scriptPubKey that corresponds to this key — the pkScript of the * wallet's current receive address. Returned alongside {@link pubkey} so * callers building the lockup script do not need a separate * {@link IWallet.getAddress} round-trip or an `ArkAddress.decode` call. */ pkScript: Uint8Array; /** * The receive address {@link pkScript} was decoded from, captured in the * same single {@link IWallet.getAddress} read. The quote-time refund * address and the covenant's `refundPkScript` must always name the same * script, and two independent reads of a wallet that rotates its receive * address would not guarantee that — reuse this, never a second * `getAddress()` call. */ address: string; } /** * A claim key together with the preimage it claims with. `pkScript` and * `address` are excluded: a claim leg funds nothing, so it names no refund * destination. */ interface ProvisionedClaimSecret extends Omit { preimage: Uint8Array; /** `sha256(preimage)` — what the contract commits to. */ paymentHash: Uint8Array; /** * The salt {@link preimage} was derived from, when it came from the salted * arm. **Public** — the opposite of {@link preimage} above: persist it with * the artifact in the clear, because without it a wallet holding the seed * still cannot re-derive P. * * Absent on the other two arms: an HD child descriptor already names one * artifact, and a stored preimage needs no derivation input. */ preimageSalt?: Uint8Array; /** * Persist `preimage` with the artifact: this wallet cannot re-derive it. * True when the caller supplied P, or when the signer cannot sign * deterministically. When false the preimage re-derives from the seed — * given {@link preimageSalt}, where there is one — and nothing secret * needs to be stored. */ mustPersistPreimage: boolean; } /** * The key that spends a leg we fund — the refund key of an HTLC, the user key * of a covenant's cancel path. * * The returned `pubkey` is taken from a signer the wallet produced, never * from the descriptor string alone. That is the difference between an * invariant and a comment: deriving the leaf key is pure parsing and would * succeed just as well for a key this wallet cannot sign for — a descriptor * allocated by a worker rebound to another identity, a record restored onto * the wrong seed — and the covenant would bind it, the leg would fund, and * the failure would surface at refund time with the money already committed. * Throws {@link ForeignDescriptorError} instead, before there is a quote. * * The wallet's identity key is reused rather than a fresh HD child, so no * index is consumed when the artifact is never built — and the covenant's * refund path is on the same key as the refund address the caller sends to * at quote time. */ declare function provisionRefundKey(wallet: IWallet): Promise; /** * The key that spends a leg we claim, plus the preimage that unlocks it. * * Pass `opts.preimage` to bring your own 32-byte P; it comes back verbatim * with `mustPersistPreimage` set, since the wallet cannot re-derive what it * did not choose. * * Three arms, and which one a wallet lands in is the whole of this function: * * 1. **Caller-supplied P.** Returned unchanged, `mustPersistPreimage: true`. * 2. **Per-artifact descriptor** (an HD child). Derives from the key alone at * the pinned index. Nothing at rest. Raises rather than falling through if * the signer cannot sign deterministically — an HD descriptor whose wallet * refuses is a broken wallet, not a fallback case. * 3. **Anything else** — a static wallet's `tr(pubkey)`, or a constant * descriptor from a custom provider. Mints a public per-artifact salt and * derives from it, so a key that repeats still yields a distinct P. Only * this arm falls back to a stored random preimage, and only when the signer * refuses — which is discovered by deriving, never by probing. */ declare function provisionClaimSecret(wallet: IWallet, opts?: { preimage?: Uint8Array; }): Promise; /** * The signer for a provisioned descriptor, verified to actually be that * descriptor's key. * * The verification is a public-key equality, never a wallet-type probe: a * wallet that answers with the wrong key — another seed's, after a restore * mix-up — would sign happily, and the failure would surface only as a * counterparty rejection or a dead script. * * Know its limit. It catches a wallet that substitutes its *baseline* * identity, which is the failure this exists for. It cannot catch one that * returns a descriptor-scoped identity built over the same descriptor, since * such an identity reads its pubkey back out of the descriptor string and the * comparison becomes a tautology. What rules that case out is * {@link resolveDescriptorSigner}'s `isOurs` test, which both shipped wallets * resolve through — so this is the backstop for a hand-rolled `IWallet`, not * the primary guarantee. */ declare function contractSigner(wallet: IWallet, descriptor: string): Promise; /** * A wallet that holds a contract's key but cannot sign with it: a watch-only * identity, or a remote signer whose transport is absent. * * Distinct from {@link ForeignDescriptorError} because the remedy differs — * that one says "wrong wallet", this one says "this wallet, without its * signer". Raised at provisioning as well as at signing, so a wallet that * could never spend the leg finds out before it funds one. */ declare class WalletCannotSignError extends Error { readonly descriptor: string; readonly name = "WalletCannotSignError"; constructor(descriptor: string); } /** * The preimage for a provisioned descriptor: `opts.stored` when the artifact * kept one, otherwise re-derived from the seed. * * Resolves in one precedence order, mirroring the arms * {@link provisionClaimSecret} chose between: * * 1. `opts.stored` — a caller-supplied P, or one from a wallet that could not * derive. Whatever the wallet can do now, a stored P is the artifact's. * 2. a per-artifact descriptor — derive at the pinned index. * 3. `opts.salt` — derive from the salted message. * 4. otherwise throw: a repeating descriptor with neither a stored preimage * nor a salt has nothing to derive from that would not collide. */ declare function contractPreimage(wallet: IWallet, descriptor: string, opts?: { stored?: Uint8Array; salt?: Uint8Array; }): Promise; /** An identity that signs with `aux_rand = 0`, which is what makes the * derivation reproducible. `DescriptorIdentity` satisfies it, and throws * rather than degrading to a random-aux signer. */ interface DeterministicSigner extends ReadonlyIdentity { /** * Implementors wrapping a remote: throw only for a refusal that will * repeat. {@link provisionClaimSecret} reads a transient throw — a network * hiccup, an extension timeout — as "this signer cannot derive", and falls * back to a stored random preimage for the artifact's whole life. That is * claimable, but it persists a secret the seed would otherwise have * replaced, and nothing surfaces the downgrade to the caller. */ signSchnorrDeterministic(messageHash: Uint8Array): Promise; } declare function isDeterministicSigner(value: unknown): value is DeterministicSigner; /** * Claim a restored artifact's index so a later allocation cannot reissue it — * which would derive that artifact's preimage a second time, for a different * one. * * Monotonic, and a no-op wherever there is no index to reserve — a shared-key * descriptor, or another seed's artifact. Restores iterate whole histories, so * an artifact this wallet has nothing to adopt for must not abort the loop. * * Telling "another seed's" apart from a failure needs * {@link HDWalletCapable.signerForDescriptor}, so an allocation-capable wallet * that does not also implement it gets only the watermark call's own untyped * refusal, and a foreign artifact reaches the caller as an error rather than a * skip. Every wallet shipped here implements both. */ declare function adoptContractDescriptor(wallet: IWallet, descriptor: string): Promise; type RequestEnvelope = { tag: string; id: string; broadcast?: boolean; }; type ResponseEnvelope = { tag: string; id?: string; error?: Error; broadcast?: boolean; }; interface MessageHandler { /** * A unique identifier for the updater. * This is used to route messages to the correct updater. */ readonly messageTag: string; /** * Called once when the SW is starting up * @param services - Providers and wallet instances available to the handler. * @param repositories - Repositories available to the handler. * @param channel - Push channel to clients, for responses that answer no * request. Use it instead of holding events for the next {@link tick}. * The channel is scoped to the generation it was handed out in: after a * bus re-init or `stop()` it goes inert on its own, so retaining one past * `stop()` cannot post. **/ start(services: { arkProvider: ArkProvider; wallet?: Wallet; readonlyWallet: ReadonlyWallet; }, repositories: { walletRepository: WalletRepository; }, channel?: { broadcast(response: RES): void; }): Promise; /** Called once when the SW is shutting down */ stop(): Promise; /** * Called by the scheduler to perform a tick. * Can be used by the updater to perform periodic tasks or return * delayed responses (eg: subscriptions). * @param now The current time in milliseconds since the epoch. **/ tick(now: number): Promise; /** * Handle routed messages from the clients **/ handleMessage(message: REQ): Promise; /** * Optional opt-out from the bus-level message timeout. * * Long-running flows (e.g. settlement) surrender control to remote peers * and can legitimately sit idle for longer than `messageTimeoutMs`. When * this returns true, the bus awaits `handleMessage` without a deadline. * Defaults to false. */ isLongRunning?(message: REQ): boolean; } type Options = { messageHandlers: MessageHandler[]; tickIntervalMs?: number; messageTimeoutMs?: number; /** * Per-operation timeout overrides. Keys are either message types * (e.g. "SETTLE") or handler tags (e.g. "WALLET_UPDATER"). Message-type * matches take precedence over tag matches. Unspecified operations use * `messageTimeoutMs`. These are treated as defaults: any map supplied * via `INITIALIZE_MESSAGE_BUS` overrides per-key and is re-applied on * every (re-)init. */ messageTimeoutOverrides?: Record; /** * Optional intent-lifecycle repository, handed to the worker's wallet as * `storage.intentRepository`. Same opt-in as a main-thread wallet: without * it the worker persists no settlement intent, reconciles none on restart, * and counts intent-locked VTXOs as available. Pass the same repository the * main thread uses so both sides read one intent state. * * Ignored when `buildServices` is supplied — that override owns `storage`. */ intentRepository?: IntentRepository; debug?: boolean; buildServices?: (config: Initialize["config"]) => Promise<{ arkProvider: ArkProvider; wallet?: Wallet; readonlyWallet: ReadonlyWallet; }>; }; type Initialize = { type: "INITIALIZE_MESSAGE_BUS"; id: string; config: { wallet: SerializedIdentity | LegacySerializedIdentity; arkServer: { url: string; publicKey?: string; }; delegateUrl?: string; /** @deprecated alias for @see Initialize.config.delegateUrl */ delegatorUrl?: string; indexerUrl?: string; esploraUrl?: string; settlementConfig?: SettlementConfig | false; walletMode?: "auto" | "static" | "hd"; watcherConfig?: Partial>; /** @see WalletConfig.lookAheadWindow */ lookAheadWindow?: number; /** @see WalletConfig.minBatchExpirySeconds */ minBatchExpirySeconds?: bigint; /** @see WalletConfig.minCheckpointExitDelaySeconds */ minCheckpointExitDelaySeconds?: bigint; /** * Page-supplied per-operation timeout map. Keys are message types * (e.g. "SETTLE"). Overrides constructor-supplied * `messageTimeoutOverrides` per-key; re-applied on every init. */ messageTimeouts?: Record; }; }; declare class MessageBus { private readonly walletRepository; private readonly contractRepository; private handlers; private tickIntervalMs; private messageTimeoutMs; private readonly constructorTimeoutOverrides; private messageTimeoutOverrides; private lateDeliveries; private running; private tickTimeout; private tickInProgress; private debug; private initialized; /** * FIFO chain that serializes `INITIALIZE_MESSAGE_BUS` handling. Each init * runs to completion in enqueue order. */ private initChain; /** * Number of inits accepted but not yet settled (queued or running). The * bus refuses ordinary wallet messages while this is > 0. */ private pendingInitCount; private readonly buildServicesFn; private readonly boundOnMessage; private readonly intentRepository?; /** Pending broadcasts, drained FIFO by a single {@link drain} loop. */ private broadcastQueue; private draining; /** Bumped wherever the generation owning the queued broadcasts ends. */ private broadcastEpoch; /** Create the service-worker message bus with repositories and handler configuration. */ constructor(walletRepository: WalletRepository, contractRepository: ContractRepository, { messageHandlers, tickIntervalMs, messageTimeoutMs, messageTimeoutOverrides, intentRepository, debug, buildServices, }: Options); /** Start the message bus and attach service-worker event listeners. */ start(): Promise; /** Stop the message bus, cancel ticks, and stop all registered handlers. */ stop(): Promise; /** Invalidate every broadcast belonging to the outgoing generation. */ private invalidateBroadcasts; private broadcastToClients; /** * Single fan-out loop, so pushes and tick responses keep one FIFO order. * A helper posting per response would race: `clients.matchAll()` gives no * cross-call resolution order, and it would de-batch `runTick`. */ private drain; /** * A dropped batch has no periodic recovery to fall back on now that events * no longer ride the tick, so name the loss under {@link Options.debug}. */ private logDroppedBroadcasts; private scheduleNextTick; private runTick; /** * Serialize init requests on {@link initChain}. * Returns this init's own result. */ private waitForInit; /** * The bus serves ordinary wallet messages only when it is initialized AND * no init is in flight. */ private acceptsWalletMessages; private doInit; private buildServices; private onMessage; private processMessage; /** * Race `promise` against a timeout. Note: this does NOT cancel the * underlying work — the original promise keeps running. Call * `attachLateDelivery` after catching the timeout to surface the * eventual result so the message id does not go silent. */ private withTimeout; /** * Extract the declared `type` from a request envelope (e.g. "SETTLE"). * Not every envelope carries a type (PING/INIT are special cased * earlier), so this returns undefined for envelopes that lack one. */ private extractMessageType; /** * Resolve the timeout for an operation. Message-type overrides take * precedence over handler-tag overrides, with the bus-wide default * (`messageTimeoutMs`) as the final fallback. */ private resolveTimeoutMs; /** * Build a human-readable label for timeout errors. Format: * `" via "` when both are known, else the * handler tag alone. Used so timeout errors name the operation the * client actually triggered (e.g. SETTLE) rather than just the * handler that received it (e.g. WALLET_UPDATER). */ private labelFor; /** * Post a response to the originating client. When `source` is null * (client tab closed, detached frame, etc.) the response cannot be * delivered; we log the drop in debug mode so it is not invisible. */ private deliverResponse; /** * After a handler times out the client has already received a timeout * error, but the handler keeps running. Attach a follow-up so the * handler's eventual result (or error) is delivered under the same * message id, or — if the handler never completes within * {@link LATE_DELIVERY_GRACE_MS} — an "Operation abandoned" error is * sent so the client's listener (if still attached) does not hang. */ private attachLateDelivery; /** * Returns the registered SW for the path. * It uses the functions in `service-worker-manager.ts` module. * @param path * @return the Service Worker * @throws if not running in a browser environment */ static getServiceWorker(path?: string): Promise; /** * Set up and register the Service Worker, ensuring it's done once at most. * It uses the functions in `service-worker-manager.ts` module. * @param path * @return the Service Worker * @throws if not running in a browser environment */ static setup(path: string): Promise; } /** * A leaf node in the Taproot script tree, as consumed by * `@scure/btc-signer`'s `p2tr(internalKey, tree, ...)`. */ interface TaprootLeaf { script: Bytes; leafVersion: number; } /** * Internal tree node shape consumed by `@scure/btc-signer`'s `p2tr`: * - A leaf is `{ script, leafVersion }` * - A branch is a 2-element tuple `[leftNode, rightNode]` of nodes */ type TaprootTreeNode = TaprootLeaf | [TaprootTreeNode, TaprootTreeNode]; /** * Assemble a Taproot script tree from a flat list of scripts using the * exact algorithm arkd's btcd dependency uses * (`txscript.AssembleTaprootScriptTree`, see * https://github.com/btcsuite/btcd/blob/master/txscript/taproot.go). * * The algorithm: * * Phase 1 — pair leaves left-to-right: * for i := 0; i < len(leaves); i += 2: * if i is the last index (odd leaf at end): * merge with the LAST branch built so far (do NOT pair as a fresh leaf) * else: * create a new branch from (leaves[i], leaves[i+1]) * * Phase 2 — FIFO-queue merge branches: * while branches has ≥ 2 items: * take front two, combine into a new branch, push to back of queue * * This matters because `@scure/btc-signer`'s `taprootListToTree` builds a * Huffman tree (weight-1 leaves combine by smallest-weight pairs). For * power-of-2 leaf counts both algorithms happen to produce the same * perfectly-balanced binary tree and agree. For any other count they * produce DIFFERENT shapes → different merkle roots → different taproot * output keys → arkd rejects spends with `INVALID_PSBT_INPUT`. * * Reproducing btcd's algorithm here lets the SDK construct taptrees that * arkd accepts for arbitrary leaf counts. * * @param scripts - Raw tapscript bytes for each leaf, in the order they * should be encoded in the TapTree PSBT field. * @returns The nested-tuple form `p2tr` accepts. */ declare function assembleBtcdTaprootTree(scripts: Bytes[]): TaprootTreeNode; declare enum AssetInputType { Unspecified = 0, Local = 1, Intent = 2 } declare enum AssetRefType { Unspecified = 0, ByID = 1, ByGroup = 2 } declare class BufferWriter { private buffer; write(data: Uint8Array): void; writeByte(byte: number): void; writeUint16LE(value: number): void; writeVarUint(value: bigint | number): void; writeVarSlice(data: Uint8Array): void; writeCompactSize(value: number): void; writeCompactSlice(data: Uint8Array): void; toBytes(): Uint8Array; } declare class BufferReader { private view; private offset; constructor(data: Uint8Array); remaining(): number; readByte(): number; readSlice(size: number): Uint8Array; readUint16LE(): number; readVarUint(): bigint; readVarSlice(): Uint8Array; readCompactSize(): number; readCompactSlice(): Uint8Array; } /** * AssetId identifies a specific asset. * * @remarks * Asset ids are derived from the genesis transaction id plus the asset group index. * * @see AssetRef * * @example * ```typescript * const assetId = AssetId.create('00'.repeat(32), 0) * const encoded = assetId.toString() * const decoded = AssetId.fromString(encoded) * ``` */ declare class AssetId { readonly txid: Uint8Array; readonly groupIndex: number; private constructor(); /** * Create an asset id from a genesis transaction id and group index. * * @param txid - Hex-encoded genesis transaction id * @param groupIndex - Asset group index within the genesis transaction * @returns A validated asset id * @throws Error if the txid is missing, malformed, or not 32 bytes long * @see fromString */ static create(txid: string, groupIndex: number): AssetId; /** * Decode an asset id from its hex string representation. * * @param s - Hex-encoded asset id * @returns Decoded asset id * @throws Error if the string is not valid hex or does not encode a valid asset id * @see toString */ static fromString(s: string): AssetId; /** * Decode an asset id from its serialized bytes. * * @param buf - Serialized asset id bytes * @returns Decoded asset id * @throws Error if the buffer length is invalid */ static fromBytes(buf: Uint8Array): AssetId; /** * Serialize the asset id to raw bytes. * * @returns Serialized asset id bytes * @see fromBytes */ serialize(): Uint8Array; /** * Encode the asset id to a hex string. * * @returns Hex-encoded asset id * @see fromString */ toString(): string; /** * Validate the asset id fields. * * @throws Error if the txid is empty or the group index is out of range */ validate(): void; /** * Decode an asset id from a binary reader. * * @param reader - Reader positioned at an asset id * @returns Decoded asset id * @throws Error if the reader does not contain enough bytes */ static fromReader(reader: BufferReader): AssetId; /** * Serialize the asset id into an existing binary writer. * * @param writer - Writer to append the asset id to * @see serialize */ serializeTo(writer: BufferWriter): void; } type AssetRefByID = { type: AssetRefType.ByID; assetId: AssetId; }; type AssetRefByGroup = { type: AssetRefType.ByGroup; groupIndex: number; }; /** * Reference to either an explicit asset id or another asset group in the same packet. * * @see AssetId * * @example * ```typescript * const refById = AssetRef.fromId(assetId) * const refByGroup = AssetRef.fromGroupIndex(0) * ``` */ declare class AssetRef { readonly ref: AssetRefByID | AssetRefByGroup; private constructor(); /** Reference type discriminator. */ get type(): AssetRefType; /** * Create an asset reference that points to a specific asset id. * * @param assetId - Asset id referenced by this pointer * @returns Asset reference by id * @see fromGroupIndex */ static fromId(assetId: AssetId): AssetRef; /** * Create an asset reference that points to another asset group by index. * * @param groupIndex - Zero-based asset group index in the packet * @returns Asset reference by group index * @see fromId */ static fromGroupIndex(groupIndex: number): AssetRef; /** * Decode an asset reference from its hex string form. * * @param s - Hex-encoded asset reference * @returns Decoded asset reference * @throws Error if the string is not valid hex or does not encode a valid asset reference * @see toString */ static fromString(s: string): AssetRef; /** * Decode an asset reference from its serialized bytes. * * @param buf - Serialized asset reference bytes * @returns Decoded asset reference * @throws Error if the buffer is empty or malformed */ static fromBytes(buf: Uint8Array): AssetRef; /** * Serialize the asset reference to raw bytes. * * @returns Serialized asset reference bytes * @see fromBytes */ serialize(): Uint8Array; /** * Encode the asset reference to a hex string. * * @returns Hex-encoded asset reference * @see fromString */ toString(): string; /** * Decode an asset reference from a binary reader. * * @param reader - Reader positioned at an asset reference * @returns Decoded asset reference * @throws Error if the type is unknown or the reader does not contain enough bytes */ static fromReader(reader: BufferReader): AssetRef; /** * Serialize the asset reference into an existing binary writer. * * @param writer - Writer to append the asset reference to * @see serialize */ serializeTo(writer: BufferWriter): void; } type AssetInputLocal = { type: AssetInputType.Local; vin: number; amount: bigint; }; type AssetInputIntent = Pick & { type: AssetInputType.Intent; txid: Uint8Array; }; /** * AssetInput represents an input of an asset group. * a local input references a real transaction input and specify the amount in satoshis. * an intent input references an external intent transaction. It is created by the server to handle batch leaf transaction. */ declare class AssetInput { readonly input: AssetInputLocal | AssetInputIntent; private constructor(); /** Gets the transaction input index for an asset input, e.g. 0 */ get vin(): number; /** Gets the amount for an input (in most cases, 330 sats) */ get amount(): bigint; /** Create a local asset input that points at a transaction input index. */ static create(vin: number, amount: bigint | number): AssetInput; /** Create an intent-backed asset input referencing an external intent transaction. */ static createIntent(txid: string, vin: number, amount: bigint | number): AssetInput; /** Decode an asset input from its hex string form. */ static fromString(s: string): AssetInput; /** Decode an asset input from its serialized bytes. */ static fromBytes(buf: Uint8Array): AssetInput; /** Serialize the asset input to raw bytes. */ serialize(): Uint8Array; /** Encode the asset input to a hex string. */ toString(): string; /** Validate the asset input fields. */ validate(): void; /** Decode an asset input from a buffer reader. */ static fromReader(reader: BufferReader): AssetInput; /** Serialize the asset input into an existing buffer writer. */ serializeTo(writer: BufferWriter): void; } /** * AssetInputs represents a list of asset inputs. */ declare class AssetInputs { readonly inputs: AssetInput[]; private constructor(); /** Create a validated list of asset inputs. */ static create(inputs: AssetInput[]): AssetInputs; /** Decode an asset input list from its hex string form. */ static fromString(s: string): AssetInputs; /** Serialize the asset input list to raw bytes. */ serialize(): Uint8Array; /** Encode the asset input list to a hex string. */ toString(): string; /** Validate the asset input list. */ validate(): void; /** Decode an asset input list from a buffer reader. */ static fromReader(reader: BufferReader): AssetInputs; /** Serialize the asset input list into an existing buffer writer. */ serializeTo(writer: BufferWriter): void; } /** * AssetOutput references a real transaction output and specify the amount in satoshis. * it must be present in an AssetGroup. * * @param vout - the output index in the transaction * @param amount - asset amount in satoshis */ declare class AssetOutput { readonly vout: number; readonly amount: bigint; static readonly TYPE_LOCAL = 1; private constructor(); /** Create a local asset output referencing a transaction output index. */ static create(vout: number, amount: bigint | number): AssetOutput; /** Decode an asset output from its hex string form. */ static fromString(s: string): AssetOutput; /** Decode an asset output from its serialized bytes. */ static fromBytes(buf: Uint8Array): AssetOutput; /** Serialize the asset output to raw bytes. */ serialize(): Uint8Array; /** Encode the asset output to a hex string. */ toString(): string; /** Validate the asset output fields. */ validate(): void; /** Decode an asset output from a buffer reader. */ static fromReader(reader: BufferReader): AssetOutput; /** Serialize the asset output into an existing buffer writer. */ serializeTo(writer: BufferWriter): void; } /** * AssetOutputs is a list of AssetOutput references. * it must be present in an AssetGroup. * * @param outputs - the list of asset outputs */ declare class AssetOutputs { readonly outputs: AssetOutput[]; private constructor(); /** Create a validated list of asset outputs. */ static create(outputs: AssetOutput[]): AssetOutputs; /** Decode an asset output list from its hex string form. */ static fromString(s: string): AssetOutputs; /** Serialize the asset output list to raw bytes. */ serialize(): Uint8Array; /** Encode the asset output list to a hex string. */ toString(): string; /** Validate the asset output list. */ validate(): void; /** Decode an asset output list from a buffer reader. */ static fromReader(reader: BufferReader): AssetOutputs; /** Serialize the asset output list into an existing buffer writer. */ serializeTo(writer: BufferWriter): void; } /** * Metadata represents a key-value pair. * @param key - the key * @param value - the value */ declare class Metadata { readonly key: Uint8Array; readonly value: Uint8Array; private constructor(); /** Create a metadata entry from raw key and value bytes. */ static create(key: Bytes, value: Bytes): Metadata; /** Decode metadata from its hex string form. */ static fromString(s: string): Metadata; /** Decode metadata from its serialized bytes. */ static fromBytes(buf: Uint8Array): Metadata; /** Serialize metadata to raw bytes. */ serialize(): Uint8Array; /** Encode metadata to a hex string. */ toString(): string; get keyString(): string; get valueString(): string; /** Validate the metadata key and value. */ validate(): void; /** Decode metadata from a buffer reader. */ static fromReader(reader: BufferReader): Metadata; /** Serialize metadata into an existing buffer writer. */ serializeTo(writer: BufferWriter): void; } declare class MetadataList { readonly items: Metadata[]; static readonly ARK_LEAF_TAG = "ArkadeAssetLeaf"; static readonly ARK_BRANCH_TAG = "ArkadeAssetBranch"; static readonly ARK_LEAF_VERSION = 0; constructor(items: Metadata[]); /** Create a metadata list from its hex string form. */ static fromString(s: string): MetadataList; /** Decode a metadata list from its serialized bytes. */ static fromBytes(buf: Uint8Array): MetadataList; /** Decode a metadata list from a buffer reader. */ static fromReader(reader: BufferReader): MetadataList; /** Serialize the metadata list into an existing buffer writer. */ serializeTo(writer: BufferWriter): void; /** Serialize the metadata list to raw bytes. */ serialize(): Uint8Array; /** Iterate through metadata entries in insertion order. */ [Symbol.iterator](): Iterator; get length(): number; /** Compute the tagged Merkle root for the metadata list. */ hash(): Uint8Array; } /** * An asset group contains inputs, outputs, and all data related to a given asset id. * * @see Packet * @see AssetId * @see AssetRef * * @example * ```typescript * const group = AssetGroup.create( * null, // asset ID: null for new issuance * null, // control asset ID: null when reissuance not needed * [], // asset inputs: empty for new issuance * [AssetOutput.create(0, 1000)], // asset outputs: 1000 units at vout index 0 * [] // metadata: can be empty * ) * ``` */ declare class AssetGroup { readonly assetId: AssetId | null; readonly controlAsset: AssetRef | null; readonly inputs: AssetInput[]; readonly outputs: AssetOutput[]; private readonly metadataList; /** @see create */ constructor(assetId: AssetId | null, controlAsset: AssetRef | null, inputs: AssetInput[], outputs: AssetOutput[], metadata: Metadata[]); /** * Create and validate an asset group. * * @param assetId - Asset id for this group, or `null` for fresh issuance * @param controlAsset - Optional control asset reference for (re) issuance * @param inputs - Asset inputs in the group * @param outputs - Asset outputs in the group * @param metadata - Metadata entries associated with the group * @returns A validated asset group * @throws Error if the group fails validation * @see validate */ static create(assetId: AssetId | null, controlAsset: AssetRef | null, inputs: AssetInput[], outputs: AssetOutput[], metadata: Metadata[]): AssetGroup; /** * Decode an asset group from its hex string form. * * @param s - Hex-encoded asset group * @returns Decoded asset group * @throws Error if the string is not valid hex or does not encode a valid asset group * @see toString */ static fromString(s: string): AssetGroup; /** * Decode an asset group from its serialized bytes. * * @param buf - Serialized asset group bytes * @returns Decoded asset group * @throws Error if the buffer is empty or malformed */ static fromBytes(buf: Uint8Array): AssetGroup; /** * Return true when the group represents an issuance. * * @returns `true` when the group has no asset id */ isIssuance(): boolean; /** * Return true when the group represents a reissuance. * * @returns `true` when the group has an asset id and outputs exceed local inputs * @remarks * Only local inputs contribute to the comparison; intent-backed inputs contribute `0` here. */ isReissuance(): boolean; /** * Serialize the asset group to raw bytes. * * @returns Serialized asset group bytes * @see fromBytes */ serialize(): Uint8Array; /** * Validate the asset group and its child structures. * * @throws Error if the group is empty or violates issuance invariants */ validate(): void; /** * Convert the group into its batch-leaf representation for the given intent txid. * * @param intentTxid - Intent transaction id used to build the leaf input reference * @returns Batch-leaf asset group * @see AssetInput.createIntent */ toBatchLeafAssetGroup(intentTxid: Uint8Array): AssetGroup; /** * Encode the asset group to a hex string. * * @returns Hex-encoded asset group * @see fromString */ toString(): string; /** * Decode an asset group from a binary reader. * * @param reader - Reader positioned at an asset group * @returns Decoded asset group * @throws Error if the encoded group is malformed */ static fromReader(reader: BufferReader): AssetGroup; /** * Serialize the asset group into an existing binary writer. * * @param writer - Writer to append the asset group to */ serializeTo(writer: BufferWriter): void; } /** * ExtensionPacket is the interface that all extension packets must implement. * It mirrors the Go extension.Packet interface. */ interface ExtensionPacket { /** type returns the 1-byte packet type tag */ type(): number; /** serialize returns the raw bytes of the packet (without type or length prefix) */ serialize(): Uint8Array; } /** * UnknownPacket holds a packet whose type is not recognized by this implementation. * It round-trips opaquely: the raw bytes are preserved as-is. */ declare class UnknownPacket implements ExtensionPacket { private readonly packetType; private readonly data; constructor(packetType: number, data: Uint8Array); type(): number; serialize(): Uint8Array; } /** * Packet represents a collection of asset groups. * It encodes/decodes as raw bytes only — OP_RETURN framing is handled by the Extension module. */ declare class Packet implements ExtensionPacket { readonly groups: AssetGroup[]; /** PACKET_TYPE is the 1-byte TLV type tag used in the Extension envelope. */ static readonly PACKET_TYPE = 0; private constructor(); /** Create a validated asset packet from a list of asset groups. */ static create(groups: AssetGroup[]): Packet; /** * fromBytes parses a Packet from raw bytes. */ static fromBytes(buf: Uint8Array): Packet; /** * fromString parses a Packet from a raw hex string (not an OP_RETURN script). */ static fromString(s: string): Packet; /** * type returns the TLV packet type tag. Implements ExtensionPacket interface. */ type(): number; /** Convert the packet into the batch-leaf form for a specific intent transaction id. */ leafTxPacket(intentTxid: Uint8Array): Packet; /** * serialize encodes the packet as raw bytes (varint group count + group data). * Does NOT include OP_RETURN, Arkade magic bytes (`ARK`), or TLV type/length; those are * added by the Extension module. */ serialize(): Uint8Array; /** * toString returns the hex-encoded raw packet bytes. */ toString(): string; /** Validate packet structure and cross-group references. */ validate(): void; private static fromReader; } /** * Cross-repo test vectors for the Arkade Asset ID encodings. * * @remarks * Shipped from `src/` rather than `test/` so consumers outside this package can * pin the same bytes: `lightning-swap-service` imports this value, while NArk * and go-sdk vendor the backing `assetIdVectors.json` from a pinned revision of * this repository. * * The encoding is implemented in seven places across three repos and every * disagreement between them fails silently — a flipped txid makes a covenant * unsatisfiable, a flipped group index resolves to a different asset, a case * mismatch is a routing miss. None of those name their cause, which is what * these vectors are for. * * @see plans/asset-id-shared-vectors.md */ interface AssetIdVectors { readonly version: number; readonly note: string; /** Genesis txid in display order — the 32 bytes an id's leading 64 hex chars carry. */ readonly txid_hex: string; /** `txid_hex` reversed: serialization order, what the introspection opcodes match. */ readonly script_txid_hex: string; readonly valid: ReadonlyArray<{ readonly label: string; readonly group_index: number; /** `txid_hex` with the group index appended little-endian. */ readonly asset_id_hex: string; /** * The group index as a stack item, after script-number encoding and * before push framing. Not the raw u16: `65535` is `ffff00` here and * `ffff` on the wire, and `0` is the empty item. */ readonly script_group_index_item_hex: string; }>; /** Refused by the identity rule (`^[0-9a-f]{68}$`), though not necessarily by the decoder. */ readonly invalid_identity: ReadonlyArray<{ readonly label: string; readonly value: string; readonly why: string; /** Present when the value still decodes: what `toString()` must return. */ readonly normalizes_to?: string; }>; /** Refused by {@link AssetId.fromString} / `fromBytes`. */ readonly invalid_decode: ReadonlyArray<{ readonly label: string; readonly value: string; /** Substring of the thrown message — normative for TS, advisory for other SDKs. */ readonly expected_error: string; }>; /** Refused by {@link AssetId.create} / `validate`. */ readonly invalid_construction: ReadonlyArray<{ readonly label: string; readonly txid_hex: string; readonly group_index: number; readonly expected_error: string; /** Set when the group index cannot be expressed by an SDK whose constructor takes a native u16. */ readonly outside_uint16?: boolean; }>; } /** * The asset-id test vectors, deeply frozen. * * @remarks * Frozen all the way down, not just at the top: every value that matters lives * inside the nested arrays, and the module is a singleton — one consumer * mutating an entry would corrupt it for every other consumer in the process. * * @example * ```typescript * import { asset } from '@arkade-os/sdk' * const { txid_hex, valid } = asset.ASSET_ID_VECTORS * expect(AssetId.create(txid_hex, valid[0].group_index).toString()).toBe(valid[0].asset_id_hex) * ``` */ declare const ASSET_ID_VECTORS: AssetIdVectors; declare const index$1_ASSET_ID_VECTORS: typeof ASSET_ID_VECTORS; type index$1_AssetGroup = AssetGroup; declare const index$1_AssetGroup: typeof AssetGroup; type index$1_AssetId = AssetId; declare const index$1_AssetId: typeof AssetId; type index$1_AssetIdVectors = AssetIdVectors; type index$1_AssetInput = AssetInput; declare const index$1_AssetInput: typeof AssetInput; type index$1_AssetInputType = AssetInputType; declare const index$1_AssetInputType: typeof AssetInputType; type index$1_AssetInputs = AssetInputs; declare const index$1_AssetInputs: typeof AssetInputs; type index$1_AssetOutput = AssetOutput; declare const index$1_AssetOutput: typeof AssetOutput; type index$1_AssetOutputs = AssetOutputs; declare const index$1_AssetOutputs: typeof AssetOutputs; type index$1_AssetRef = AssetRef; declare const index$1_AssetRef: typeof AssetRef; type index$1_AssetRefType = AssetRefType; declare const index$1_AssetRefType: typeof AssetRefType; type index$1_Metadata = Metadata; declare const index$1_Metadata: typeof Metadata; type index$1_MetadataList = MetadataList; declare const index$1_MetadataList: typeof MetadataList; type index$1_Packet = Packet; declare const index$1_Packet: typeof Packet; declare namespace index$1 { export { index$1_ASSET_ID_VECTORS as ASSET_ID_VECTORS, index$1_AssetGroup as AssetGroup, index$1_AssetId as AssetId, type index$1_AssetIdVectors as AssetIdVectors, index$1_AssetInput as AssetInput, index$1_AssetInputType as AssetInputType, index$1_AssetInputs as AssetInputs, index$1_AssetOutput as AssetOutput, index$1_AssetOutputs as AssetOutputs, index$1_AssetRef as AssetRef, index$1_AssetRefType as AssetRefType, index$1_Metadata as Metadata, index$1_MetadataList as MetadataList, index$1_Packet as Packet }; } /** * Creates an asset packet from asset inputs and receivers. * Groups inputs and outputs by asset ID and creates the Packet object * @param assetInputs - map input index -> assets * @param receivers - array of recipients with their asset allocations * @param changeReceiver - (optional) change receiver containing remaining assets * @returns packet containing all asset groups */ declare function createAssetPacket(assetInputs: Map, receivers: Recipient[], changeReceiver?: Recipient): Packet; /** * Selects coins that contain a specific asset. * Returns coins sorted by amount (smallest first for better coin selection). */ declare function selectCoinsWithAsset(coins: ExtendedVirtualCoin[], assetId: string, requiredAmount: bigint): { selected: ExtendedVirtualCoin[]; totalAssetAmount: bigint; }; /** * Thrown when a collaborative-exit / offboard would leave a change VTXO below * the dust threshold. Lets callers (e.g. wallet UI) react with appropriate UX * — for instance, offering to exit the full balance — instead of forwarding a * server-side dust rejection to the user. */ declare class DustChangeError extends Error { readonly change: bigint; readonly dustAmount: bigint; constructor(change: bigint, dustAmount: bigint); } /** Thrown when a collaborative exit's change VTXO is above the server's * per-output ceiling, which arkd answers with `AMOUNT_TOO_HIGH`. */ declare class OversizedChangeError extends Error { readonly change: bigint; readonly maxAmount: bigint; constructor(change: bigint, maxAmount: bigint); } /** * Ramps is a class wrapping `settle` method to provide a more convenient interface for onboarding and offboarding operations. * * @see IWallet.settle * @see onboard * @see offboard * * @example * ```typescript * const ramps = new Ramps(wallet); * const feeInfo = { intentFee: {}, txFeeRate: '1' }; * await ramps.onboard(feeInfo); // onboard all boarding inputs * await ramps.offboard('bc1q...', feeInfo); // collaboratively exit all virtual outputs to an onchain address * ``` */ declare class Ramps { readonly wallet: IWallet; /** * Create convenience wrappers for onboarding and offboarding flows. * * @param wallet - Wallet used to query funds and execute settlement transactions */ constructor(wallet: IWallet); /** * Onboard boarding inputs. * * @param feeInfo - The fee info to deduct from the onboard amount. * @param boardingUtxos - Specific boarding inputs to onboard. If not provided, all boarding inputs will be used. * @param amount - Amount to onboard. If not provided, the total amount of boarding inputs will be onboarded. * @param eventCallback - Optional callback that receives settlement events * @returns The Arkade transaction id created by settlement * @throws Error if no boarding inputs remain after fee deduction or if `amount` exceeds available value * @see IWallet.getBoardingUtxos * @see IWallet.settle * @example * ```typescript * const feeInfo = { intentFee: {}, txFeeRate: '1' }; * const ramps = new Ramps(wallet); * await ramps.onboard(feeInfo); * ``` */ onboard(feeInfo: FeeInfo, boardingUtxos?: ExtendedCoin[], amount?: bigint, eventCallback?: (event: SettlementEvent) => void): ReturnType; /** * Offboard virtual outputs, or collaboratively exit them to an onchain address. * * @param destinationAddress - The destination address to offboard to. * @param feeInfo - The fee info to deduct from the offboard amount. * @param amount - The amount to offboard. If not provided, the total amount of virtual outputs will be offboarded. * @param eventCallback - Optional callback that receives settlement events * @param vtxos - Specific virtual outputs to spend, mirroring {@link onboard}'s * `boardingUtxos`. Omitted, every spendable one is spent — merging the whole * off-chain balance into a single change output. Taken as given like * `settle({ inputs })`: an `amount` these cannot cover is an error, not a top-up. * @returns The Arkade transaction id created by settlement * @throws Error if no virtual outputs remain after fee deduction or the destination address cannot be decoded * @see IWallet.getSpendableVtxos * @see IWallet.settle * @example * ```typescript * const feeInfo = { intentFee: {}, txFeeRate: '1' }; * const ramps = new Ramps(wallet); * await ramps.offboard('bc1q...', feeInfo); * ``` */ offboard(destinationAddress: string, feeInfo: FeeInfo, amount?: bigint, eventCallback?: (event: SettlementEvent) => void, vtxos?: NormalizedExtendedVirtualCoin[]): ReturnType; /** * Collaboratively exit with the DESTINATION paid exactly `amount`, pricing the * fee on that output rather than on a grossed-up figure. {@link offboard} keeps * the other anchor, which needs `g - fee(g) = amount` solved — impossible for * a program charging the whole output. */ offboardExact(params: { destinationAddress: string; feeInfo: FeeInfo; /** Sats the destination receives, exactly. */ amount: bigint; eventCallback?: (event: SettlementEvent) => void; vtxos?: NormalizedExtendedVirtualCoin[]; }): ReturnType; /** Fund the change output: pay its own fee, then judge it against both * server-side bounds. `0n` in is `0n` out — no change output at all. */ private fundChange; } /** * HD-wallet {@link DescriptorProvider} that allocates a fresh signing * descriptor on every call. The provider holds no notion of "current" — it * is a pure rotating allocator. The question of "which descriptor is the * wallet currently bound to?" is answered by querying the contract * repository for active contracts, not by asking this provider. * * State is persisted under `WalletRepository.getWalletState().settings.hd` so * that no storage-schema migration is required when switching a wallet from * single-key to HD. The provider is backed by an {@link HDCapableIdentity}, * which carries the wildcard account descriptor template (for derivation) * and the signing primitives. * * The read-modify-write of the persisted index runs inside the shared per- * repo `updateWalletState` mutex, so two `getNextSigningDescriptor` callers * — including those driving separate `HDDescriptorProvider` instances on * the same repo — can never observe the same index. * * @example * ```ts * const provider = await HDDescriptorProvider.create(identity, walletRepo); * const descriptor = await provider.getNextSigningDescriptor(); * // descriptor: tr([fp/86'/0'/0']xpub/0/0) * const next = await provider.getNextSigningDescriptor(); * // next: tr([fp/86'/0'/0']xpub/0/1) * ``` */ declare class HDDescriptorProvider implements DescriptorProvider, ReceiveRotatorFactory { private readonly identity; private readonly walletRepository; private constructor(); /** * Construct an HDDescriptorProvider. No I/O is performed here; * persisted state is read lazily on the first call to * `getNextSigningDescriptor`. A descriptor-mismatch error surfaces on * first use rather than at boot. */ static create(identity: HDCapableIdentity, walletRepository: WalletRepository): Promise; /** * Allocate the next descriptor and return it. The first call on a fresh * wallet returns descriptor at index 0; subsequent calls return 1, 2, 3, * ... in order. Each call is atomic with respect to other rotations on * the same repo: two concurrent callers can never observe the same * index. */ getNextSigningDescriptor(): Promise; /** * Re-derive the descriptor at the most recently allocated index * WITHOUT advancing — i.e. read the same descriptor * `getNextSigningDescriptor` last returned. Returns `undefined` * when no descriptor has ever been allocated on this repo. * * Used by the boot path to keep the wallet's display address * stable across restarts: when no tagged display contract exists * (e.g. a fresh wallet that hasn't rotated yet, or a wallet whose * baseline-only repo carries no rotation history), the boot should * re-derive the existing index rather than burn a new one. */ getCurrentSigningDescriptor(): Promise; /** * The most recently allocated index, or `undefined` when nothing has ever * been allocated on this repo. Read-only peek at the watermark that * {@link getNextSigningDescriptor} advances. */ getLastIndexUsed(): Promise; /** * Monotonically advance the allocation watermark so the next * `getNextSigningDescriptor()` skips indices discovered by a restore * scan. Never rewinds: a lower or equal `index` is a no-op. * * An invalid `index` (non-integer / negative / past the BIP32 * non-hardened ceiling) is ignored (no-op): persisting it would corrupt * `lastIndexUsed` and make the next `parseSettings()` throw, mirroring * the validation parseSettings already enforces. The upper bound matters * because the damage outlives the call — `materializeDescriptorAt` cannot * derive at or above 2^31, so a watermark parked there would fail every * subsequent allocation for the life of the repo. */ advanceLastIndexUsed(index: number): Promise; /** * Returns true when the given descriptor is derivable from this wallet's * seed. Delegates to the underlying identity, which handles both HD and * simple `tr(pubkey)` descriptors. */ isOurs(descriptor: string): boolean; /** * Signs each request with the key derived from its descriptor. Delegates * to the identity's signing primitives — the identity, not the provider, * holds the seed. */ signWithDescriptor(requests: DescriptorSigningRequest[]): Promise; /** Signs a message using the key derived from `descriptor`. */ signMessageWithDescriptor(descriptor: string, message: Uint8Array, signatureType?: "schnorr" | "ecdsa"): Promise; /** * HD providers participate in receive rotation. The default * factory boot (contract-repo lookup → allocate fresh descriptor) * is exactly what we want, so this just delegates to * {@link WalletReceiveRotator.defaultBoot}. */ createReceiveRotator(opts: ReceiveRotatorBootOpts): Promise; /** * Substitute the wildcard in the identity's account-descriptor template * with a concrete index, going through the descriptors-scure parser * rather than ad-hoc string substitution. The parser's `expand({ index })` * call validates that the input is a ranged template AND produces a * canonical materialized key expression at the given index. * * This is a pure read: it does NOT advance the allocation watermark. * Used by restore's gap-scan to peek descriptors at arbitrary indices * without side-effects. */ materializeDescriptorAt(index: number): string; /** * Run the read-modify-write of HD settings inside the shared per-repo * wallet-state mutex. The closure receives a freshly-validated settings * snapshot, mutates it, and returns whatever value the caller wants to * surface; the mutated settings are then persisted as part of the same * atomic update. * * Doing the read inside the lock is what prevents two providers (or two * concurrent callers on the same provider) from racing on a stale index. */ private mutate; /** * Validate the persisted HD settings (or initialize a fresh record when * absent) and return a clone safe for the caller to mutate. * * The cast to `HDWalletSettings` trusts storage; a corrupted or * partially-migrated repo could otherwise produce `NaN` descriptors. * Fail loud rather than silently derive garbage. */ private parseSettings; } declare class WalletNotInitializedError extends Error { constructor(); } declare class ReadonlyWalletError extends Error { constructor(); } declare class DelegateNotConfiguredError extends Error { constructor(); } /** @deprecated alias for DelegateNotConfiguredError */ declare const DelegatorNotConfiguredError: typeof DelegateNotConfiguredError; type DelegatorNotConfiguredError = DelegateNotConfiguredError; type RequestInitWallet = RequestEnvelope & { type: "INIT_WALLET"; payload: { /** * Legacy per-request key material. Ignored by the current handler — * identity hydration happens during INITIALIZE_MESSAGE_BUS. Retained * for wire compatibility with older workers that may still read it. * Slated for removal in the next major. * * @deprecated Identity is now carried by INITIALIZE_MESSAGE_BUS. */ key?: { privateKey: string; } | { publicKey: string; } | {}; arkServerUrl: string; arkServerPublicKey?: string; }; }; type ResponseInitWallet = ResponseEnvelope & { type: "WALLET_INITIALIZED"; }; type RequestSettle = RequestEnvelope & { type: "SETTLE"; payload: { params?: SettleParams; }; }; type ResponseSettle = ResponseEnvelope & { type: "SETTLE_SUCCESS"; payload: { txid: string; }; }; type RequestSendBitcoin = RequestEnvelope & { type: "SEND_BITCOIN"; payload: SendBitcoinParams; }; type ResponseSendBitcoin = ResponseEnvelope & { type: "SEND_BITCOIN_SUCCESS"; payload: { txid: string; }; }; type RequestGetAddress = RequestEnvelope & { type: "GET_ADDRESS"; }; type ResponseGetAddress = ResponseEnvelope & { type: "ADDRESS"; payload: { address: string; }; }; type RequestGetBoardingAddress = RequestEnvelope & { type: "GET_BOARDING_ADDRESS"; }; type ResponseGetBoardingAddress = ResponseEnvelope & { type: "BOARDING_ADDRESS"; payload: { address: string; }; }; type RequestGetBalance = RequestEnvelope & { type: "GET_BALANCE"; }; type ResponseGetBalance = ResponseEnvelope & { type: "BALANCE"; payload: WalletBalance; }; type RequestGetVtxos = RequestEnvelope & { type: "GET_VTXOS"; payload: { filter?: GetVtxosFilter; }; }; type ResponseGetVtxos = ResponseEnvelope & { type: "VTXOS"; payload: { vtxos: Awaited>; }; }; type RequestGetSpendableVtxos = RequestEnvelope & { type: "GET_SPENDABLE_VTXOS"; payload: { filter?: GetSpendableVtxosFilter; }; }; type ResponseGetSpendableVtxos = ResponseEnvelope & { type: "SPENDABLE_VTXOS"; payload: { vtxos: Awaited>; filterApplied?: boolean; }; }; type RequestGetBoardingUtxos = RequestEnvelope & { type: "GET_BOARDING_UTXOS"; }; type ResponseGetBoardingUtxos = ResponseEnvelope & { type: "BOARDING_UTXOS"; payload: { utxos: ExtendedCoin[]; }; }; type RequestGetTransactionHistory = RequestEnvelope & { type: "GET_TRANSACTION_HISTORY"; }; type ResponseGetTransactionHistory = ResponseEnvelope & { type: "TRANSACTION_HISTORY"; payload: { transactions: ArkTransaction[]; }; }; type RequestGetStatus = RequestEnvelope & { type: "GET_STATUS"; }; type ResponseGetStatus = ResponseEnvelope & { type: "WALLET_STATUS"; payload: { walletInitialized: boolean; xOnlyPublicKey: Uint8Array | undefined; providerConnectionState?: ProviderConnectionState; }; }; type RequestGetContractSyncState = RequestEnvelope & { type: "GET_CONTRACT_SYNC_STATE"; }; type ResponseGetContractSyncState = ResponseEnvelope & { type: "CONTRACT_SYNC_STATE"; payload: { syncState: ContractSyncState; }; }; type RequestClear = RequestEnvelope & { type: "CLEAR"; }; type ResponseClear = ResponseEnvelope & { type: "CLEAR_SUCCESS"; payload: { cleared: boolean; }; }; type RequestSignTransaction = RequestEnvelope & { type: "SIGN_TRANSACTION"; payload: { tx: Transaction; inputIndexes?: number[]; }; }; type ResponseSignTransaction = ResponseEnvelope & { type: "SIGN_TRANSACTION"; payload: { tx: Transaction; }; }; type RequestReloadWallet = RequestEnvelope & { type: "RELOAD_WALLET"; }; type ResponseReloadWallet = ResponseEnvelope & { type: "RELOAD_SUCCESS"; payload: { reloaded: boolean; }; }; type RequestCreateContract = RequestEnvelope & { type: "CREATE_CONTRACT"; payload: CreateContractParams; }; type ResponseCreateContract = ResponseEnvelope & { type: "CONTRACT_CREATED"; payload: { contract: Contract; }; }; type RequestGetContracts = RequestEnvelope & { type: "GET_CONTRACTS"; payload: { filter?: GetContractsFilter; }; }; type ResponseGetContracts = ResponseEnvelope & { type: "CONTRACTS"; payload: { contracts: Contract[]; }; }; type RequestGetContractsWithVtxos = RequestEnvelope & { type: "GET_CONTRACTS_WITH_VTXOS"; payload: { filter?: GetContractsFilter; options?: { maxSyncAgeMs?: number; unspentOnly?: boolean; requireSynced?: boolean; }; }; }; type ResponseGetContractsWithVtxos = ResponseEnvelope & { type: "CONTRACTS_WITH_VTXOS"; payload: { contracts: ContractWithVtxos[]; filterApplied?: boolean; }; }; type RequestWatchScript = RequestEnvelope & { type: "WATCH_SCRIPT"; payload: { script: string | string[]; label?: string; }; }; type ResponseWatchScript = ResponseEnvelope & { type: "SCRIPT_WATCHED"; payload: { script: string | string[]; }; }; type RequestUnwatchScript = RequestEnvelope & { type: "UNWATCH_SCRIPT"; payload: { script: string | string[]; }; }; type ResponseUnwatchScript = ResponseEnvelope & { type: "SCRIPT_UNWATCHED"; payload: { script: string | string[]; }; }; type RequestGetWatchedScripts = RequestEnvelope & { type: "GET_WATCHED_SCRIPTS"; payload: Record; }; type ResponseGetWatchedScripts = ResponseEnvelope & { type: "WATCHED_SCRIPTS"; payload: { scripts: WatchedScript[]; }; }; type RequestAnnotateVtxos = RequestEnvelope & { type: "ANNOTATE_VTXOS"; payload: { vtxos: VirtualCoin[]; }; }; type ResponseAnnotateVtxos = ResponseEnvelope & { type: "ANNOTATED_VTXOS"; payload: { vtxos: ExtendedVirtualCoin[]; }; }; type RequestUpdateContract = RequestEnvelope & { type: "UPDATE_CONTRACT"; payload: { script: string; updates: Partial>; }; }; type ResponseUpdateContract = ResponseEnvelope & { type: "CONTRACT_UPDATED"; payload: { contract: Contract; }; }; type RequestDeleteContract = RequestEnvelope & { type: "DELETE_CONTRACT"; payload: { script: string; }; }; type ResponseDeleteContract = ResponseEnvelope & { type: "CONTRACT_DELETED"; payload: { deleted: boolean; }; }; type RequestGetSpendablePaths = RequestEnvelope & { type: "GET_SPENDABLE_PATHS"; payload: { options: GetSpendablePathsOptions; }; }; type ResponseGetSpendablePaths = ResponseEnvelope & { type: "SPENDABLE_PATHS"; payload: { paths: PathSelection[]; }; }; type RequestIsContractManagerWatching = RequestEnvelope & { type: "IS_CONTRACT_MANAGER_WATCHING"; }; type ResponseIsContractManagerWatching = ResponseEnvelope & { type: "CONTRACT_WATCHING"; payload: { isWatching: boolean; }; }; type RequestRefreshVtxos = RequestEnvelope & { type: "REFRESH_VTXOS"; payload?: { scripts?: string[]; after?: number; before?: number; }; }; type ResponseRefreshVtxos = ResponseEnvelope & { type: "REFRESH_VTXOS_SUCCESS"; }; type RequestRefreshOutpoints = RequestEnvelope & { type: "REFRESH_OUTPOINTS"; payload: { outpoints: { txid: string; vout: number; }[]; }; }; type ResponseRefreshOutpoints = ResponseEnvelope & { type: "REFRESH_OUTPOINTS_SUCCESS"; }; type RequestGetCurrentSigningDescriptor = RequestEnvelope & { type: "GET_CURRENT_SIGNING_DESCRIPTOR"; }; type ResponseGetCurrentSigningDescriptor = ResponseEnvelope & { type: "CURRENT_SIGNING_DESCRIPTOR"; payload: { descriptor?: string; }; }; type RequestGetNextSigningDescriptor = RequestEnvelope & { type: "GET_NEXT_SIGNING_DESCRIPTOR"; }; type ResponseGetNextSigningDescriptor = ResponseEnvelope & { type: "NEXT_SIGNING_DESCRIPTOR"; payload: { descriptor?: string; }; }; type RequestGetNewAddresses = RequestEnvelope & { type: "GET_NEW_ADDRESSES"; payload?: GetNewAddressesOptions; }; type ResponseGetNewAddresses = ResponseEnvelope & { type: "NEW_ADDRESSES"; payload: { addresses: NewAddress[]; }; }; type RequestGetUsedSigningDescriptors = RequestEnvelope & { type: "GET_USED_SIGNING_DESCRIPTORS"; payload?: { lookAhead?: number; }; }; type ResponseGetUsedSigningDescriptors = ResponseEnvelope & { type: "USED_SIGNING_DESCRIPTORS"; payload: { descriptors: string[]; }; }; type RequestAdvanceSigningDescriptorWatermark = RequestEnvelope & { type: "ADVANCE_SIGNING_DESCRIPTOR_WATERMARK"; payload: { descriptor: string; }; }; type ResponseAdvanceSigningDescriptorWatermark = ResponseEnvelope & { type: "SIGNING_DESCRIPTOR_WATERMARK_ADVANCED"; }; type RequestGetAllSpendingPaths = RequestEnvelope & { type: "GET_ALL_SPENDING_PATHS"; payload: { options: GetAllSpendingPathsOptions; }; }; type ResponseGetAllSpendingPaths = ResponseEnvelope & { type: "ALL_SPENDING_PATHS"; payload: { paths: PathSelection[]; }; }; type ResponseSettleEvent = ResponseEnvelope & { broadcast: true; type: "SETTLE_EVENT"; payload: SettlementEvent; }; type ResponseRecoverVtxosEvent = ResponseEnvelope & { type: "RECOVER_VTXOS_EVENT"; payload: SettlementEvent; }; type ResponseRenewVtxosEvent = ResponseEnvelope & { type: "RENEW_VTXOS_EVENT"; payload: SettlementEvent; }; type ResponseMigrateDeprecatedSignerVtxosEvent = ResponseEnvelope & { type: "MIGRATE_DEPRECATED_SIGNER_VTXOS_EVENT"; payload: SettlementEvent; }; type ResponseUtxoUpdate = ResponseEnvelope & { broadcast: true; type: "UTXO_UPDATE"; payload: { coins: ExtendedCoin[]; }; }; type ResponseVtxoUpdate = ResponseEnvelope & { broadcast: true; type: "VTXO_UPDATE"; payload: { newVtxos: ExtendedCoin[]; spentVtxos: ExtendedCoin[]; }; }; type ResponseContractEvent = ResponseEnvelope & { tag: string; broadcast: true; type: "CONTRACT_EVENT"; payload: { event: ContractEvent; }; }; type RequestSend = RequestEnvelope & { type: "SEND"; payload: { recipients: [Recipient, ...Recipient[]]; /** @see SendParams.selectedVtxos */ selectedVtxos?: ExtendedVirtualCoin[]; }; }; type ResponseSend = ResponseEnvelope & { type: "SEND_SUCCESS"; payload: { txid: string; }; }; type RequestGetAssetDetails = RequestEnvelope & { type: "GET_ASSET_DETAILS"; payload: { assetId: string; }; }; type ResponseGetAssetDetails = ResponseEnvelope & { type: "ASSET_DETAILS"; payload: { assetDetails: AssetDetails; }; }; type RequestIssue = RequestEnvelope & { type: "ISSUE"; payload: { params: IssuanceParams; }; }; type ResponseIssue = ResponseEnvelope & { type: "ISSUE_SUCCESS"; payload: { result: IssuanceResult; }; }; type RequestReissue = RequestEnvelope & { type: "REISSUE"; payload: { params: ReissuanceParams; }; }; type ResponseReissue = ResponseEnvelope & { type: "REISSUE_SUCCESS"; payload: { txid: string; }; }; type RequestBurn = RequestEnvelope & { type: "BURN"; payload: { params: BurnParams; }; }; type ResponseBurn = ResponseEnvelope & { type: "BURN_SUCCESS"; payload: { txid: string; }; }; type RequestDelegate = RequestEnvelope & { type: "DELEGATE"; payload: { vtxoOutpoints: { txid: string; vout: number; }[]; destination: string; delegateAt?: number; }; }; type ResponseDelegate = ResponseEnvelope & { type: "DELEGATE_SUCCESS"; payload: { delegated: { txid: string; vout: number; }[]; failed: { outpoints: { txid: string; vout: number; }[]; error: string; }[]; }; }; type RequestGetDelegateInfo = RequestEnvelope & { type: "GET_DELEGATE_INFO"; }; type ResponseGetDelegateInfo = ResponseEnvelope & { type: "DELEGATE_INFO"; payload: { info: DelegateInfo; }; }; type RequestRecoverVtxos = RequestEnvelope & { type: "RECOVER_VTXOS"; }; type ResponseRecoverVtxos = ResponseEnvelope & { type: "RECOVER_VTXOS_SUCCESS"; payload: { txid: string; }; }; type RequestGetRecoverableBalance = RequestEnvelope & { type: "GET_RECOVERABLE_BALANCE"; }; type ResponseGetRecoverableBalance = ResponseEnvelope & { type: "RECOVERABLE_BALANCE"; payload: { recoverable: string; subdust: string; includesSubdust: boolean; vtxoCount: number; }; }; type RequestGetExpiringVtxos = RequestEnvelope & { type: "GET_EXPIRING_VTXOS"; payload: { thresholdMs?: number; }; }; type ResponseGetExpiringVtxos = ResponseEnvelope & { type: "EXPIRING_VTXOS"; payload: { vtxos: ExtendedVirtualCoin[]; }; }; type RequestRenewVtxos = RequestEnvelope & { type: "RENEW_VTXOS"; payload?: RenewVtxosOptions; }; type ResponseRenewVtxos = ResponseEnvelope & { type: "RENEW_VTXOS_SUCCESS"; payload: { txid: string; }; }; type RequestGetExpiredBoardingUtxos = RequestEnvelope & { type: "GET_EXPIRED_BOARDING_UTXOS"; }; type ResponseGetExpiredBoardingUtxos = ResponseEnvelope & { type: "EXPIRED_BOARDING_UTXOS"; payload: { utxos: ExtendedCoin[]; }; }; type RequestSweepExpiredBoardingUtxos = RequestEnvelope & { type: "SWEEP_EXPIRED_BOARDING_UTXOS"; }; type ResponseSweepExpiredBoardingUtxos = ResponseEnvelope & { type: "SWEEP_EXPIRED_BOARDING_UTXOS_SUCCESS"; payload: { txid: string; }; }; type WireMigrationVtxoRef = { txid: string; vout: number; value: number; signerPubKey: string; cutoffDate?: string; }; type WireDeprecatedSignerReport = { signerPubKey: string; status: SignerStatus; cutoffDate?: string; secondsUntilCutoff?: number; vtxoCount: number; totalValue: number; boardingCount: number; boardingValue: number; recoverableCount: number; recoverableValue: number; awaitingSweepCount: number; awaitingSweepValue: number; nextSweepEta?: number; }; type WireMigrationLegReport = { txid?: string; migrated: WireMigrationVtxoRef[]; skipped?: MigrationLegSkipReason; deferred?: number; oversized?: WireMigrationVtxoRef[]; error?: string; }; type WireDeprecatedSignerMigrationReport = { rotated: boolean; skipped?: MigrationGlobalSkipReason; vtxos?: WireMigrationLegReport; boarding?: WireMigrationLegReport; expired: WireMigrationVtxoRef[]; signers: WireDeprecatedSignerReport[]; }; type RequestMigrateDeprecatedSignerVtxos = RequestEnvelope & { type: "MIGRATE_DEPRECATED_SIGNER_VTXOS"; }; type ResponseMigrateDeprecatedSignerVtxos = ResponseEnvelope & { type: "MIGRATE_DEPRECATED_SIGNER_VTXOS_SUCCESS"; payload: { report: WireDeprecatedSignerMigrationReport; }; }; type RequestGetDeprecatedSignerStatus = RequestEnvelope & { type: "GET_DEPRECATED_SIGNER_STATUS"; }; type ResponseGetDeprecatedSignerStatus = ResponseEnvelope & { type: "DEPRECATED_SIGNER_STATUS"; payload: { signers: WireDeprecatedSignerReport[]; }; }; type RequestRestoreWallet = RequestEnvelope & { type: "RESTORE_WALLET"; payload: { gapLimit?: number; }; }; type ResponseRestoreWallet = ResponseEnvelope & { type: "RESTORE_WALLET_SUCCESS"; }; type WalletUpdaterRequest = RequestInitWallet | RequestSettle | RequestSendBitcoin | RequestGetAddress | RequestGetBoardingAddress | RequestGetBalance | RequestGetVtxos | RequestGetSpendableVtxos | RequestGetBoardingUtxos | RequestGetTransactionHistory | RequestGetStatus | RequestGetContractSyncState | RequestClear | RequestReloadWallet | RequestSignTransaction | RequestCreateContract | RequestGetContracts | RequestGetContractsWithVtxos | RequestWatchScript | RequestUnwatchScript | RequestGetWatchedScripts | RequestAnnotateVtxos | RequestUpdateContract | RequestDeleteContract | RequestGetSpendablePaths | RequestGetAllSpendingPaths | RequestIsContractManagerWatching | RequestRefreshVtxos | RequestRefreshOutpoints | RequestGetCurrentSigningDescriptor | RequestGetNextSigningDescriptor | RequestGetNewAddresses | RequestGetUsedSigningDescriptors | RequestAdvanceSigningDescriptorWatermark | RequestSend | RequestGetAssetDetails | RequestIssue | RequestReissue | RequestBurn | RequestDelegate | RequestGetDelegateInfo | RequestRecoverVtxos | RequestGetRecoverableBalance | RequestGetExpiringVtxos | RequestRenewVtxos | RequestGetExpiredBoardingUtxos | RequestSweepExpiredBoardingUtxos | RequestMigrateDeprecatedSignerVtxos | RequestGetDeprecatedSignerStatus | RequestRestoreWallet; type WalletUpdaterResponse = ResponseEnvelope & (ResponseInitWallet | ResponseSettle | ResponseSettleEvent | ResponseSendBitcoin | ResponseGetAddress | ResponseGetBoardingAddress | ResponseGetBalance | ResponseGetVtxos | ResponseGetSpendableVtxos | ResponseGetBoardingUtxos | ResponseGetTransactionHistory | ResponseGetStatus | ResponseGetContractSyncState | ResponseClear | ResponseReloadWallet | ResponseUtxoUpdate | ResponseVtxoUpdate | ResponseSignTransaction | ResponseCreateContract | ResponseGetContracts | ResponseGetContractsWithVtxos | ResponseWatchScript | ResponseUnwatchScript | ResponseGetWatchedScripts | ResponseAnnotateVtxos | ResponseUpdateContract | ResponseDeleteContract | ResponseGetSpendablePaths | ResponseGetAllSpendingPaths | ResponseIsContractManagerWatching | ResponseRefreshVtxos | ResponseRefreshOutpoints | ResponseGetCurrentSigningDescriptor | ResponseGetNextSigningDescriptor | ResponseGetNewAddresses | ResponseGetUsedSigningDescriptors | ResponseAdvanceSigningDescriptorWatermark | ResponseContractEvent | ResponseSend | ResponseGetAssetDetails | ResponseIssue | ResponseReissue | ResponseBurn | ResponseDelegate | ResponseGetDelegateInfo | ResponseRecoverVtxos | ResponseRecoverVtxosEvent | ResponseGetRecoverableBalance | ResponseGetExpiringVtxos | ResponseRenewVtxos | ResponseRenewVtxosEvent | ResponseGetExpiredBoardingUtxos | ResponseSweepExpiredBoardingUtxos | ResponseMigrateDeprecatedSignerVtxos | ResponseMigrateDeprecatedSignerVtxosEvent | ResponseGetDeprecatedSignerStatus | ResponseRestoreWallet); declare class WalletMessageHandler implements MessageHandler { readonly messageTag: string; private wallet; private readonlyWallet; private arkProvider; private indexerProvider; private walletRepository; private incomingFundsSubscription; private contractEventsSubscription; private onNextTick; private channel; private incomingFundsEpoch; private contractEventsEpoch; /** Per-request progress callbacks: ends with the handler, not with a re-init. */ private requestEpoch; /** * Instantiate a new WalletUpdater. * Can override the default `messageTag` allowing more than one updater to run in parallel. * Note that the default ServiceWorkerWallet sends messages to the default WalletUpdater tag. */ constructor(options?: { messageTag?: string; }); start(...params: Parameters["start"]>): Promise; stop(): Promise; tick(_now: number): Promise; private epochOf; private newGeneration; /** * A handle rather than a `broadcast(response, epoch)` call, because it can * only be written where the closure it guards is created — an epoch passed * at the call site reads identically whether it guards anything or not. */ private newEmitter; private requireWallet; private tagged; isLongRunning(message: WalletUpdaterRequest): boolean; handleMessage(message: WalletUpdaterRequest): Promise; private handleInitWallet; /** * The worker's own balance. Same bucketing rules as `Wallet.getBalance` — * both call {@link computeOffchainBalance} — but deliberately a different * freshness: this reads the repository directly, with no indexer sync, so a * polling UI never pays for a network round-trip. That rules out * `pendingRecoveryOutpoints()`, whose snapshot syncs; the same classification * runs over this method's local snapshot instead. */ private handleGetBalance; private getAllBoardingUtxos; private onWalletInitialized; /** * Refresh virtual outputs, boarding inputs, and transaction history from cache. * Shared by onWalletInitialized (full bootstrap) and reloadWallet * (post-refresh), avoiding duplicate subscriptions and VtxoManager restarts. */ private refreshCachedData; /** * Force a full VTXO refresh from the indexer, then refresh cached data. * Used by RELOAD_WALLET to ensure fresh data without re-subscribing * to incoming funds or restarting the VtxoManager. */ private reloadWallet; private handleSettle; private handleSendBitcoin; private handleSignTransaction; private handleDelegate; /** * Ungated repository read mirroring `Wallet.getVtxos`, the raw reporting read. */ private handleGetVtxos; /** Tear down handler subscriptions, then delegate the full wipe to the wallet. */ private clear; /** * Read all virtual outputs from the repository, aggregated across all contract * addresses and the wallet's primary address, with deduplication. */ private getVtxosFromRepo; /** * The worker's equivalent of `ReadonlyWallet.contractSnapshot`: contracts * paired with the VTXOs the repository holds for them, plus the flat * deduplicated list. Purely local — unlike the main-thread snapshot it never * syncs against the indexer. * * One read of the contract rows serves both halves. Reading them twice * (once for the VTXO buckets, once for the gate) races the contract * manager's own writes: a contract registered between the two reads yields * VTXOs with no matching row in the gate, and `gatedContracts` lists only * what it knows is closed — so the coins of a just-registered escrowed * contract would count as available until the next poll. */ private repoSnapshot; /** * Build transaction history from cached virtual outputs, hitting the indexer only for * uncached timestamps. Best-effort, like the plain Wallet path. * * Takes its own {@link repoSnapshot} rather than a caller-supplied VTXO * list: the coins and the gate that judges them have to come off one read * or they answer about different instants, and being the worker's only * history builder means neither caller can supply one without the other. * The snapshot and the boarding read are independent, so they run together * — the same pairing `handleGetBalance` makes. */ private buildTransactionHistoryFromCache; private ensureContractEventBroadcasting; } type RequestType = WalletUpdaterRequest["type"]; type MessageTimeouts = Partial>; type ServiceWorkerWalletMode = "auto" | "static" | "hd"; declare const DEFAULT_MESSAGE_TIMEOUTS: Readonly>; /** * Service Worker-based wallet implementation for browser environments. * * This wallet uses a service worker as a backend to handle wallet logic, * providing secure key storage and transaction signing in web applications. * The service worker runs in a separate thread and can persist data between * browser sessions. * * @example * ```typescript * // SIMPLE: Recommended approach * const wallet = await ServiceWorkerWallet.setup({ * serviceWorkerPath: '/service-worker.js', * arkServerUrl: 'https://arkade.computer', * identity: MnemonicIdentity.fromMnemonic('abandon abandon...') * }); * * // ADVANCED: Manual setup with service worker control * const serviceWorker = await setupServiceWorker("/service-worker.js"); * const wallet = await ServiceWorkerWallet.create({ * serviceWorker, * arkServerUrl: 'https://arkade.computer', * identity: MnemonicIdentity.fromMnemonic('abandon abandon...') * }); * * // Use like any other wallet * const address = await wallet.getAddress(); * const balance = await wallet.getBalance(); * ``` */ interface ServiceWorkerWalletOptions { /** Optional Arkade server public key used to construct and validate Arkade addresses. */ arkServerPublicKey?: string; /** * Base URL of the Arkade server. * * @deprecated Provide an explicit provider via the worker config instead. * URL-based configuration will be removed in a future major version. */ arkServerUrl?: string; /** * Optional override for the indexer URL. * * @deprecated Provide an explicit provider via the worker config instead. */ indexerUrl?: string; /** * Optional override for the Esplora API URL. * * @deprecated Provide an explicit provider via the worker config instead. */ esploraUrl?: string; /** * Repository-backed storage configuration overrides. * Defaults to IndexedDB if unset. */ storage?: StorageConfig; /** Identity used to derive addresses and optionally sign operations. */ identity: ReadonlyIdentity | Identity; /** Optional delegation service URL. */ delegateUrl?: string; /** @deprecated alias for @see ServiceWorkerWalletOptions.delegateUrl */ delegatorUrl?: string; /** * Override the default tag used for messages sent to and received from the service worker. * @see DEFAULT_MESSAGE_TAG */ walletUpdaterTag?: string; /** Timeout used while bootstrapping the message bus inside the service worker. */ messageBusTimeoutMs?: number; /** Optional settlement configuration forwarded to the worker wallet. */ settlementConfig?: SettlementConfig | false; /** * Receive-address strategy forwarded to the worker wallet. * * Service workers can only receive serializable configuration, so the * descriptor-provider object form accepted by `Wallet.create()` is not * supported here. */ walletMode?: ServiceWorkerWalletMode; /** Optional contract watcher configuration forwarded to the worker wallet. */ watcherConfig?: Partial>; /** * HD look-ahead window forwarded to the worker wallet. Only takes effect * together with `walletMode: 'hd'`. * * @see WalletConfig.lookAheadWindow */ lookAheadWindow?: number; /** * Timelock floors forwarded to the worker wallet. Lowering either below its * per-network default relaxes a fund-safety bound; intended for local * testing. The fast public test networks whose arkd runs below the * mainnet-grade checkpoint floor (signet, mutinynet) are defaulted to their * own advertised value, so neither needs these set. * * @see WalletConfig.minBatchExpirySeconds */ minBatchExpirySeconds?: bigint; /** @see WalletConfig.minCheckpointExitDelaySeconds */ minCheckpointExitDelaySeconds?: bigint; /** * Per-request timeout overrides for wallet-updater messages. * @see DEFAULT_MESSAGE_TIMEOUTS */ messageTimeouts?: MessageTimeouts; } /** * Options for creating a service-worker wallet with an existing worker instance. * * @see ServiceWorkerReadonlyWallet.create * @see ServiceWorkerWallet.create */ type ServiceWorkerWalletCreateOptions = ServiceWorkerWalletOptions & { /** Existing service worker instance used for messaging. */ serviceWorker: ServiceWorker; }; /** * Options for registering a service worker and then creating a wallet around it. * * @see ServiceWorkerReadonlyWallet.setup * @see ServiceWorkerWallet.setup */ type ServiceWorkerWalletSetupOptions = ServiceWorkerWalletOptions & { /** Path to the service worker script to register. */ serviceWorkerPath: string; /** Timeout while waiting for the service worker to activate. */ serviceWorkerActivationTimeoutMs?: number; }; type MessageBusInitConfig = { wallet: SerializedIdentity | LegacySerializedIdentity; arkServer: { url: string; publicKey?: string; }; delegateUrl?: string; /** @deprecated alias for @see MessageBusInitConfig.delegateUrl */ delegatorUrl?: string; indexerUrl?: string; esploraUrl?: string; timeoutMs?: number; settlementConfig?: SettlementConfig | false; walletMode?: ServiceWorkerWalletMode; watcherConfig?: Partial>; lookAheadWindow?: number; minBatchExpirySeconds?: bigint; minCheckpointExitDelaySeconds?: bigint; messageTimeouts?: Record; }; declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet { readonly serviceWorker: ServiceWorker; protected readonly messageTag: string; readonly walletRepository: WalletRepository; readonly contractRepository: ContractRepository; readonly identity: ReadonlyIdentity; readonly activity: ActivityRegistry; private readonly _readonlyAssetManager; protected initConfig: MessageBusInitConfig | null; protected initWalletPayload: RequestInitWallet["payload"] | null; protected messageBusTimeoutMs?: number; protected messageTimeouts: Record; protected arkServerUrl?: string; protected arkServerPublicKey?: string; protected delegateUrl?: string; /** @deprecated alias for @see ServiceWorkerReadonlyWallet.delegateUrl */ protected delegatorUrl?: string; protected indexerUrl?: string; protected esploraUrl?: string; protected watcherConfig?: Partial>; protected settlementConfig?: SettlementConfig | false; private reinitPromise; private pingPromise; private inflightRequests; get assetManager(): IReadonlyAssetManager; protected constructor(serviceWorker: ServiceWorker, identity: ReadonlyIdentity, walletRepository: WalletRepository, contractRepository: ContractRepository, messageTag: string); private getTimeoutForRequest; /** * Create a readonly service-worker wallet bound to an already-registered worker. * * @param options - Service worker, identity, and backend configuration * @returns Initialized readonly service-worker wallet * @throws Error if service-worker initialization fails */ static create(options: ServiceWorkerWalletCreateOptions): Promise; /** * Simplified setup method that handles service worker registration * and wallet initialization automatically. * * @see ServiceWorkerReadonlyWallet.create * * @example * ```typescript * const wallet = await ServiceWorkerReadonlyWallet.setup({ * serviceWorkerPath: '/service-worker.js', * arkServerUrl: 'https://arkade.computer', * identity: ReadonlySingleKey.fromPublicKey('your_public_key_hex') * }); * ``` */ static setup(options: ServiceWorkerWalletSetupOptions): Promise; private sendMessageDirect; private sendMessageStreaming; protected sendMessage(request: WalletUpdaterRequest): Promise; private pingServiceWorker; protected sendMessageWithRetry(request: WalletUpdaterRequest, withEvents?: { onEvent: (response: WalletUpdaterResponse) => void; isComplete: (response: WalletUpdaterResponse) => boolean; }): Promise; /** * Produce a serialized envelope for the wallet's identity. The base * class always emits a readonly envelope; `ServiceWorkerWallet` * overrides to emit a signing envelope. */ protected serializeIdentity(): Promise; /** * Return the cached init config, or rebuild one from live instance * state when the cache was never populated. Recovery path for * SDK-factory-created wallets; manual constructor bypasses do not * retain enough state here and will hit the "never initialized" throw. */ protected buildInitConfig(): Promise; /** Minimal INIT_WALLET payload used on reinitialize when the cache is gone. */ protected buildInitWalletPayload(): RequestInitWallet["payload"]; private reinitialize; /** * Verify the worker is bound to this wallet's identity before the SDK hands * back (or recovers) a usable wallet object. * * Compares the stable baseline identity key (`identity.xOnlyPublicKey()`) to the one reported * by the worker via GET_STATUS. */ protected assertWorkerIdentityMatches(): Promise; /** This tells the service worker to wipe all locally persisted wallet data. */ clear(): Promise; getAddress(): Promise; getBoardingAddress(): Promise; getBalance(): Promise; getBoardingUtxos(): Promise; /** * Return service-worker wallet status, including connectivity and sync state. * * @returns Current service-worker wallet status payload including `walletInitalized` and `xOnlyPublicKey` */ getStatus(): Promise; /** * Wallet-level provider-connection freshness, delegated to the worker via * `GET_STATUS`. Async by necessity (the worker boundary is asynchronous); no * synchronous variant is offered because it would have the same transport * mismatch as the contract-manager proxy's cached `getSyncState()`. */ getProviderConnectionState(): Promise; getActivityHistory(): Promise; getTransactionHistory(): Promise; getVtxos(filter?: GetVtxosFilter): Promise; /** * The gate has to run *inside* the worker (no plugin object or contract row * metadata exists on this side), so this is its own message rather than a * post-filter over `GET_VTXOS`. * * A worker predating this message answers "Unknown message" and the call * throws. That is deliberate: service workers activate asynchronously, so * new page code runs against a stale worker for a window on every deploy, * and falling back to `GET_VTXOS` there would silently spend ungated coins. * Fail closed — loud and recoverable — rather than make the gate advisory. */ getSpendableVtxos(filter?: GetSpendableVtxosFilter): Promise; /** * Trigger a wallet reload inside the service worker. * * @returns `true` when the wallet was reloaded */ reload(): Promise; getContractManager(): Promise; } declare class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet implements IWallet, HDWalletCapable, HDAllocationCapable, AddressAllocationCapable { readonly serviceWorker: ServiceWorker; readonly walletRepository: WalletRepository; readonly contractRepository: ContractRepository; readonly identity: Identity; private readonly _assetManager; private readonly hasDelegate; private _restoreInFlight?; protected constructor(serviceWorker: ServiceWorker, identity: Identity, walletRepository: WalletRepository, contractRepository: ContractRepository, messageTag: string, hasDelegate: boolean); get assetManager(): IAssetManager; protected serializeIdentity(): Promise; static create(options: ServiceWorkerWalletCreateOptions): Promise; /** * Simplified setup method that handles service worker registration * and wallet initialization automatically. * * @example * ```typescript * const wallet = await ServiceWorkerWallet.setup({ * serviceWorkerPath: '/service-worker.js', * arkServerUrl: 'https://arkade.computer', * identity: MnemonicIdentity.fromMnemonic('abandon abandon...') * }); * ``` */ static setup(options: ServiceWorkerWalletSetupOptions): Promise; /** @see HDWalletCapable.getCurrentSigningDescriptor */ getCurrentSigningDescriptor(): Promise; /** @see HDAllocationCapable.getNextSigningDescriptor */ getNextSigningDescriptor(): Promise; /** * @see Wallet.getNewAddresses * * Proxied as a single request rather than rebuilt page-side out of * {@link getNextSigningDescriptor}: the worker owns the HD watermark *and* * the contract repository, so splitting the operation would let a page burn * an index and then fail to register the script, leaving an address nothing * watches. * * Note the error shape — `WalletCannotAllocateAddressError` is thrown * worker-side and cannot cross the message boundary as a class, so a * `forceNew` refusal surfaces here as a plain `Error` carrying its message * (and the worker-side error as `cause`). Callers that must branch on the * refusal match the message rather than `instanceof`. */ getNewAddresses(opts?: GetNewAddressesOptions): Promise; /** @see HDWalletCapable.getUsedSigningDescriptors */ getUsedSigningDescriptors(opts?: { lookAhead?: number; }): Promise; /** @see HDAllocationCapable.advanceSigningDescriptorWatermark */ advanceSigningDescriptorWatermark(descriptor: string): Promise; /** * @see HDWalletCapable.signerForDescriptor * * Runs page-side: an `Identity` cannot cross the message bus, and it does * not need to — the page owns the signing identity, and resolving a * descriptor reads no allocation state. Shares * {@link resolveDescriptorSigner} with {@link Wallet.signerForDescriptor} * so the two sides of the bus cannot answer differently for one * descriptor. * * No provider is passed, and none is needed: `HDDescriptorProvider`'s * `isOurs` and signing members all delegate to the identity, which the * page holds. Building one here would only add a page-side read of the * wallet state the worker allocates from — shared mutable state on a path * that has no business touching it. */ signerForDescriptor(descriptor: string): Promise; sendBitcoin(params: SendBitcoinParams): Promise; settle(params?: SettleParams, callback?: (event: SettlementEvent) => void): Promise; /** * Explicitly recover this wallet's contracts and balance on a fresh repo. * Mirrors {@link Wallet.restore} but drives the scan inside the service * worker — the materialize() callback used by `scanContracts` cannot * cross the postMessage boundary, so the entire flow runs worker-side * and only the gapLimit / outcome cross the wire. * * Uses the streaming send path so the bus deadline does not race a * long indexer-bound scan. AggregateError thrown by the worker is * reconstructed here so callers can inspect `.errors`. */ restore(opts?: { gapLimit?: number; }): Promise; private _restoreWorkerWallet; send(...args: [SendParams] | [Recipient, ...Recipient[]]): Promise; getDelegateManager(): Promise; /** @deprecated alias for @see ServiceWorkerWallet.getDelegateManager */ getDelegatorManager(): Promise; getVtxoManager(): Promise; } /** * A zero-value anchor output. */ declare const P2A: { script: Uint8Array; amount: bigint; }; interface AnchorBumper { bumpP2A(parent: Transaction$1): Promise<[string, string]>; } /** * Onchain Bitcoin wallet implementation for traditional Bitcoin transactions. * * This wallet handles regular Bitcoin transactions on the blockchain without * using the Arkade protocol. It supports P2TR (Pay-to-Taproot) addresses and * provides basic Bitcoin wallet functionality. * * @example * ```typescript * const wallet = await OnchainWallet.create(identity, 'mainnet'); * const balance = await wallet.getBalance(); * const txid = await wallet.send({ * address: 'bc1...', * amount: 50000 * }); * ``` */ declare class OnchainWallet implements AnchorBumper { private identity; static MIN_FEE_RATE: number; readonly onchainP2TR: P2TR; readonly provider: OnchainProvider; readonly network: Network; private constructor(); /** * Create an onchain wallet for the given identity and Bitcoin network. * * @param identity - Identity used to derive the Taproot key and sign transactions * @param networkName - Bitcoin network name, @see NetworkName * @param provider - Optional onchain provider override, @see OnchainProvider * @returns Configured onchain wallet * @throws Error if the configured identity cannot produce a valid x-only public key */ static create(identity: Identity, networkName?: NetworkName, provider?: OnchainProvider): Promise; get address(): string; /** * Fetch spendable onchain outputs for the wallet address. * * @returns Spendable onchain outputs for the wallet address * @see getBalance */ getCoins(): Promise; /** * Return the wallet's total onchain balance in satoshis. * * @returns Confirmed plus unconfirmed onchain balance * @see getCoins */ getBalance(): Promise; /** * Iteratively selects coins and estimates transaction fees until convergence. * * This method handles the circular dependency between output selection and fee * estimation: the fee depends on transaction size, which depends on the number * of inputs (selected outputs) and whether a change output is needed. * * The algorithm iterates up to 10 times, refining the fee estimate based on * the actual transaction structure. It resolves dust oscillation loops that * occur when the change amount hovers near the dust threshold—adding/removing * the change output causes the fee to fluctuate, preventing convergence. * When a lower fee is computed (indicating the change output was dropped), * the function accepts this state to guarantee termination. * * @param coins - Available onchain outputs to select from * @param amount - Target send amount in satoshis * @param feeRate - Fee rate in sat/vbyte * @param recipientAddress - Destination address for size estimation * @returns Selected inputs, change amount, and calculated fee * @throws Error if fee estimation fails to converge within max iterations */ private estimateFeesAndSelectCoins; /** * Send bitcoin to a single onchain address. * * @param params - destination `address`, `amount` (in satoshis), and optional `feeRate` override (other fields ignored) * @returns Broadcast transaction id * @throws Error if the amount is non-positive, below dust, or cannot be funded * @see SendBitcoinParams */ send(params: SendBitcoinParams): Promise; /** * CPFP-bump a parent transaction that contains a pay-to-anchor output. * * @param parent - Parent transaction containing a pay-to-anchor output * @returns Tuple of parent transaction id and child transaction id * @throws Error if the parent transaction has no pay-to-anchor output or bumping cannot be funded * @see send */ bumpP2A(parent: Transaction): Promise<[string, string]>; /** * Build and sign a CPFP fee child for a parent tx (given as raw hex) * carrying a P2A anchor, funding it from this wallet's **confirmed** * coins, and return the 1P1C package hexes WITHOUT broadcasting. * * This is the graph-mode fee source ({@link ExitFeeWallet}): the exit * executor calls it to bump each transported virtual tx at execution * time, so funding can be deferred rather than pre-signed. * * @param parentHex - Finalized parent transaction, raw network hex * @param feeRate - sat/vB floor for the package (raised to MIN_FEE_RATE) * @returns Tuple of parent hex (unchanged) and signed child hex * @throws If the parent has no anchor, or funding cannot be selected/signed */ bumpAnchor(parentHex: string, feeRate: number): Promise<[string, string]>; /** * Shared core of {@link bumpP2A} and {@link bumpAnchor}: probe the package * fee with a single-input child, select coins for it, then build and sign * with the actual selection (the fee grows per extra input). */ private buildBumpPackage; } type SetupServiceWorkerOptions = { path: string; activationTimeoutMs?: number; }; /** * setupServiceWorker sets up the service worker. * @param pathOrOptions - the path to the service worker script or setup options * @throws if service workers are not supported or activation fails * @example * ```typescript * const worker = await setupServiceWorker("/service-worker.js"); * ``` */ declare function setupServiceWorker(pathOrOptions: string | SetupServiceWorkerOptions): Promise; /** * Default WebSocket Electrum endpoints. Mainnet, mutinynet, and signet * point at Ark Labs–operated Fulcrum 2.1 deployments (which support * `blockchain.transaction.broadcast_package` for atomic 1P1C TRUC * relay; see `ElectrumOnchainProvider.broadcastTransaction`). Testnet * defaults to Blockstream's public Fulcrum because Ark doesn't host * it. Regtest assumes the Fulcrum WebSocket endpoint exposed by the * local `arkade-regtest` stack. * * @example * ```typescript * import { ElectrumWS } from "ws-electrumx-client"; * import { ELECTRUM_WS_URL, ElectrumOnchainProvider, networks } from "@arkade-os/sdk"; * * const ws = new ElectrumWS(ELECTRUM_WS_URL.bitcoin); * const provider = new ElectrumOnchainProvider(ws, networks.bitcoin); * ``` */ declare const ELECTRUM_WS_URL: Record; /** * Hostnames for Electrum endpoints reachable over raw TCP. Provided as * a reference for Node-side consumers — the SDK's * {@link ElectrumOnchainProvider} only speaks WebSocket because it has * to run in browsers, so this map is informational only and not * consumed by any built-in provider. * * Public Ark Labs Fulcrum instances expose: * - port 50001 — plain TCP (Electrum protocol) * - port 50002 — TCP + TLS (Electrum protocol) * - port 50003 — WebSocket (Electrum-over-WS, see {@link ELECTRUM_WS_URL}) */ declare const ELECTRUM_TCP_HOST: Record; type TransactionHistory = { tx_hash: string; height: number; fee?: number; }; type BlockHeader = { height: number; hex: string; }; type Unspent = { txid: string; vout: number; witnessUtxo: { script: Uint8Array; value: bigint; }; }; type VerboseTransaction = { txid: string; confirmations: number; blockhash?: string; blocktime?: number; time?: number; /** Raw transaction hex. Bitcoin Core's getrawtransaction 1 always * includes this; we use it to derive exact satoshi amounts instead of * multiplying the floating-point `value` field by 1e8. */ hex?: string; vout: { n: number; value: number; scriptPubKey: { addresses?: string[]; address?: string; hex: string; }; }[]; vin: { txid: string; vout: number; }[]; }; type HeaderSubscribeResult = { height: number; hex: string; }; /** * WebSocket-based Electrum chain source using ws-electrumx-client. * Provides low-level methods for the Electrum protocol. * * @example * ```typescript * import { ElectrumWS } from "ws-electrumx-client"; * import { WsElectrumChainSource } from "./providers/electrum"; * import { networks } from "./networks"; * * const ws = new ElectrumWS("wss://electrum.blockstream.info:50004"); * const chain = new WsElectrumChainSource(ws, networks.bitcoin); * * const history = await chain.fetchHistories([script]); * await chain.close(); * ``` */ declare class WsElectrumChainSource { private ws; private network; private cachedTip; private headersSubscribePromise; constructor(ws: ElectrumWS, network: Network); /** * Send N requests in parallel and aggregate the results, replacement * for `ws.batchRequest`. The library's batchRequest is implemented as * `Promise.all` over individual request promises — when one element * rejects, the others remain pending. When their (often error) * responses arrive later, the library rejects them too, and nobody is * awaiting them: the rejections become unhandled and crash the test * runner / pollute production logs. * * `safeBatchRequest` issues each request through `ws.request` (so each * has its own request-promise lifecycle), waits for all of them via * `Promise.allSettled` (every promise gets an explicit handler), and * then surfaces the first error if any failed. Same wall-clock cost * as the library's batch (parallel send), no orphan rejections. * * Use this in place of `ws.batchRequest` for any call where one or * more elements may legitimately error (e.g. electrs index lag * surfacing as `missingheight` for a subset of heights/txids). */ safeBatchRequest(requests: { method: string; params: unknown[]; }[]): Promise; fetchTransactions(txids: string[]): Promise<{ txID: string; hex: string; }[]>; fetchVerboseTransaction(txid: string): Promise; fetchVerboseTransactions(txids: string[]): Promise; /** * Look up the block height of a confirmed transaction without relying * on the verbose-tx endpoint. `blockchain.transaction.get_merkle` is * part of the standard SPV protocol and is supported by both Fulcrum * and electrs (whereas `blockchain.transaction.get` with verbose=true * is Fulcrum-only). Returns `null` when the tx is in the mempool — * electrs in that case rejects with a "not yet in a block" error. */ fetchTxMerkle(txid: string): Promise<{ blockHeight: number; } | null>; unsubscribeScriptStatus(script: Uint8Array): Promise; subscribeScriptStatus(script: Uint8Array, callback: (scripthash: string, status: string | null) => void): Promise; fetchHistories(scripts: Uint8Array[]): Promise; fetchHistory(script: Uint8Array): Promise; fetchBlockHeaders(heights: number[]): Promise; fetchBlockHeader(height: number): Promise; /** * Returns the current chain tip and keeps it fresh via a single * server-side subscription. Subsequent calls return the cached tip * (updated by background notifications) without round-tripping to the * server. Previously each call issued `blockchain.headers.subscribe` as * a regular request, leaving a stale subscription on the server every * time — under polling that adds up. ws-electrumx-client deduplicates * `subscribe()` by method+params, so registering once is enough. */ subscribeHeaders(): Promise; estimateFees(targetNumberBlocks: number): Promise; broadcastTransaction(txHex: string): Promise; /** * Submit a package of raw transactions atomically via Fulcrum's * `blockchain.transaction.broadcast_package` method, the on-the-wire * equivalent of bitcoind's `submitpackage` RPC. * * Required for TRUC (BIP 431) 1P1C relay where the parent has zero * (or below-minfee) fee and depends on the child to pay for both via * CPFP — sequential broadcast cannot work in that case because the * parent would be rejected from the mempool on its own. * * @param txHexes - Topologically sorted raw transactions; child must * be the last element. Currently must be a 1P1C pair * (length 2). Parents may not depend on each other. * @returns The child transaction id (the last entry in the array), * computed locally — `broadcast_package` itself returns * `{success, errors}` rather than a txid. * @throws If the server does not implement `broadcast_package` (e.g. * ElectrumX, or older Fulcrum, or Fulcrum backed by bitcoind * < v28.0.0). Callers must surface this clearly to users — * this method does NOT silently fall back to sequential * broadcasts because doing so would let TRUC packages fail * in subtle ways. * @throws If the server returns `success=false`, surfacing the * underlying mempool rejection in the error message. */ broadcastPackage(txHexes: string[]): Promise; getRelayFee(): Promise; close(): Promise; waitForAddressReceivesTx(addr: string): Promise; listUnspents(addr: string): Promise; /** * Get the address string for a script output, if decodable. */ addressForScript(scriptHex: string): string | undefined; } /** * Electrum-based implementation of the {@link OnchainProvider} interface. * * Built around the subset of the Electrum protocol that both **Fulcrum** * and **electrs** support — listunspent, get_history, transaction.get * (non-verbose), transaction.get_merkle, block.header, * headers.subscribe, scripthash.subscribe, estimatefee, relayfee, and * broadcast. The verbose form of `transaction.get` is **not** used (it's * Fulcrum-only and rejected by electrs); confirmation status is derived * from `transaction.get_merkle` plus parsed block headers. * * Output amounts are derived from parsed raw transaction bytes (exact * bigints), never the floating-point `value` fields some servers return. * * Atomic 1P1C package broadcast (TRUC / BIP 431) is supported via * Fulcrum's `blockchain.transaction.broadcast_package`. There is **no * fallback** to sequential parent-then-child broadcasts — TRUC packages * with a zero-fee parent would silently fail, so the call surfaces an * error against servers that don't support the method. * * @example Default URL via {@link ELECTRUM_WS_URL} * ```typescript * import { ElectrumWS } from "ws-electrumx-client"; * import { * ElectrumOnchainProvider, * ELECTRUM_WS_URL, * networks, * } from "@arkade-os/sdk"; * * const ws = new ElectrumWS(ELECTRUM_WS_URL.bitcoin); * const provider = new ElectrumOnchainProvider(ws, networks.bitcoin); * * const coins = await provider.getCoins("bc1q..."); * await provider.close(); * ``` * * @example Custom server * ```typescript * const ws = new ElectrumWS("wss://my-fulcrum.example:50004"); * const provider = new ElectrumOnchainProvider(ws, networks.bitcoin); * ``` */ declare class ElectrumOnchainProvider implements OnchainProvider { private ws; private network; private chain; private mtpCache?; private mtpInflight?; constructor(ws: ElectrumWS, network: Network); getCoins(address: string): Promise; getFeeRate(): Promise; /** * Broadcast a single transaction or a TRUC (BIP 431) 1P1C package * atomically. * * **Server requirements for 1P1C packages:** the backing Electrum * server must implement `blockchain.transaction.broadcast_package` * (Fulcrum ≥ 1.10) and be backed by bitcoind ≥ v28.0.0. ElectrumX * does not implement this method. There is **no fallback** to * sequential parent-then-child broadcast: TRUC packages typically * have a zero-fee parent and would be rejected from the mempool on * their own, so a fallback would silently fail in subtle ways. * Callers receiving a "method not found" error here should route * through a different provider for that submission. * * @param txs - One transaction (single broadcast) or two * topologically-sorted transactions (parent first, * child last) for 1P1C package relay. * @returns The broadcast txid (or the child txid for 1P1C packages). */ broadcastTransaction(...txs: string[]): Promise; getRawTransaction(txid: string): Promise; getTxOutspends(txid: string): Promise<{ spent: boolean; txid: string; }[]>; getTransactions(address: string): Promise; /** * Resolve a list of `{tx_hash, height}` entries (as returned by the * scripthash history endpoint) into ExplorerTransaction shape **without * using the verbose-tx endpoint**, which only Fulcrum implements. We * reconstruct everything the verbose response would have given us: * - vouts ← parse the raw tx (exact sat amounts, no float precision risk) * - block_time ← batch-fetch the block headers for the heights present * - addresses ← decode each output's scriptPubKey via @scure/btc-signer */ private historyToExplorerTxs; /** * Build an ExplorerTransaction from a history entry plus the raw tx hex * (when known) and a height→block_time map. Parse errors propagate — * silently returning an empty vout would hide real outputs (e.g. a * deposit) and is far worse for protocol-level money handling than * failing the whole batch. */ private buildExplorerTx; /** * Decode `address` into its scriptPubKey, throwing a clear error if the * input is malformed. @scure/btc-signer raises a generic decode error * which is hard to map back to user input — this wraps it. */ private encodeAddress; getTxStatus(txid: string): Promise<{ confirmed: false; } | { confirmed: true; blockTime: number; blockHeight: number; }>; /** `time` is median-time-past, per {@link OnchainProvider.getChainTip}; * Electrum serves raw headers and no MTP, so it is computed here. */ getChainTip(): Promise<{ height: number; time: number; hash: string; }>; /** * Consensus's `GetMedianTimePast`, ending at and including `height`. * * Cached per tip HASH, not height: this runs on every balance read, and * hashing means a reorg at the same height recomputes rather than serving * the orphaned chain's median. */ private medianTimePast; /** Degrades to the tip's `nTime` rather than throwing — a caller that * cannot read the tip at all is worse off. */ private readMedianTimePast; watchAddresses(addresses: string[], eventCallback: (txs: ExplorerTransaction[]) => void): Promise<() => void>; /** Close the underlying WebSocket connection. */ close(): Promise; } /** * Decorates an {@link ArkProvider}, caching {@link ArkProvider.getInfo} for * `ttlMs` (default 60s). Other provider methods pass through. * Expired-cache refresh failures propagate; persisted wallet boot fallback * lives in `arkInfoSnapshot`. */ declare class CachingArkProvider implements ArkProvider { private readonly inner; private readonly ttlMs; private cached?; private expiresAt; private inflight?; /** Bumped on every event-driven cache write; see {@link getInfo}. */ private generation; private readonly unsubscribe; constructor(inner: ArkProvider, ttlMs?: number); /** Releases the inner provider's server-info subscription. */ dispose(): void; get serverUrl(): string | undefined; getInfo(): Promise; submitTx(signedArkTx: string, checkpointTxs: string[]): Promise<{ arkTxid: string; finalArkTx: string; signedCheckpointTxs: string[]; }>; finalizeTx(arkTxid: string, finalCheckpointTxs: string[]): Promise; registerIntent(intent: SignedIntent): Promise; deleteIntent(intent: SignedIntent): Promise; confirmRegistration(intentId: string): Promise; submitTreeNonces(batchId: string, pubkey: string, nonces: TreeNonces): Promise; submitTreeSignatures(batchId: string, pubkey: string, signatures: TreePartialSigs): Promise; submitSignedForfeitTxs(signedForfeitTxs: string[], signedCommitmentTx?: string): Promise; getEventStream(signal: AbortSignal, topics: string[]): AsyncIterableIterator; getTransactionsStream(signal: AbortSignal): AsyncIterableIterator; getPendingTxs(intent: SignedIntent): Promise; onServerInfoChanged(listener: (info: ArkInfo) => void): () => void; } type ArkTxInput = { tapLeafScript: TapLeafScript; } & EncodedVtxoScript & Pick; type OffchainTx = { arkTx: Transaction; checkpoints: Transaction[]; }; /** * Builds an offchain transaction with checkpoint transactions. * * Creates one checkpoint transaction per input and a virtual transaction that * combines all the checkpoints, sending to the specified outputs. This is the * core function for creating Arkade transactions. * * @param inputs - Array of virtual transaction inputs * @param outputs - Array of transaction outputs * @param serverUnrollScript - Server unroll script for checkpoint transactions * @returns Object containing the virtual transaction and checkpoint transactions */ declare function buildOffchainTx(inputs: ArkTxInput[], outputs: TransactionOutput[], serverUnrollScript: CSVMultisigTapscript.Type): OffchainTx; declare function hasBoardingTxExpired(coin: ExtendedCoin, boardingTimelock: RelativeTimelock, chainTipHeight?: number): boolean; /** * Verify tapscript signatures on a transaction input * @param tx Transaction to verify * @param inputIndex Index of the input to verify * @param requiredSigners List of required signer pubkeys (hex encoded) * @param excludePubkeys List of pubkeys to exclude from verification (hex encoded, e.g., server key not yet signed) * @param allowedSighashTypes List of allowed sighash types (defaults to [SigHash.DEFAULT]) * @param expectedLeafHash Tapscript leaf hash the signatures must commit to. When omitted, a * signature over any leaf carried by the input is accepted. * @throws Error if verification fails */ declare function verifyTapscriptSignatures(tx: Transaction, inputIndex: number, requiredSigners: string[], excludePubkeys?: string[], allowedSighashTypes?: number[], expectedLeafHash?: Uint8Array): void; /** * Merges the tapscript signatures of `signedTx` onto `originalTx`, in place. * * Invariant: both transactions must have the same number of inputs and BOTH * must carry a `tapScriptSig` on every input — the result is the per-input * concatenation `originalTx.tapScriptSig ++ signedTx.tapScriptSig`. A missing * signature on either side is rejected with an input-indexed error rather than * silently corrupting the witness (the previous code appended `undefined` when * `signedTx` was unsigned). Callers that partially sign must merge only fully * co-signed inputs. * * @param signedTx signed transaction * @param originalTx original transaction (mutated and returned) */ declare function combineTapscriptSigs(signedTx: Transaction, originalTx: Transaction): Transaction; /** * Validates if a given string is a valid Arkade address by attempting to decode it. * @param address The Arkade address to validate. * @returns True if the address is valid, false otherwise. */ declare function isValidArkAddress(address: string): boolean; /** * Minimal Ark provider surface required to submit and finalize an offchain * transaction. Both {@link ArkProvider} implementations satisfy it * structurally, so declaring it here keeps this module free of a provider * import (and of the dependency cycle that would create). */ interface OffchainTxSubmitProvider { submitTx(signedArkTx: string, checkpointTxs: string[]): Promise<{ arkTxid: string; finalArkTx?: string; signedCheckpointTxs: string[]; }>; finalizeTx(arkTxid: string, finalCheckpointTxs: string[]): Promise; } /** * Pair each server-returned checkpoint PSBT with the locally built checkpoint * of the same txid, rejecting anything else. * * The ark tx signed just above spends each checkpoint at `checkpointTxid:0`, so * the response is only usable if it carries the same txids: a checkpoint with * any other txid leaves that ark tx unspendable. The two sets are therefore * equal by construction, and this pairing is what the signing step below * consumes. * * Matching is by txid rather than by position — nothing in the wire contract * promises arkd echoes checkpoints in submission order — while the result keeps * the server's order, so nothing downstream is reordered. * * Comparing txids is sound while checkpoint inputs are taproot-only: witness * data does not affect the txid, so the server's signature cannot change it. A * future P2SH-wrapped/scriptSig input could acquire a `finalScriptSig` * server-side and legitimately change txid; that protocol shape would need a * different comparison target. */ declare function matchServerCheckpoints(serverCheckpointTxs: string[], expectedCheckpoints: Transaction[], context: string): { server: Transaction; local: Transaction; }[]; /** * Assert a `submitTx` response refers to the ark transaction just submitted. * * The returned `arkTxid` is what gets passed to `finalizeTx` and persisted by * callers, so a stale or misrouted response must be rejected before either * happens: it must equal the locally signed transaction's txid, and when the * response carries the counter-signed `finalArkTx`, that transaction's txid * must match too. Server signatures live in the witness and cannot * legitimately change either — with the same taproot-only caveat as * {@link matchServerCheckpoints}. */ declare function assertSubmittedArkTxid(response: { arkTxid: string; finalArkTx?: string; }, signedArkTx: Transaction, context: string): void; /** * Opt-in proof that the server co-signed what it handed back. * * Be precise about what this buys, because it is less than it sounds like. It * does NOT protect a preimage or any other condition value: those reach the * server at `submitTx`, before any of this runs. What it buys is a loud, * immediate failure instead of a caller reporting success, watching nothing * confirm, and learning the truth when the counterparty's refund lands. */ interface VerifyServerSignatures { /** The Ark server's key; x-only or compressed, both accepted. */ serverPubkey: Uint8Array; /** Retired signer keys the server still advertises: a leaf built before a * rotation names one of these, and it is the leaf that decides. */ deprecatedServerPubkeys?: Uint8Array[]; } /** * Build, sign, submit, and finalize an offchain transaction whose every input * is controlled by a single key — the "thin signer" path. * * Needs no wallet, repository, or contract state: just an identity that can * sign, an Ark provider, the inputs (already carrying their spend leaf and tap * tree), the outputs, and the server unroll script. Used by the ArkadeCash sweep * to move bearer coins to the receiver's address without spinning up a full * background-managed wallet on shared repositories. * * @returns The Ark transaction id. */ declare function signAndSubmitOffchainTx(params: { identity: { sign(tx: Transaction, inputIndexes?: number[]): Promise; }; provider: OffchainTxSubmitProvider; inputs: ArkTxInput[]; outputs: TransactionOutput[]; serverUnrollScript: CSVMultisigTapscript.Type; /** Forwarded to {@link submitOffchainTx}; omitted, nothing is checked. */ verifyServerSignatures?: VerifyServerSignatures; }): Promise; /** * Decorate a signer so it reveals `preimage` on every input it signs. * * The ordering encoded here is the whole point: the condition witness is NOT * part of what is signed, so attaching it before signing leaves a signature * over a PSBT that no longer matches once the field is present — which the * server rejects as `INVALID_SIGNATURE`. Decorate per spend, never wallet-wide. * * For any condition-leaf spend (VHTLC claims on both swap directions); the * generic signature keeps the decorated identity's own type, so the result is * still whatever was passed in. */ declare function claimWithPreimageIdentity; }>(identity: T, preimage: Uint8Array): T; /** * ArkPsbtFieldKey are the available key names for the Arkade PSBT custom fields. */ declare enum ArkPsbtFieldKey { VtxoTaprootTree = "taptree", VtxoTreeExpiry = "expiry", Cosigner = "cosigner", ConditionWitness = "condition", PrevArkTx = "prevarktx", PrevoutTx = "prevouttx" } /** * ArkPsbtFieldKeyType is the key type of the Arkade PSBT custom field. * Every Arkade PSBT field has key type 222. */ declare const ArkPsbtFieldKeyType = 222; /** * ArkPsbtFieldCoder is the coder for the Arkade PSBT custom fields. * Each type has its own coder. */ interface ArkPsbtFieldCoder { key: ArkPsbtFieldKey; encode: (value: T) => NonNullable[number]; decode: (value: NonNullable[number]) => T | null; } /** * setArkPsbtField appends a new unknown field to the input at inputIndex * * @example * ```typescript * setArkPsbtField(tx, 0, VtxoTaprootTree, myTaprootTree); * setArkPsbtField(tx, 0, VtxoTreeExpiry, myVtxoTreeExpiry); * ``` */ declare function setArkPsbtField(tx: Transaction$1, inputIndex: number, coder: ArkPsbtFieldCoder, value: T): void; /** * getArkPsbtFields returns all the values of the given coder for the input at inputIndex * Multiple fields of the same type can exist in a single input. * * @example * ```typescript * const vtxoTaprootTreeFields = getArkPsbtFields(tx, 0, VtxoTaprootTree); * console.log(`input has ${vtxoTaprootTreeFields.length} vtxoTaprootTree fields`); */ declare function getArkPsbtFields(tx: Transaction$1, inputIndex: number, coder: ArkPsbtFieldCoder): T[]; /** * VtxoTaprootTree is set to pass all spending leaves of the vtxo input * * @example * ```typescript * const vtxoTaprootTree = VtxoTaprootTree.encode(myTaprootTree); */ declare const VtxoTaprootTree: ArkPsbtFieldCoder; /** * ConditionWitness is set to pass the witness data used to finalize the conditionMultisigClosure * * @example * ```typescript * const conditionWitness = ConditionWitness.encode(myConditionWitness); */ declare const ConditionWitness: ArkPsbtFieldCoder; /** * PrevArkTxField carries the serialized raw bitcoin tx of the previous Ark tx * spent by an input. Used by OP_INSPECTINPUTSCRIPTPUBKEY on intent proofs and * other contexts where the prevout pkScript must be looked up off-chain. * * Key: [0xde] || "prevarktx". Value: serialized wire.MsgTx (NOT a PSBT). */ declare const PrevArkTxField: ArkPsbtFieldCoder; /** * PrevoutTxField carries the serialized raw bitcoin tx that produced the * previous output spent by an input. Used by OP_INSPECTINPUTSCRIPTPUBKEY in * the SubmitOnchainTx flow, where there is no Ark tx but a plain Bitcoin * funding tx whose pkScript must be resolvable. * * Key: [0xde] || "prevouttx". Value: serialized wire.MsgTx (NOT a PSBT). */ declare const PrevoutTxField: ArkPsbtFieldCoder; /** * CosignerPublicKey is set on every TxGraph transactions to identify the musig2 public keys * * @example * ```typescript * const cosignerPublicKey = CosignerPublicKey.encode(myCosignerPublicKey); */ declare const CosignerPublicKey: ArkPsbtFieldCoder<{ index: number; key: Uint8Array; }>; /** * VtxoTreeExpiry is set to pass the expiry time of the input * * @example * ```typescript * const vtxoTreeExpiry = VtxoTreeExpiry.encode(myVtxoTreeExpiry); */ declare const VtxoTreeExpiry: ArkPsbtFieldCoder<{ type: "blocks" | "seconds"; value: bigint; }>; /** * BIP-322 simple message signing and verification. * * Supports P2TR (Taproot) signing and verification, P2WPKH verification, * and legacy P2PKH verification (Bitcoin Core signmessage format). * * Reuses the same toSpend/toSign transaction construction as Intent proofs, * but with the standard BIP-322 tagged hash ("BIP0322-signed-message") * instead of the Arkade-specific tag. * * @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki * * @example * ```typescript * // Sign a message (P2TR) * const signature = await BIP322.sign("Hello Bitcoin!", identity); * * // Verify a signature (P2TR or P2WPKH) * const valid = BIP322.verify("Hello Bitcoin!", signature, "bc1p..."); * const valid2 = BIP322.verify("Hello Bitcoin!", signature, "bc1q..."); * ``` */ declare namespace BIP322 { /** * Sign a message using the BIP-322 simple signature scheme. * * Constructs the standard BIP-322 toSpend and toSign transactions, * signs via the Identity interface, and returns the base64-encoded * witness stack. * * @param message - The message to sign * @param identity - Identity instance (holds the private key internally) * @param network - Optional Bitcoin network for P2TR address derivation * @returns Base64-encoded BIP-322 simple signature (witness stack) */ function sign(message: string, identity: Identity, network?: BTC_NETWORK): Promise; /** * Verify a BIP-322 signature for a P2TR, P2WPKH, or legacy P2PKH address. * * For segwit addresses (P2TR, P2WPKH), reconstructs the BIP-322 * toSpend/toSign transactions and verifies the witness signature. * * For P2PKH addresses, verifies using the Bitcoin Core legacy * `signmessage` format (compact recoverable ECDSA signature). * * @param message - The original message that was signed * @param signature - Base64-encoded signature (BIP-322 witness or legacy compact) * @param address - P2TR, P2WPKH, or P2PKH address of the signer * @param network - Optional Bitcoin network for address decoding * @returns true if the signature is valid */ function verify(message: string, signature: string, address: string, network?: BTC_NETWORK): boolean; } /** * ArkNotes are special virtual outputs in the Arkade protocol that * can be created and spent without requiring any transactions. * The server mints them, and they are encoded as base58 strings * with a human-readable prefix, a preimage and a value. * * @see VtxoScript * * @example * ```typescript * // Create an ArkNote * const note = new ArkNote(preimage, 50000); * * // Encode to string * const noteString = note.toString(); * * // Decode from string * const decodedNote = ArkNote.fromString(noteString); * ``` */ declare class ArkNote implements ExtendedCoin { preimage: Uint8Array; value: number; HRP: string; static readonly DefaultHRP = "arknote"; static readonly PreimageLength = 32; static readonly ValueLength = 4; static readonly Length: number; static readonly FakeOutpointIndex = 0; readonly vtxoScript: VtxoScript; /** Hashlock script backing the note. */ readonly txid: string; readonly vout = 0; readonly forfeitTapLeafScript: TapLeafScript; readonly intentTapLeafScript: TapLeafScript; readonly tapTree: Bytes; readonly status: Status; readonly extraWitness?: Bytes[] | undefined; /** * Create an ArkNote from a preimage and value. * * @param preimage - 32-byte preimage revealed to spend the note * @param value - Note value in satoshis * @param HRP - Optional human-readable prefix for string encoding */ constructor(preimage: Uint8Array, value: number, HRP?: string); /** * Encode the note as raw bytes. * * @returns Serialized note bytes * @see decode */ encode(): Uint8Array; /** * Decode a note from raw bytes. * * @param data - Serialized note bytes * @param hrp - Human-readable prefix expected for future string encoding * @returns Decoded ArkNote * @throws Error if the payload length is invalid * @see encode */ static decode(data: Uint8Array, hrp?: string): ArkNote; /** * Decode a note from its base58 string form. * * @param noteStr - Base58-encoded note string * @param hrp - Human-readable prefix expected on the note string * @returns Decoded ArkNote * @throws Error if the prefix or base58 payload is invalid * @see toString */ static fromString(noteStr: string, hrp?: string): ArkNote; /** * Encode the note to its human-readable base58 string form. * * @returns Base58-encoded note string * @see fromString */ toString(): string; } /** * ArkadeCash is a bearer instrument for the Ark protocol. * It encodes a private key and contract parameters as a bech32m string, * enabling wallet-to-wallet transfers without address exchange. * * Format: arkadecash1... (bech32m encoded) * Payload: version (1 byte) + private key (32 bytes) + server pubkey (32 bytes) + csv timelock sequence (4 bytes) */ declare class ArkadeCash { readonly csvTimelock: RelativeTimelock; readonly hrp: string; static readonly DefaultHRP = "arkadecash"; static readonly Version = 0; static readonly PayloadLength: number; private readonly _privateKey; private readonly _serverPubKey; private readonly _publicKey; constructor(privateKey: Uint8Array, serverPubKey: Uint8Array, csvTimelock: RelativeTimelock, hrp?: string); /** The note's private key. Returns a fresh copy; mutating it is a no-op. */ get privateKey(): Uint8Array; /** The server's public key. Returns a fresh copy; mutating it is a no-op. */ get serverPubKey(): Uint8Array; /** The note's public key. Returns a fresh copy; mutating it is a no-op. */ get publicKey(): Uint8Array; static generate(serverPubKey: Uint8Array, csvTimelock: RelativeTimelock, hrp?: string): ArkadeCash; static fromString(encoded: string): ArkadeCash; toString(): string; get identity(): SingleKey; get vtxoScript(): DefaultVtxo.Script; address(addressHrp: string): ArkAddress; } /** * Emulator REST client. * * The emulator is a signing service that executes Arkade scripts * and co-signs transactions when the scripts pass validation. * * The read here goes through `baseFetch`, the SDK's shared fetch boundary, so * it picks up whatever policy that boundary carries. The four submit calls * deliberately do not: aborting a submit would not tell the caller whether the * emulator signed, and none of them has a way to find out, so they stay on the * global `fetch` with their transport-error type unchanged. */ interface EmulatorInfo { signerPubkey: string; } interface EmulatorProvider { getInfo(): Promise; submitTx(arkTx: string, checkpointTxs: string[]): Promise<{ signedArkTx: string; signedCheckpointTxs: string[]; }>; submitIntent(intent: { proof: string; message: Intent.RegisterMessage; }): Promise; submitFinalization(intent: { proof: string; message: Intent.RegisterMessage; }, forfeits: string[], connectorTree: ConnectorTreeNode[] | null, commitmentTx: string): Promise<{ signedForfeits: string[]; signedCommitmentTx?: string; }>; submitOnchainTx(tx: string): Promise<{ signedTx: string; }>; } interface ConnectorTreeNode { txid: string; tx: string; children: Record; } /** * REST-based emulator client. * * @example * ```typescript * const client = new RestEmulatorProvider('http://localhost:7073'); * const info = await client.getInfo(); * console.log('Emulator pubkey:', info.signerPubkey); * ``` */ declare class RestEmulatorProvider implements EmulatorProvider { serverUrl: string; constructor(serverUrl: string); getInfo(): Promise; submitTx(arkTx: string, checkpointTxs: string[]): Promise<{ signedArkTx: string; signedCheckpointTxs: string[]; }>; submitIntent(intent: { proof: string; message: Intent.RegisterMessage; }): Promise; submitFinalization(intent: { proof: string; message: Intent.RegisterMessage; }, forfeits: string[], connectorTree: ConnectorTreeNode[] | null, commitmentTx: string): Promise<{ signedForfeits: string[]; signedCommitmentTx?: string; }>; submitOnchainTx(tx: string): Promise<{ signedTx: string; }>; } /** * Arkade Batch Handler * * Factory function that creates a `Batch.Handler` for arkade-script * transactions with emulator co-signing. Handles both on-chain * boarding inputs and off-chain virtual VTXO settlement in a single batch. * * @module arkade/batch */ /** An onchain boarding input to an arkade batch. */ type ArkadeExtendedCoin = ExtendedCoin & { arkadeScriptBytes: Uint8Array; }; /** A virtual output input to an arkade batch. */ type ArkadeExtendedVirtualCoin = ExtendedVirtualCoin & { arkadeScriptBytes: Uint8Array; }; /** * An input to {@link createArkadeBatchHandler}, boarding or virtual. * * @remarks * Spelled as a union so the discriminant {@link isVtxoCoin} reads — `script`, required on * `VirtualCoin` and absent from `ExtendedCoin` — is visible in the type. `ArkadeExtendedCoin[]` * alone would let a caller hand over a virtual output with no `script`, which the handler would * silently treat as boarding and settle without a forfeit. */ type ArkadeBatchInput = ArkadeExtendedCoin | ArkadeExtendedVirtualCoin; declare function createArkadeBatchHandler(intentId: string, inputs: ArkadeBatchInput[], signer: Identity, signedProof: string, message: Intent.RegisterMessage, session: SignerSession, arkProvider: ArkProvider, emulator: EmulatorProvider, network: Network, /** * Expected recipients of the settlement, validated against the virtual * output tree before co-signing it, and — when tree signing did not run — * against the commitment tx before signing anything at finalization * (mirrors `Wallet.createBatchHandler`). Without this the handler signs * whatever the server proposes, on both paths. */ recipients?: Recipient[], /** Overrides for the `batchExpiry` bounds; defaults derive from `network`. */ batchExpiryPolicy?: Partial): Batch.Handler; type VSize = { value: bigint; fee(feeRate: bigint): bigint; }; declare class TxWeightEstimator { static readonly P2PKH_SCRIPT_SIG_SIZE: number; static readonly INPUT_SIZE: number; static readonly BASE_CONTROL_BLOCK_SIZE: number; static readonly OUTPUT_SIZE: number; static readonly P2WPKH_OUTPUT_SIZE: number; static readonly BASE_TX_SIZE: number; static readonly WITNESS_HEADER_SIZE = 2; static readonly WITNESS_SCALE_FACTOR = 4; static readonly P2TR_OUTPUT_SIZE: number; hasWitness: boolean; inputCount: number; outputCount: number; inputSize: number; inputWitnessSize: number; outputSize: number; private constructor(); static create(): TxWeightEstimator; addP2AInput(): TxWeightEstimator; addKeySpendInput(isDefault?: boolean): TxWeightEstimator; addP2PKHInput(): TxWeightEstimator; addTapscriptInput(leafWitnessSize: number, leafScriptSize: number, leafControlBlockSize: number): TxWeightEstimator; addP2WPKHOutput(): TxWeightEstimator; addP2TROutput(): TxWeightEstimator; /** * Adds an output given a raw script. * Cost = 8 bytes (amount) + varint(scriptLen) + scriptLen */ addOutputScript(script: Uint8Array): TxWeightEstimator; /** * Adds an output by decoding the address to get the exact script size. */ addOutputAddress(address: string, network: Network): TxWeightEstimator; vsize(): VSize; } /** * Called when a VTXO's exit becomes observable onchain. * * The seam exists because nothing else can learn the fact: delta sync filters on *creation* time * (`GetVtxosOptions.after` / `before`), so a status change on an older VTXO is invisible to it, and * neither exit path can reach a contract manager on its own — `Unroll.Session` takes no contract * layer by design, and the `UnilateralExit` executor is deliberately keyless and provider-only. * * A plain function type, with no runtime import anywhere in this module, is what lets both keep * that independence: an exit path calls the hook, it does not import the contract layer. * * **Best-effort by contract.** An exit is a disaster-recovery path and must never fail because a * cache write did, so every call site goes through {@link notifyExitObserved}. */ type OnExitObserved = (outpoint: Outpoint) => void | Promise; /** * Adapt a contract manager into an {@link OnExitObserved}: the wallet-side half of the seam, kept * out of `exit/` so the dependency direction stays outward. * * **A prompt, not a guarantee.** The hook fires when the EXPLORER reports the exit confirmed, and * `refreshOutpoints` then asks the INDEXER. If arkd has not yet marked the output `isUnrolled` the * refresh writes the state it does report — or returns silently when it names no vtxo at all — and * nothing here retries. A miss leaves the wallet exactly where it was before this seam existed: it * learns the exit whenever something else next refreshes the outpoint. The executor's second fire * at sweep-confirm is the natural retry, and by then the exit has been onchain for at least the * CSV delay. */ declare function exitObserverFor(manager: Pick): OnExitObserved; /** Fire an observation without letting it into the exit's failure path. */ declare function notifyExitObserved(onExitObserved: OnExitObserved | undefined, outpoint: Outpoint): Promise; declare namespace Unroll { enum StepType { UNROLL = 0, WAIT = 1, DONE = 2 } /** * Unroll step where the transaction has to be broadcasted in a 1C1P package */ type UnrollStep = { tx: Transaction; pkg: [parent: string, child: string]; }; /** * Wait step where the transaction has to be confirmed onchain */ type WaitStep = { txid: string; }; /** * Done step where the unrolling process is complete */ type DoneStep = { vtxoTxid: string; }; type Step = ({ type: StepType.DONE; } & DoneStep) | ({ type: StepType.UNROLL; } & UnrollStep) | ({ type: StepType.WAIT; } & WaitStep); /** * Manages the unrolling process of a virtual output back to the Bitcoin blockchain. * * The Session class implements an async iterator that processes the unrolling steps: * 1. **WAIT**: Waits for a transaction to be confirmed onchain (if it's in mempool) * 2. **UNROLL**: Broadcasts the next transaction in the chain to the blockchain * 3. **DONE**: Indicates the unrolling process is complete * * The unrolling process works by traversing the transaction chain from the root (most recent) * to the leaf (oldest), broadcasting each transaction that isn't already onchain. * * @example * ```typescript * const session = await Unroll.Session.create(vtxoOutpoint, bumper, explorer, indexer); * * // iterate over the steps * for await (const doneStep of session) { * switch (doneStep.type) { * case Unroll.StepType.WAIT: * console.log(`Transaction ${doneStep.txid} confirmed`); * break; * case Unroll.StepType.UNROLL: * console.log(`Broadcasting transaction ${doneStep.tx.id}`); * break; * case Unroll.StepType.DONE: * console.log(`Unrolling complete for virtual output ${doneStep.vtxoTxid}`); * break; * } * } * ``` **/ class Session implements AsyncIterable { readonly toUnroll: Outpoint & { chain: ChainTx[]; }; readonly bumper: AnchorBumper; readonly explorer: OnchainProvider; readonly indexer: IndexerProvider; /** * Optional virtual-tx repository. When provided, each step's raw * tx is read from the repo first and fetched from the indexer on a * miss, then cached best-effort for later exits. Omitted ⇒ exact * previous behaviour (indexer only). */ readonly virtualTxRepository?: VirtualTxRepository | undefined; /** * Fired once the chain is fully onchain (`StepType.DONE`), so a * wallet repository can be prompted to re-read the outpoint — * delta sync never would, filtering as it does on creation time. * Fired ONCE, so an observer that reads a lagging indexer gets no * second chance; see {@link exitObserverFor}. Best-effort — a * rejection never reaches the exit. Prefer {@link sessionFor}, * which wires it from the wallet. */ readonly onExitObserved?: OnExitObserved | undefined; /** Create an unroll session from a virtual output outpoint and its dependency chain. */ constructor(toUnroll: Outpoint & { chain: ChainTx[]; }, bumper: AnchorBumper, explorer: OnchainProvider, indexer: IndexerProvider, /** * Optional virtual-tx repository. When provided, each step's raw * tx is read from the repo first and fetched from the indexer on a * miss, then cached best-effort for later exits. Omitted ⇒ exact * previous behaviour (indexer only). */ virtualTxRepository?: VirtualTxRepository | undefined, /** * Fired once the chain is fully onchain (`StepType.DONE`), so a * wallet repository can be prompted to re-read the outpoint — * delta sync never would, filtering as it does on creation time. * Fired ONCE, so an observer that reads a lagging indexer gets no * second chance; see {@link exitObserverFor}. Best-effort — a * rejection never reaches the exit. Prefer {@link sessionFor}, * which wires it from the wallet. */ onExitObserved?: OnExitObserved | undefined); /** Create an unroll session by loading the virtual output chain from the indexer. */ static create(toUnroll: Outpoint, bumper: AnchorBumper, explorer: OnchainProvider, indexer: IndexerProvider, virtualTxRepository?: VirtualTxRepository, onExitObserved?: OnExitObserved): Promise; /** * Resolve a chain tx's raw PSBT (base64). Repo first when configured; * indexer on a miss, then best-effort cached so a later exit doesn't * re-fetch. Never throws from the cache * write — exit correctness must not depend on persistence. */ private resolveVirtualTxBase64; /** * Get the next step to be executed * * @remarks * `do()` is the step, not a formality: every effect lives there, and at * `StepType.DONE` that includes firing {@link onExitObserved}. A caller * driving `next()` by hand must always await it — the async iterator * below does. Reading `type` and skipping `do()` leaves the exit * unobserved, and on the other step kinds unbroadcast. * * @returns The next step to be executed + the function to execute it */ next(): Promise Promise; }>; /** * Iterate over the steps to be executed and execute them * @returns An async iterator over the executed steps */ [Symbol.asyncIterator](): AsyncIterator; } /** * {@link Session.create} with everything the wallet already holds — explorer, indexer, * virtual-tx cache — plus the exit observer, which re-reads the outpoint at `StepType.DONE` * rather than waiting for a delta sync that cannot see the change. That single fire is a * prompt and not a guarantee — see {@link exitObserverFor}. * * Prefer this to building a `Session` by hand: an optional parameter only reaches callers who * know to pass it, which is the same failure mode the seam exists to close. */ function sessionFor(wallet: Wallet, toUnroll: Outpoint, bumper: AnchorBumper): Promise; /** * Complete the unroll of a virtual output by broadcasting the transaction that spends the CSV path. * @param wallet the wallet owning the virtual output(s) * @param vtxoTxids the txids of the virtual output(s) to complete unroll * @param outputAddress the address to send the unrolled funds to * @throws if the virtual output(s) are not fully unrolled, if the txids are not found, if the tx is not confirmed, if no exit path is found or not available * @returns the txid of the transaction spending the unrolled funds */ function completeUnroll(wallet: Wallet, vtxoTxids: string[], outputAddress: string): Promise; } /** Relative delay before a sweep becomes valid, decoded from BIP-68. */ type ExitDelay = { type: "blocks" | "seconds"; value: number; }; /** Broadcast a single pre-signed transaction (the funding splitter). */ type BroadcastStep = { kind: "broadcast"; txid: string; hex: string; }; /** Broadcast a 1P1C package: virtual tx + its pre-signed CPFP fee child. */ type PackageStep = { kind: "package"; parentTxid: string; parentHex: string; childTxid: string; childHex: string; /** Outpoints (txid:vout) of the VTXOs this step serves. */ forVtxos: string[]; }; /** * A virtual tx that must go onchain but whose CPFP fee child is NOT * pre-signed (graph mode). The executor builds and signs the child at * execution time using its own fee wallet — this is how "send funds to * this address and we proceed" works: funding is deferred, not baked in. */ type BumpStep = { kind: "bump"; parentTxid: string; parentHex: string; /** Outpoints (txid:vout) of the VTXOs this step serves. */ forVtxos: string[]; }; /** Broadcast a pre-signed CSV sweep once its dependency matured. */ type SweepStep = { kind: "sweep"; vtxo: string; txid: string; hex: string; /** The VTXO-creating leaf tx whose confirmation starts the CSV clock. */ dependsOnTxid: string; delay: ExitDelay; }; type ExitStep = BroadcastStep | PackageStep | BumpStep | SweepStep; /** * How fee funding is provided: * - `"funded"`: a splitter tx (broadcast at prepare time) pre-funds * pre-signed fee children — execution is fully keyless. * - `"graph"`: only the tx graph + sweeps are transported; the executor * funds and signs the CPFP bumps at execution time from its own fee * wallet ("send funds to this address and we proceed"). */ type ExitMode = "funded" | "graph"; /** Per-VTXO metadata; skipped VTXOs carry a human-readable reason. */ type ExitVtxoInfo = { outpoint: string; value?: number; sweepFee?: number; /** `${contractType}:${pathLabel}` for observability, e.g. "vhtlc:unilateral". */ path?: string; delay?: ExitDelay; skipped?: string; }; type ExitTotals = { /** Distinct transactions the executor ensures onchain. */ txCount: number; totalFeeSats: number; /** Sats consumed from the onchain wallet by the splitter (fees + funding outputs). */ fundingRequiredSats: number; /** Sats arriving at the sweep address once all sweeps confirm. */ recoveredSats: number; }; /** * Versioned, language-agnostic unilateral exit package. * Everything inside is pre-signed; executing it requires no keys and no * Arkade infrastructure — only an Esplora-compatible API. */ type ExitPackage = { version: 1; /** Fee-funding strategy. Absent is treated as "funded" (v1 default). */ mode?: ExitMode; network: NetworkName; createdAt: number; /** Min batch expiry (unix seconds) across included txs. Informational. */ validUntil?: number; feeRate: number; sweepAddress: string; totals: ExitTotals; vtxos: ExitVtxoInfo[]; /** Topologically ordered: a step's txs confirm before dependents proceed. */ steps: ExitStep[]; }; /** Cost/size quote returned by `UnilateralExit.estimate` — no funds needed. */ type ExitQuote = { feeRate: number; fundingAddress: string; currentBalanceSats: number; shortfallSats: number; validUntil?: number; totals: ExitTotals; vtxos: ExitVtxoInfo[]; }; declare function serializeExitPackage(pkg: ExitPackage): string; declare function deserializeExitPackage(json: string): ExitPackage; type ExitOptions = { /** Wallet owning the VTXOs: identity (signing) + indexer + onchain provider. */ wallet: Wallet; /** Fee funding source and change/funding address. Must share the wallet identity. */ onchainWallet: OnchainWallet; /** Destination for the exited funds. */ sweepAddress: string; /** sat/vB; defaults to the onchain provider estimate, floored at MIN_FEE_RATE. */ feeRate?: number; /** Defaults to all spendable VTXOs. */ vtxos?: Outpoint[]; /** * Fee-funding strategy (default `"funded"`): * - `"funded"`: broadcast a splitter at prepare time and pre-sign the * fee children — the package executes fully keyless. * - `"graph"`: transport only the tx graph + sweeps; the executor funds * and signs the CPFP bumps at execution time ("send funds to this * address and we proceed"). No splitter, no `onchainWallet` funds used. */ mode?: ExitMode; /** * Network label embedded in the package (executor sanity check). * Resolved from the wallet's network when omitted — exact for * "bitcoin" and "regtest"; the tb-family defaults to "testnet", so * pass this explicitly on signet/mutinynet. */ networkName?: NetworkName; }; /** * Quote a unilateral exit: how many transactions, how many sats of fees, * and how much must be deposited to the funding address. Requires no * onchain funds and signs nothing. */ declare function estimate(opts: ExitOptions): Promise; type ExecutorEvent = { stepIndex: number; kind: ExitStep["kind"]; status: "skipped" | "broadcast" | "confirmed" | "waiting_csv" | "failed" | "warning"; forVtxos?: string[]; txid?: string; reason?: string; /** For waiting_csv with a blocks delay: absolute height at maturity. */ maturesAtHeight?: number; /** For waiting_csv with a seconds delay: unix time at maturity. */ maturesAtTime?: number; }; /** * Fee source for graph-mode `bump` steps. Given a parent tx carrying a P2A * anchor, it builds and signs the CPFP fee child from its own funds and * returns the 1P1C package hexes (parent unchanged, child signed) — WITHOUT * broadcasting; the executor owns broadcast. `OnchainWallet` implements it. */ interface ExitFeeWallet { bumpAnchor(parentHex: string, feeRate: number): Promise<[parentHex: string, childHex: string]>; } interface ExecutorOptions { pollIntervalMs?: number; feeWallet?: ExitFeeWallet; /** Abort to stop execution. * * Iteration then rejects with `signal.reason`. Calling `abort()` * with no argument yields an error whose `name` is `"AbortError"`; * a custom reason is forwarded as-is, matching `fetch` and * `AbortSignal.prototype.throwIfAborted`. * * Already-broadcast transactions are not recalled; the executor is * idempotent, so a later run resumes from the chain. */ signal?: AbortSignal; /** * Fired per VTXO once every step serving its branch is onchain, and again * once its sweep confirms — the two moments an exit becomes observable. * Two fires rather than one is what makes an observer reading a lagging * indexer recoverable here: by the sweep the exit has been onchain for at * least the CSV delay. * * Best-effort: a rejection never reaches the exit, which is the whole point * of a keyless disaster-recovery path. `UnilateralExit.execute` wires it * from a wallet; passing it here keeps the bare executor provider-only. */ onExitObserved?: OnExitObserved; } /** * Keyless, stateless executor for a pre-signed exit package. * * The blockchain is the only state: every action re-checks tx status first, * so the executor can be killed and re-run anywhere at any time. It never * parses transaction hex — it only relays it. */ declare class Executor implements AsyncIterable { readonly pkg: ExitPackage; readonly provider: OnchainProvider; private readonly pollIntervalMs; private readonly feeWallet?; private readonly signal?; private readonly onExitObserved?; constructor(pkg: ExitPackage, provider: OnchainProvider, opts?: ExecutorOptions); /** * Per-VTXO branch progress, which the executor otherwise does not track: * `dead` / `done` are per-step, and a VTXO is only onchain once EVERY * `package`/`bump` step naming it has confirmed. (`broadcast` — the funding * splitter — carries no `forVtxos` and serves no single VTXO.) */ private branchSteps; /** * Poll delay. Nearly all wall-clock time is spent here, so it must be the * thing that reacts to abort — checking only between polls would leave * cancellation up to a full interval late and the timer still pending. */ private sleep; private status; private waitConfirmed; [Symbol.asyncIterator](): AsyncIterator; /** `"txid:vout"` -> outpoint, then hand it to the hook. A malformed entry is skipped. */ private observe; } /** * An {@link Executor} with the wallet's exit observer already wired, so the repository re-reads * each outpoint as its branch confirms and again as its sweep does, moving the value into the * `unrolled` balance bucket without waiting for a delta sync — which could not see the change * anyway, filtering as it does on creation time. What the re-read learns depends on the indexer * having caught up; see `exitObserverFor`. * * The bare `Executor` stays keyless and provider-only; this is the wallet-side convenience that * makes the default path correct, because an opt-in parameter only reaches callers who know to pass * it. An explicit `opts.onExitObserved` still wins. */ declare function execute(wallet: Wallet, pkg: ExitPackage, opts?: ExecutorOptions & { provider?: OnchainProvider; }): Promise; /** * Build the fully pre-signed unilateral exit package. * * Signs every transaction needed to land the selected VTXOs onchain and * **broadcasts the funding splitter** as a side effect — reserving the fee * budget so later wallet activity cannot invalidate the package. The * returned package is keyless to execute: hand it to * `UnilateralExit.Executor` (or any Esplora-speaking watchtower). */ declare function prepare(opts: ExitOptions): Promise; /** * Pre-signed unilateral exit. * * `estimate` quotes the cost (tx count, fees, funding required) without * touching funds; `prepare` signs every transaction needed to land the * VTXOs onchain and broadcasts the fee-funding splitter; `Executor` drives * the resulting package to completion with nothing but an * Esplora-compatible endpoint — no keys, no Arkade infrastructure. * * `execute` is the wallet-side shorthand for that last step: the same * executor, with the exit observer wired so the wallet's own repository * re-reads each outpoint as its branch confirms and again as its sweep * does — picking the exit up as it lands, indexer lag permitting. See * `exitObserverFor` for why that is a prompt rather than a guarantee. */ declare const UnilateralExit: { readonly estimate: typeof estimate; readonly prepare: typeof prepare; readonly execute: typeof execute; readonly Executor: typeof Executor; }; /** * Whether a provider error is a *retryable* availability failure (operator or * indexer temporarily unreachable) rather than a terminal one. * * Retryable: * - {@link ProviderUnavailableError} — already-classified 5xx/429/unavailable * (see e.g. {@link https | RestArkProvider.getInfo}'s non-2xx branch); * - {@link FetchError} — transport-level `fetch` rejection (DNS failure, * connection refused, TLS/CORS), which also carries undici timeouts as its * `cause`; * - {@link isFetchTimeoutError} — header/body timeouts that surface directly. * * Everything else (4xx, invalid JSON, schema violations, network mismatch, * unsupported network) is terminal and returns `false`. * * NOTE (Step 2, Scope 2): this classifies the errors currently produced by the * boot-critical `getInfo` path. Normalizing the indexer's per-branch * `!res.ok` throws and the remaining Ark RPC methods into * {@link ProviderUnavailableError} is the broader Scope-2 work still pending. */ declare function isRetryableProviderError(error: unknown): boolean; declare function validateConnectorsTxGraph(settlementTxB64: string, connectorsGraph: TxTree): void; declare function validateVtxoTxGraph(graph: TxTree, roundTransaction: Transaction, sweepTapTreeRoot: Uint8Array): void; /** * Assert the commitment tx received at batch finalization is the one validated * at tree signing. * * The vtxo tree co-signed at tree signing spends `commitmentTxid:0`, so a * finalization commitment carrying another txid does not correspond to the tree * that was validated. The two are the same transaction by construction. * * No-op when `validatedCommitmentTxid` is undefined: tree signing was skipped * (not a cosigner, or an onchain-only settle), so there is nothing to compare * against. */ declare function assertFinalCommitmentMatchesValidated(finalCommitmentTx: Transaction, validatedCommitmentTxid: string | undefined, context: string): void; /** * Validates both offchain and onchain recipients. * Offchain recipients are checked against vtxo tree leaves for correct amounts and assets. * Onchain recipients are validated against the round transaction outputs (amounts and scripts) * via validateOnchainRecipient. * * Presence only: the commitment tx and the tree are shared with every other intent in the batch, * so outputs paying someone else are legitimate and cannot be rejected. What this asserts is that * a settlement cannot consume our inputs and pay us nothing. * * @param commitmentTx - The commitment transaction to validate against * @param vtxoTreeLeaves - The vtxo tree leaves to validate against * @param recipients - The expected recipients to validate (both offchain and onchain) * @param network - Network for decoding onchain addresses (e.g. mainnet, testnet) * @throws {Error} if a recipient is not present or invalid in the vtxo tree or commitment tx */ declare function validateBatchRecipients(commitmentTx: Transaction, vtxoTreeLeaves: Transaction[], recipients: Recipient[], network: Network): void; /** * Build a forfeit transaction that spends the provided inputs to a single forfeit output. * * @param inputs - Inputs to include in the forfeit transaction * @param forfeitPkScript - ScriptPubKey for the forfeit output * @param txLocktime - Optional locktime to apply to the transaction */ declare function buildForfeitTx(inputs: TransactionInputUpdate[], forfeitPkScript: Uint8Array, txLocktime?: number): Transaction; /** * EmulatorEntry represents a single entry in the Emulator Packet, * mapping a transaction input to its arkade script and witness data. */ interface EmulatorEntry { /** Transaction input index (u16 LE) */ vin: number; /** Arkade Script bytecode */ script: Uint8Array; /** Script witness data (serialized) */ witness?: Uint8Array; } /** * EmulatorPacket implements ExtensionPacket for type 0x01. * * Internal wire format (inside TLV payload): * compactSize(entry_count) + for each entry: * u16_le(vin) + compactSize(script_len) + script + compactSize(witness_len) + witness * * Uses Bitcoin CompactSize encoding for internal length fields. */ declare class EmulatorPacket implements ExtensionPacket { readonly entries: EmulatorEntry[]; /** PACKET_TYPE is the 1-byte TLV type tag used in the Extension envelope. */ static readonly PACKET_TYPE = 1; private constructor(); static create(entries: EmulatorEntry[]): EmulatorPacket; static fromBytes(data: Uint8Array): EmulatorPacket; type(): number; serialize(): Uint8Array; } /** * ArkadeMagic is the 3-byte magic prefix ("ARK") that identifies an OP_RETURN * output as an Arkade extension blob. */ declare const ARKADE_MAGIC: Uint8Array; /** * ErrExtensionNotFound is thrown when no extension output is found in a transaction. */ declare class ExtensionNotFoundError extends Error { constructor(); } /** * Extension is a set of typed packets encoded in an OP_RETURN output. * * Wire format: * OP_RETURN | | ARK(3B) | [type(1B) | varint_len | data]... */ declare class Extension { private readonly packets; private constructor(); static create(packets: ExtensionPacket[]): Extension; /** * isExtension returns true if the script is an OP_RETURN whose push data * begins with the ARK magic bytes. */ static isExtension(script: Uint8Array): boolean; /** * fromBytes parses an Extension from a raw OP_RETURN script. */ static fromBytes(script: Uint8Array): Extension; /** * fromTx searches the transaction outputs for an extension blob and parses it. * Throws ExtensionNotFoundError if none is found. */ static fromTx(tx: Transaction): Extension; /** * serialize encodes the extension as an OP_RETURN script. * * Layout: OP_RETURN | | ARK | [type | varint_len | data]... */ serialize(): Uint8Array; /** * txOut returns the extension as a zero-value OP_RETURN transaction output. */ txOut(): Required>; /** * getAssetPacket returns the embedded Packet, or null if not present. */ getAssetPacket(): Packet | null; /** * getEmulatorPacket returns the embedded EmulatorPacket, or null if not present. */ getEmulatorPacket(): EmulatorPacket | null; /** * getPacketByType returns the first packet matching the given type tag, or null. */ getPacketByType(packetType: number): ExtensionPacket | null; /** * Returns all embedded packets in insertion order. Used when callers need * to rebuild an Extension from an existing one (e.g. appending a new packet). */ getPackets(): readonly ExtensionPacket[]; } /** * Arkade Script Opcodes * * This module defines ONLY Arkade-specific opcodes (0xb3, 0xc3-0xf8). * Standard Bitcoin opcodes are imported from @scure/btc-signer. * * Reference: arkade-os/emulator pkg/arkade/opcode.go. * Byte 0xdc is OP_CHECKTIME, matching emulator v0.0.8 and compiler artifacts. */ declare const ARKADE_OP: { readonly MERKLEBRANCHVERIFY: 179; readonly DIGEST: 195; readonly SHA256INITIALIZE: 196; readonly SHA256UPDATE: 197; readonly SHA256FINALIZE: 198; readonly INSPECTINPUTOUTPOINT: 199; readonly INSPECTINPUTARKADESCRIPTHASH: 200; readonly INSPECTINPUTVALUE: 201; readonly INSPECTINPUTSCRIPTPUBKEY: 202; readonly INSPECTINPUTSEQUENCE: 203; readonly CHECKSIGFROMSTACK: 204; readonly PUSHCURRENTINPUTINDEX: 205; readonly INSPECTINPUTARKADEWITNESSHASH: 206; readonly INSPECTOUTPUTVALUE: 207; readonly INSPECTOUTPUTSCRIPTPUBKEY: 209; readonly INSPECTVERSION: 210; readonly INSPECTLOCKTIME: 211; readonly INSPECTNUMINPUTS: 212; readonly INSPECTNUMOUTPUTS: 213; readonly TXWEIGHT: 214; readonly NUM2BIN: 215; readonly BIN2NUM: 216; readonly REVERSEBYTES: 217; readonly MODEXP: 218; readonly PUSHEXPIRY: 219; readonly CHECKTIME: 220; readonly ECADD: 224; readonly ECMUL: 225; readonly ECPAIRING: 226; readonly ECMULSCALARVERIFY: 227; readonly TWEAKVERIFY: 228; readonly INSPECTNUMASSETGROUPS: 229; readonly INSPECTASSETGROUPASSETID: 230; readonly INSPECTASSETGROUPCTRL: 231; readonly FINDASSETGROUPBYASSETID: 232; readonly INSPECTASSETGROUPMETADATAHASH: 233; readonly INSPECTASSETGROUPNUM: 234; readonly INSPECTASSETGROUP: 235; readonly INSPECTASSETGROUPSUM: 236; readonly INSPECTOUTASSETCOUNT: 237; readonly INSPECTOUTASSETAT: 238; readonly INSPECTOUTASSETLOOKUP: 239; readonly INSPECTINASSETCOUNT: 240; readonly INSPECTINASSETAT: 241; readonly INSPECTINASSETLOOKUP: 242; readonly TXID: 243; readonly INSPECTPACKET: 244; readonly INSPECTINPUTPACKET: 245; readonly SIGHASH: 246; readonly TUNNEL: 247; readonly INSPECTINTENTMESSAGE: 248; }; declare const ARKADE_OPCODES: number[]; declare const ARKADE_OPCODE_NAMES: Record; declare const ARKADE_OPCODE_VALUES: Record; /** * Combined map from opcode value to name (with OP_ prefix) * Includes both Bitcoin and Arkade opcodes */ declare const OPCODE_NAMES: Record; /** * Combined map from opcode name to value * Supports both with and without OP_ prefix * Includes both Bitcoin and Arkade opcodes */ declare const OPCODE_VALUES: Record; /** * Get the name of an opcode from its value * Returns OP_DATA_N for data push opcodes (0x01-0x4b) * * @param value Opcode byte value * @returns Opcode name with OP_ prefix, or undefined if unknown */ declare function getOpcodeName(value: number): string | undefined; /** * Get the value of an opcode from its name * Supports OP_DATA_N pattern for data push opcodes * * @param name Opcode name (with or without OP_ prefix) * @returns Opcode byte value, or undefined if unknown */ declare function getOpcodeValue(name: string): number | undefined; /** * Arkade Script Encoding and Decoding * * This module provides script encoding/decoding and ASM conversion helpers * that work with both standard Bitcoin opcodes and Arkade extension opcodes. * * Note: We use our own decoder for scripts because @scure/btc-signer doesn't * recognize Arkade opcodes (0xb3, 0xc4-0xf3) and would treat them as data pushes. */ /** * Combined OP map: standard Bitcoin opcodes + Arkade extension opcodes. * Keys follow @scure convention (without OP_ prefix for most). */ declare const ARKADE_OPS: { readonly MERKLEBRANCHVERIFY: 179; readonly DIGEST: 195; readonly SHA256INITIALIZE: 196; readonly SHA256UPDATE: 197; readonly SHA256FINALIZE: 198; readonly INSPECTINPUTOUTPOINT: 199; readonly INSPECTINPUTARKADESCRIPTHASH: 200; readonly INSPECTINPUTVALUE: 201; readonly INSPECTINPUTSCRIPTPUBKEY: 202; readonly INSPECTINPUTSEQUENCE: 203; readonly CHECKSIGFROMSTACK: 204; readonly PUSHCURRENTINPUTINDEX: 205; readonly INSPECTINPUTARKADEWITNESSHASH: 206; readonly INSPECTOUTPUTVALUE: 207; readonly INSPECTOUTPUTSCRIPTPUBKEY: 209; readonly INSPECTVERSION: 210; readonly INSPECTLOCKTIME: 211; readonly INSPECTNUMINPUTS: 212; readonly INSPECTNUMOUTPUTS: 213; readonly TXWEIGHT: 214; readonly NUM2BIN: 215; readonly BIN2NUM: 216; readonly REVERSEBYTES: 217; readonly MODEXP: 218; readonly PUSHEXPIRY: 219; readonly CHECKTIME: 220; readonly ECADD: 224; readonly ECMUL: 225; readonly ECPAIRING: 226; readonly ECMULSCALARVERIFY: 227; readonly TWEAKVERIFY: 228; readonly INSPECTNUMASSETGROUPS: 229; readonly INSPECTASSETGROUPASSETID: 230; readonly INSPECTASSETGROUPCTRL: 231; readonly FINDASSETGROUPBYASSETID: 232; readonly INSPECTASSETGROUPMETADATAHASH: 233; readonly INSPECTASSETGROUPNUM: 234; readonly INSPECTASSETGROUP: 235; readonly INSPECTASSETGROUPSUM: 236; readonly INSPECTOUTASSETCOUNT: 237; readonly INSPECTOUTASSETAT: 238; readonly INSPECTOUTASSETLOOKUP: 239; readonly INSPECTINASSETCOUNT: 240; readonly INSPECTINASSETAT: 241; readonly INSPECTINASSETLOOKUP: 242; readonly TXID: 243; readonly INSPECTPACKET: 244; readonly INSPECTINPUTPACKET: 245; readonly SIGHASH: 246; readonly TUNNEL: 247; readonly INSPECTINTENTMESSAGE: 248; readonly OP_0: 0; readonly PUSHDATA1: 76; readonly PUSHDATA2: 77; readonly PUSHDATA4: 78; readonly '1NEGATE': 79; readonly RESERVED: 80; readonly OP_1: 81; readonly OP_2: 82; readonly OP_3: 83; readonly OP_4: 84; readonly OP_5: 85; readonly OP_6: 86; readonly OP_7: 87; readonly OP_8: 88; readonly OP_9: 89; readonly OP_10: 90; readonly OP_11: 91; readonly OP_12: 92; readonly OP_13: 93; readonly OP_14: 94; readonly OP_15: 95; readonly OP_16: 96; readonly NOP: 97; readonly VER: 98; readonly IF: 99; readonly NOTIF: 100; readonly VERIF: 101; readonly VERNOTIF: 102; readonly ELSE: 103; readonly ENDIF: 104; readonly VERIFY: 105; readonly RETURN: 106; readonly TOALTSTACK: 107; readonly FROMALTSTACK: 108; readonly '2DROP': 109; readonly '2DUP': 110; readonly '3DUP': 111; readonly '2OVER': 112; readonly '2ROT': 113; readonly '2SWAP': 114; readonly IFDUP: 115; readonly DEPTH: 116; readonly DROP: 117; readonly DUP: 118; readonly NIP: 119; readonly OVER: 120; readonly PICK: 121; readonly ROLL: 122; readonly ROT: 123; readonly SWAP: 124; readonly TUCK: 125; readonly CAT: 126; readonly SUBSTR: 127; readonly LEFT: 128; readonly RIGHT: 129; readonly SIZE: 130; readonly INVERT: 131; readonly AND: 132; readonly OR: 133; readonly XOR: 134; readonly EQUAL: 135; readonly EQUALVERIFY: 136; readonly RESERVED1: 137; readonly RESERVED2: 138; readonly '1ADD': 139; readonly '1SUB': 140; readonly '2MUL': 141; readonly '2DIV': 142; readonly NEGATE: 143; readonly ABS: 144; readonly NOT: 145; readonly '0NOTEQUAL': 146; readonly ADD: 147; readonly SUB: 148; readonly MUL: 149; readonly DIV: 150; readonly MOD: 151; readonly LSHIFT: 152; readonly RSHIFT: 153; readonly BOOLAND: 154; readonly BOOLOR: 155; readonly NUMEQUAL: 156; readonly NUMEQUALVERIFY: 157; readonly NUMNOTEQUAL: 158; readonly LESSTHAN: 159; readonly GREATERTHAN: 160; readonly LESSTHANOREQUAL: 161; readonly GREATERTHANOREQUAL: 162; readonly MIN: 163; readonly MAX: 164; readonly WITHIN: 165; readonly RIPEMD160: 166; readonly SHA1: 167; readonly SHA256: 168; readonly HASH160: 169; readonly HASH256: 170; readonly CODESEPARATOR: 171; readonly CHECKSIG: 172; readonly CHECKSIGVERIFY: 173; readonly CHECKMULTISIG: 174; readonly CHECKMULTISIGVERIFY: 175; readonly NOP1: 176; readonly CHECKLOCKTIMEVERIFY: 177; readonly CHECKSEQUENCEVERIFY: 178; readonly NOP4: 179; readonly NOP5: 180; readonly NOP6: 181; readonly NOP7: 182; readonly NOP8: 183; readonly NOP9: 184; readonly NOP10: 185; readonly CHECKSIGADD: 186; readonly INVALID: 255; }; /** A single script operation: opcode string key, raw bytes, number, or bigint */ type ArkadeScriptOP = keyof typeof ARKADE_OPS | Uint8Array | number | bigint; /** Array of script operations — the type that ArkadeScript encodes/decodes */ type ArkadeScriptType = ArkadeScriptOP[]; /** * Script CoderType with Arkade extension opcodes. * * Same API as `Script` from @scure/btc-signer, but with Arkade extension opcodes. * * @example * ```typescript * const script: ArkadeScriptType = ['DUP', 'HASH160', pubkeyHash, 'EQUALVERIFY', 'NUM2BIN']; * const bytes = ArkadeScript.encode(script); * const decoded = ArkadeScript.decode(bytes); * ``` */ declare const ArkadeScript: P.CoderType; /** * Convert script operations to ASM (assembly) format. * Supports both Bitcoin and Arkade opcodes. * * @example * ```typescript * toASM(['DUP', 'HASH160', 'INSPECTOUTPUTVALUE']) // "OP_DUP OP_HASH160 OP_INSPECTOUTPUTVALUE" * toASM(ArkadeScript.decode(bytes)) * ``` */ declare function toASM(script: ArkadeScriptType): string; /** * Parse ASM (assembly) format to script operations. * Supports both Bitcoin and Arkade opcodes. * * @example * ```typescript * fromASM("OP_DUP OP_HASH160 deadbeef OP_NUM2BIN") * // => ['DUP', 'HASH160', Uint8Array, 'NUM2BIN'] * ``` */ declare function fromASM(asm: string): ArkadeScriptType; /** * Convert ASM string directly to script bytes */ declare function asmToBytes(asm: string): Uint8Array; /** * Convert script bytes directly to ASM string */ declare function bytesToASM(script: Uint8Array): string; /** * Arkade Script Tweak * * Computes the tweaked public key for Arkade scripts. * The tweak is: tweakedPubKey = P + taggedHash("ArkScriptHash", script) * G * * This is NOT taproot tweaking — it's a simple EC point addition used by * the emulator service to bind a script to a signing key. */ /** * Compute the tagged hash of an Arkade script. * Uses BIP-340 tagged hash: SHA256(SHA256(tag) || SHA256(tag) || script) * * @param script - The raw Arkade script bytes * @returns 32-byte hash */ declare function arkadeScriptHash(script: Uint8Array): Uint8Array; /** * Compute the tagged hash of an Arkade witness. * Uses BIP-340 tagged hash: SHA256(SHA256(tag) || SHA256(tag) || witness) * * @param witness - The raw Arkade witness bytes * @returns 32-byte hash, or 32 zero bytes if witness is empty */ declare function arkadeWitnessHash(witness: Uint8Array): Uint8Array; /** * Compute the Arkade script tweaked public key. * result = pubKey + taggedHash("ArkScriptHash", script) * G * * The input pubKey should be a compressed (33-byte) or x-only (32-byte) public key. * Returns a 32-byte x-only public key. * * @param pubKey - The emulator's base public key (32 or 33 bytes) * @param script - The raw Arkade script bytes * @returns 32-byte x-only tweaked public key */ declare function computeArkadeScriptPublicKey(pubKey: Uint8Array, script: Uint8Array): Uint8Array; /** * BigNum — arbitrary-precision sign-magnitude little-endian encoding used by * the arkade VM in emulator v0.0.1. Up to 520 bytes (= MaxScriptElementSize). * * Wire format: * - empty bytes = 0 * - last byte's high bit (0x80) is the sign (set = negative) * - remaining bits are magnitude, little-endian * - minimal: no trailing zero magnitude byte; `[0x80]` (negative zero) is rejected * * Wraps `@scure/btc-signer`'s `ScriptNum` with a 520-byte cap. Use the * standalone API when you need to round-trip values outside of script encoding; * inside `ArkadeScript.encode([...])`, plain `bigint` literals work directly. */ /** Maximum number of bytes for a BigNum (= MaxScriptElementSize). */ declare const BIGNUM_MAX_BYTES = 520; /** * Encode `value` as a minimal sign-magnitude little-endian byte string. * Throws if the encoding would exceed 520 bytes. */ declare function encode(value: bigint): Uint8Array; /** * Decode a minimal sign-magnitude little-endian byte string into a bigint. * Throws on non-minimal encodings or values longer than 520 bytes. */ declare function decode(value: Uint8Array): bigint; declare const bignum_BIGNUM_MAX_BYTES: typeof BIGNUM_MAX_BYTES; declare const bignum_decode: typeof decode; declare const bignum_encode: typeof encode; declare namespace bignum { export { bignum_BIGNUM_MAX_BYTES as BIGNUM_MAX_BYTES, bignum_decode as decode, bignum_encode as encode }; } /** * Read the JSON `arkadec` writes into a {@link Program}: `$param` placeholders, * @scure opcode names, and one structured tapleaf per compiler leaf. */ interface ArtifactParameter { name: string; type: string; } interface ArtifactStruct { name: string; fields: ArtifactParameter[]; } interface ArtifactWitnessElement { name: string; type: string; injected?: boolean; } interface ArtifactLeaf { name: string; witness?: ArtifactWitnessElement[]; asm: string[]; } interface ArtifactCovenant { inputs: ArtifactParameter[]; asm: string[]; } interface ArtifactGroup { name: string; arkade?: ArtifactCovenant; leaves: ArtifactLeaf[]; } interface ContractArtifact { contractName: string; constructorInputs: ArtifactParameter[]; structs?: ArtifactStruct[]; functions: ArtifactGroup[]; } /** `true` when `value` is an arkadec artifact rather than a Program. */ declare function isContractArtifact(value: unknown): value is ContractArtifact; /** Adds `server`, and one param per child-output placeholder for the caller to bind to that output key. */ declare function programFromArtifact(artifact: ContractArtifact): Program; /** * Arkade Contract — artifact-driven, high-level covenant API. * * Implements the Arkade contract model: a contract is a {@link Program} (a set of * named functions, each split into a `tapscript` segment enforced on-chain and an * `arkadeScript` segment emulated by the co-signing service). The same JS object * shape mirrors the compiler artifact, so a hand-written program today and the * compiler's JSON output later flow through the identical resolver. * * The SDK never interprets scripts: it resolves `$param` placeholders into bytes, * builds the taproot tree (resolving `$param` signers such as `$server`/`$user` from * the constructor args, plus the co-signer key tweaked by the arkade-script hash), * and assembles the spend from the per-segment witness layout. All key tweaking, * packet encoding and PSBT plumbing is internal — from the caller's side it is just * Arkade. * * Program → script compilation lives in {@link ArkadeProgramScript} * (./program.ts) and is shared with the generic `"arkade"` contract handler, * so a contract created here can be persisted, watched and re-derived through * the standard `src/contracts` pipeline: pass a `contractManager` to * {@link Arkade.connect} and call {@link ArkadeContract.register}. * * Contract functions are strongly typed from the program's literal shape: a * function declaring `inputs: [{ name: "preimage", type: "bytes" }]` produces a * `functions.claim(preimage: Uint8Array)` call signature. The type flows through * `arkade.contract(...)` automatically for inline literals; for a program stored * in a variable, annotate it with `satisfies Program` to preserve the literal * type (a plain `: Program` annotation would widen it away). * * Constructor `params` follow the same convention: bare name strings are * documentation only, while typed descriptors `{ name, type }` (the form the * ArkadeScript compiler emits) make the list authoritative — every declared * param must be bound, every `$name` reference must be declared, and bound * values are validated against their type at compilation. * * A covenant spend needs an `indexer`, not just an `emulator`: the co-signing * service resolves each input's prevout pkScript from the previous ark tx the * PSBT carries, and only the indexer can supply those bytes. Pure tapscript * spends go straight to arkd and need neither. * * @example * ```typescript * const htlcProgram = { * version: 0, * name: "htlc", // metadata only — round-tripped, never compiled * params: [ * { name: "hash", type: "hash" }, * { name: "receiver", type: "pubkey" }, * { name: "amount", type: "int" }, * { name: "server", type: "pubkey" }, * ], * functions: { * claim: { * inputs: [{ name: "preimage", type: "bytes" }], * tapscript: { signers: ["$server"], asm: ["HASH160", "$hash", "EQUAL"], witness: ["preimage"] }, * arkadeScript: { asm: payTo, witness: [0] }, * }, * }, * } satisfies Program; * * const arkade = await Arkade.connect({ arkade: ark, emulator, indexer, identity, network }); * // `server` is declared in `params`, so it defaults to the client's server key. * const htlc = arkade.contract(htlcProgram, { hash, receiver, amount: 10_000n }); * // `preimage` is typed Uint8Array; calling `claim()` with no args is a type error. * const { txid } = await htlc.functions.claim(preimage).to(receiver, 10_000n).send(); * ``` * * @module arkade/contract */ /** The TS type of a single call argument, derived from its {@link InputRef}. */ type ArgTsType = R extends InputDef ? ArkadeValueType[R["type"]] : ArkadeArgValue; /** The argument tuple of a function, derived from its declared `inputs`. */ type ArgsTuple = { [K in keyof I]: ArgTsType; }; /** The call signature arguments for one function (nullary when `inputs` is absent). */ type FnArgs = F extends { inputs: infer I extends readonly InputRef[]; } ? ArgsTuple : []; /** * The statically-typed `functions` map of a contract. When the program is a * concrete literal (specific function names), each entry is precisely typed from * its `inputs`. When the program is the widened {@link Program} (an index * signature), it falls back to the loose {@link CallableFunctions}. */ type ContractFunctions

= string extends keyof P["functions"] ? CallableFunctions : { [K in keyof P["functions"]]: (...args: FnArgs) => ArkadeTransactionBuilder; }; /** A spendable coin (VTXO) — outpoint + value. */ interface Utxo { txid: string; vout: number; value: number; /** * Raw wire bytes of the ark transaction that created this VTXO, attached as * the PrevArkTx field on input 0. * * An override, not the only channel: covenant spends resolve the previous * tx of every input through the client's indexer. Supply it when the tx is * not yet indexable — a recursive covenant spending the output of an ark tx * the caller just built — and the resolved value is suppressed for input 0. */ sourceTx?: Uint8Array; } /** * An asset-group transfer attached to a spend: which inputs supply the asset and * which outputs receive it. Encoded into the asset Packet alongside the emulator * packet in the same OP_RETURN extension. */ interface AssetSpec { /** The asset id — serialized hex string or raw bytes. */ assetId: string | Uint8Array; /** Inputs supplying the asset (by transaction input index). */ inputs: { vin: number; amount: bigint | number; }[]; /** Outputs receiving the asset (by transaction output index). */ outputs: { vout: number; amount: bigint | number; }[]; /** Optional metadata key/value entries. */ metadata?: { key: Uint8Array; value: Uint8Array; }[]; } /** Result of a successful spend. */ interface ArkadeSpendResult { txid: string; signedArkTx: string; signedCheckpointTxs: string[]; } /** The callable spending paths of a contract. */ type CallableFunctions = Record ArkadeTransactionBuilder>; /** Options for {@link Arkade.connect}. */ interface ArkadeConnectOptions { /** The Ark/Arkade server provider. */ arkade: Pick; /** * The co-signing (introspector/emulator) service. Optional: only required for * contracts whose functions have an `arkadeScript` (covenant paths). Pure * tapscript contracts (multisig/timelock/hashlock) don't need it. */ emulator?: EmulatorProvider; /** * Indexer — enables `getUtxos`/`getBalance` and coin auto-selection, and * resolves the previous ark txs a covenant spend must carry (see * {@link attachPrevArkTxs}). Required for covenant spends. */ indexer?: Pick; /** Signer for paths that require a user signature; optional for watch-only. */ identity?: Identity; /** * Network for address derivation. Defaults to the network the server * reports on `getInfo` — or the SDK default when the server names none. */ network?: Network; /** * Co-sign with this emulator key (33-byte compressed hex) instead of the * one pinned for `network`. * * Needed when a network's emulator key has rotated ahead of this SDK, for a * self-hosted emulator, or on a network with no pinned key at all (signet, * testnet, a hand-built `Network` — those throw without it). Setting it * means trusting that operator as co-signer in place of the network's. * * @see resolveEmulatorPubkey */ emulatorPubkey?: string; /** * The wallet's contract manager. When set, contracts created from this * client can {@link ArkadeContract.register} themselves into the standard * contract pipeline (persistence, watching, events), and * {@link ArkadeContract.getUtxos} reads repository-backed state * (offline-first) for registered contracts instead of querying the * indexer directly. Obtain it via `wallet.getContractManager()`. */ contractManager?: IContractManager; } /** * A connected Arkade client. Holds the providers/identity/network and the * resolved network constants (server key, co-signer key, checkpoint closure), * so spinning up contracts is synchronous. */ declare class Arkade { readonly arkProvider: Pick; /** The co-signing service, or undefined for emulator-less (pure tapscript) usage. */ readonly emulator: EmulatorProvider | undefined; readonly network: Network; readonly serverKey: Uint8Array; /** * The co-signer key covenants are built against (33-byte compressed), * present only when an emulator is configured. * * Resolved from the network — or from `emulatorPubkey` — never from the * emulator's own report. When a claim is refused because the co-signer * disagrees, compare `hex.encode(arkade.emulatorKey)` against the * `signerPubkey` your emulator serves on `/v1/info`: if they differ, the * network rotated and this SDK's pin is stale. */ readonly emulatorKey: Uint8Array | undefined; readonly checkpoint: CSVMultisigTapscript.Type; readonly indexer?: Pick; readonly identity?: Identity; /** The signing identity's x-only public key, resolved at connect — identifies which inputs the wallet signs. */ readonly userKey?: Uint8Array; /** The wallet's contract manager, when contract persistence is wired up. */ readonly contractManager?: IContractManager; private constructor(); /** Connect and resolve the server key, checkpoint closure and (if present) the co-signer key. */ static connect(opts: ArkadeConnectOptions): Promise; /** * Instantiate a contract from a program and its constructor arguments. The * `program`'s literal type is preserved (`const` inference) so the resulting * contract's `functions` map is strongly typed — `functions.(...)` * knows each argument's type from the function's `inputs` descriptors. * * When the program declares a `server` or `user` param and the caller does * not bind it, it defaults to the client's server key or the identity's * key respectively; explicit args always win. */ contract(program: P, args?: Record): ArkadeContract

; } /** A resolved, instantiated Arkade contract. */ declare class ArkadeContract

{ readonly client: Arkade; readonly program: P; readonly args: Record; /** The compiled taproot tree of spending-path leaves. */ readonly vtxoScript: ArkadeProgramScript; /** Encoded taproot tree (shared spend context for every path). */ readonly tapTree: Uint8Array; /** The signer keys the program was compiled against. */ readonly keys: ProgramKeys; /** Spending paths in declaration order. */ private readonly compiled; constructor(client: Arkade, program: P, args?: Record, keys?: ProgramKeys); /** * Rebuild a callable contract from a persisted `"arkade"` contract row * (see {@link ArkadeContract.register}). The stored keys are used for * compilation — not the client's current ones — so the derived script and * address stay identical to the registered contract even after a server * signer rotation. */ static fromContract(client: Arkade, contract: Contract): ArkadeContract; /** Resolve the {@link TapLeafScript} for a spending path by its index. */ leafScript(index: number): TapLeafScript; /** Arkade funding address. */ get address(): string; /** Taproot output script. */ get pkScript(): Uint8Array; /** * Callable spending paths: `contract.functions.(...args)`. Strongly * typed from the program's literal type — each function's argument types are * derived from its `inputs` descriptors (see {@link ContractFunctions}). */ get functions(): ContractFunctions

; /** * The `createContract` payload for this contract — the serialized program, * args and keys plus the derived script/address. Useful when registering * through a manager the client does not hold. */ toContractParams(): { type: string; params: Record; script: string; address: string; }; /** * Persist this contract through the wallet's {@link IContractManager} so it * is tracked like any other contract type: stored in the contract * repository, watched for VTXO events, counted in repository-backed * balances, and re-derivable offline via the `"arkade"` contract handler. * Idempotent — re-registering the same script is a no-op. */ register(options?: { label?: string; metadata?: Record; }): Promise; /** * Spendable VTXOs locked by this contract. * * When a `contractManager` is configured and this contract is registered, * reads the repository-backed state (offline-first, kept fresh by the * contract watcher). Otherwise falls back to a direct indexer query. * * Both branches refuse a spent or unilaterally exited output. They are not * otherwise interchangeable: the fallback asks `spendableOnly`, which also * drops swept coins — the manager branch keeps those. */ getUtxos(): Promise; /** Total spendable balance (requires an indexer). */ getBalance(): Promise; } /** * Fluent builder for a single spend. Obtained from `contract.functions.(...)`; * chain `.from()`/`.to()` then `.send()` (broadcast) or `.build()` (assemble only). */ declare class ArkadeTransactionBuilder { private readonly contract; private readonly fn; private readonly args; private readonly outputs; private readonly fundingCoins; private readonly assetSpecs; private coin?; private changeScript?; /** @internal */ constructor(contract: ArkadeContract, fn: CompiledProgramFunction, args: Record); from(coin: Utxo): this; /** * Add extra inputs the caller funds (e.g. a taker's own coins in a swap). * These become inputs 1..n and are signed with the client identity. */ fund(coins: ArkTxInput[]): this; /** Transfer an asset group: which inputs supply it and which outputs receive it. */ withAsset(spec: AssetSpec): this; /** Destination for any surplus (inputs − outputs). Required when the spend is not exact. */ change(script: Uint8Array): this; /** Add an output — `(script, amount)` or a list of outputs. */ to(script: Uint8Array, amount: bigint): this; to(outputs: TransactionOutput[]): this; /** Assemble the unsigned ark transaction and its checkpoints. */ build(): Promise<{ arkTx: Transaction; checkpoints: Transaction[]; }>; /** Build, submit and return the finalized transaction. */ send(): Promise; /** Sign the client-owned inputs of the ark tx (no-op when there are none). */ private signArk; /** Indexes of inputs the client owns and must sign (contract input + funded inputs). */ private userInputIndexes; private selectCoin; private buildAssetPacket; private witnessBytes; } /** * Arkade Script Support * * This module provides encoding/decoding support for Arkade script opcodes * and PSBT fields. It reuses @scure/btc-signer for all Bitcoin standard * functionality and only adds Arkade-specific extensions. * * ## Features * - Standard Bitcoin opcodes via @scure/btc-signer (re-exported as OP) * - Arkade extension opcodes (0xb3, 0xc4-0xf3) * - Script encoding/decoding via ScriptElement arrays * - ASM format conversion with Arkade opcode support * * @module arkade */ declare const index_ARKADE_OP: typeof ARKADE_OP; declare const index_ARKADE_OPCODES: typeof ARKADE_OPCODES; declare const index_ARKADE_OPCODE_NAMES: typeof ARKADE_OPCODE_NAMES; declare const index_ARKADE_OPCODE_VALUES: typeof ARKADE_OPCODE_VALUES; declare const index_ARKADE_OPS: typeof ARKADE_OPS; type index_Arkade = Arkade; declare const index_Arkade: typeof Arkade; declare const index_ArkadeArgType: typeof ArkadeArgType; declare const index_ArkadeArgValue: typeof ArkadeArgValue; type index_ArkadeBatchInput = ArkadeBatchInput; type index_ArkadeConnectOptions = ArkadeConnectOptions; type index_ArkadeContract

= ArkadeContract

; declare const index_ArkadeContract: typeof ArkadeContract; declare const index_ArkadeContractParams: typeof ArkadeContractParams; type index_ArkadeExtendedCoin = ArkadeExtendedCoin; type index_ArkadeExtendedVirtualCoin = ArkadeExtendedVirtualCoin; declare const index_ArkadeFunction: typeof ArkadeFunction; declare const index_ArkadeParamValue: typeof ArkadeParamValue; declare const index_ArkadeProgramScript: typeof ArkadeProgramScript; declare const index_ArkadeScript: typeof ArkadeScript; type index_ArkadeScriptOP = ArkadeScriptOP; type index_ArkadeScriptType = ArkadeScriptType; declare const index_ArkadeSegment: typeof ArkadeSegment; type index_ArkadeSpendResult = ArkadeSpendResult; type index_ArkadeTransactionBuilder = ArkadeTransactionBuilder; declare const index_ArkadeTransactionBuilder: typeof ArkadeTransactionBuilder; declare const index_AsmToken: typeof AsmToken; type index_AssetSpec = AssetSpec; type index_CallableFunctions = CallableFunctions; declare const index_CompiledProgramFunction: typeof CompiledProgramFunction; type index_ContractArtifact = ContractArtifact; type index_ContractFunctions

= ContractFunctions

; declare const index_InputDef: typeof InputDef; declare const index_InputRef: typeof InputRef; declare const index_OP: typeof OP; declare const index_OPCODE_NAMES: typeof OPCODE_NAMES; declare const index_OPCODE_VALUES: typeof OPCODE_VALUES; declare const index_Program: typeof Program; declare const index_ProgramKeys: typeof ProgramKeys; declare const index_SUPPORTED_PROGRAM_VERSION: typeof SUPPORTED_PROGRAM_VERSION; declare const index_Script: typeof Script; declare const index_ScriptType: typeof ScriptType; declare const index_SignerRef: typeof SignerRef; declare const index_TapscriptSegment: typeof TapscriptSegment; declare const index_TweakedSigner: typeof TweakedSigner; type index_Utxo = Utxo; declare const index_WitnessRef: typeof WitnessRef; declare const index_arkadeScriptHash: typeof arkadeScriptHash; declare const index_arkadeWitnessHash: typeof arkadeWitnessHash; declare const index_asmToBytes: typeof asmToBytes; declare const index_bytesToASM: typeof bytesToASM; declare const index_computeArkadeScriptPublicKey: typeof computeArkadeScriptPublicKey; declare const index_createArkadeBatchHandler: typeof createArkadeBatchHandler; declare const index_deserializeArkadeContractParams: typeof deserializeArkadeContractParams; declare const index_fromASM: typeof fromASM; declare const index_getOpcodeName: typeof getOpcodeName; declare const index_getOpcodeValue: typeof getOpcodeValue; declare const index_isContractArtifact: typeof isContractArtifact; declare const index_parseArtifact: typeof parseArtifact; declare const index_programFromArtifact: typeof programFromArtifact; declare const index_resolveAsm: typeof resolveAsm; declare const index_serializeArkadeContractParams: typeof serializeArkadeContractParams; declare const index_stringifyArtifact: typeof stringifyArtifact; declare const index_toASM: typeof toASM; declare const index_validateProgram: typeof validateProgram; declare const index_witnessRefToBytes: typeof witnessRefToBytes; declare namespace index { export { index_ARKADE_OP as ARKADE_OP, index_ARKADE_OPCODES as ARKADE_OPCODES, index_ARKADE_OPCODE_NAMES as ARKADE_OPCODE_NAMES, index_ARKADE_OPCODE_VALUES as ARKADE_OPCODE_VALUES, index_ARKADE_OPS as ARKADE_OPS, index_Arkade as Arkade, index_ArkadeArgType as ArkadeArgType, index_ArkadeArgValue as ArkadeArgValue, type index_ArkadeBatchInput as ArkadeBatchInput, type index_ArkadeConnectOptions as ArkadeConnectOptions, index_ArkadeContract as ArkadeContract, index_ArkadeContractParams as ArkadeContractParams, type index_ArkadeExtendedCoin as ArkadeExtendedCoin, type index_ArkadeExtendedVirtualCoin as ArkadeExtendedVirtualCoin, index_ArkadeFunction as ArkadeFunction, index_ArkadeParamValue as ArkadeParamValue, index_ArkadeProgramScript as ArkadeProgramScript, index_ArkadeScript as ArkadeScript, type index_ArkadeScriptOP as ArkadeScriptOP, type index_ArkadeScriptType as ArkadeScriptType, index_ArkadeSegment as ArkadeSegment, type index_ArkadeSpendResult as ArkadeSpendResult, index_ArkadeTransactionBuilder as ArkadeTransactionBuilder, index_AsmToken as AsmToken, type index_AssetSpec as AssetSpec, bignum as BigNum, type index_CallableFunctions as CallableFunctions, index_CompiledProgramFunction as CompiledProgramFunction, type index_ContractArtifact as ContractArtifact, type index_ContractFunctions as ContractFunctions, index_InputDef as InputDef, index_InputRef as InputRef, index_OP as OP, index_OPCODE_NAMES as OPCODE_NAMES, index_OPCODE_VALUES as OPCODE_VALUES, index_Program as Program, index_ProgramKeys as ProgramKeys, index_SUPPORTED_PROGRAM_VERSION as SUPPORTED_PROGRAM_VERSION, index_Script as Script, index_ScriptType as ScriptType, index_SignerRef as SignerRef, index_TapscriptSegment as TapscriptSegment, index_TweakedSigner as TweakedSigner, type index_Utxo as Utxo, index_WitnessRef as WitnessRef, index_arkadeScriptHash as arkadeScriptHash, index_arkadeWitnessHash as arkadeWitnessHash, index_asmToBytes as asmToBytes, index_bytesToASM as bytesToASM, index_computeArkadeScriptPublicKey as computeArkadeScriptPublicKey, index_createArkadeBatchHandler as createArkadeBatchHandler, index_deserializeArkadeContractParams as deserializeArkadeContractParams, index_fromASM as fromASM, index_getOpcodeName as getOpcodeName, index_getOpcodeValue as getOpcodeValue, index_isContractArtifact as isContractArtifact, index_parseArtifact as parseArtifact, index_programFromArtifact as programFromArtifact, index_resolveAsm as resolveAsm, index_serializeArkadeContractParams as serializeArkadeContractParams, index_stringifyArtifact as stringifyArtifact, index_toASM as toASM, index_validateProgram as validateProgram, index_witnessRefToBytes as witnessRefToBytes }; } /** Known BIP21 parameters including Arkade-specific extensions. */ interface BIP21Params { address?: string; amount?: number; label?: string; message?: string; /** Optional Arkade address parameter. */ ark?: string; /** Optional Silent Payment address parameter. */ sp?: string; [key: string]: string | number | undefined; } /** Result returned by `BIP21.parse`. */ interface BIP21ParseResult { originalString: string; params: BIP21Params; } declare enum BIP21Error { INVALID_URI = "Invalid BIP21 URI", INVALID_ADDRESS = "Invalid address" } declare class BIP21 { /** * Create a BIP21 URI from the provided parameters. * * @param params - BIP21 parameters to encode * @returns Encoded BIP21 URI */ static create(params: BIP21Params): string; /** * Integer-sats amount encoded in a BIP21 URI (`amount=` is BTC), or * `undefined` when absent or the URI is unparseable. Shared by the payment * rails, which accept either a bare address or a BIP21 URI. * * @param uri - BIP21 URI (or bare address) to read the amount from * @returns The amount in satoshis, or `undefined` */ static amountSats(uri: string): number | undefined; /** * Parse a BIP21 URI and return its decoded parameters. * * @param uri - BIP21 URI to parse * @returns Parsed BIP21 URI data * @throws Error if the URI does not start with the `bitcoin:` scheme */ static parse(uri: string): BIP21ParseResult; } type PaymentStatus = "pending" | "sent" | "settled" | "failed"; interface RouteResult { railId: string; txid?: string; preimage?: string; swapId?: string; } /** An in-flight payment. Observe it — its outcome may also arrive out-of-band * (swap monitor / webhook) and surface as a tx in the wallet's history. */ interface PaymentHandle { readonly id: string; readonly status: PaymentStatus; /** Progress stream; returns an unsubscribe fn. Replays the latest update. * A terminal `"failed"` update carries the rejection in `error` (for swap rails * a `SwapError` whose `isRefundable`/`pendingSwap` drive recovery). */ subscribe(fn: (u: { status: PaymentStatus; result?: RouteResult; error?: unknown; }) => void): () => void; /** OPTIONAL await — resolves on a terminal result, rejects on the timeout. * Fire-and-forget rails may never resolve it; that is allowed. */ settled(opts?: { timeoutMs?: number; }): Promise; } /** * A priced route. The three amounts are **receiver-exact** and mean the same * thing on every rail, so `options()` can be ranked on cost and a rail swapped * without changing what the recipient gets: * * - `amount` — sats delivered **to the recipient**. * - `fee` — sats charged **on top**, by the rail and its counterparty. * - `total` — `amount + fee`; the sats that leave the wallet. * * Rails whose underlying primitive *deducts* its fee (the collaborative exit) * gross the amount up to honour this; rails that add it on top pass it through. * A rail that does not perform the deducting spend cannot gross up to a fee it * never chooses, and flags that instead: see `claimFeeDeductedFromPayout`. * * `fee` is a pre-send estimate wherever the true cost is only fixed later: the * swap rails quote from Boltz's advertised pricing and are superseded by the * amount Boltz returns at swap creation, and the collaborative exit omits the * per-input intent fees whenever the request left the VTXO selection to * settlement — naming {@link PaymentRequest.selectedVtxos} fixes them, and they * are priced in. Treat it as a display and ranking figure, not a guarantee. */ interface RouteQuote { railId: string; /** Sats delivered to the recipient. */ amount: number; /** Sats charged on top of {@link amount}; an estimate on the swap rails. */ fee: number; /** `amount + fee` — what leaves the wallet. */ total: number; /** * Unix seconds after which the counterparty stops honouring this quote. * * Absent means "nothing to observe", not "never expires": a rail with no * counterparty and no quote book — an Arkade transfer, an asset transfer, a * collaborative exit — has no validity to state. A caller holding a quote * across user think-time should read absence as "no check possible", and must * still expect {@link send} to refuse either way: the swap rails re-check * validity there, which is the only point that can judge it against the * moment of spending. */ validUntil?: number; /** * @experimental The asset shape is provisional. The v2 swap client models * assets as `give`/`take`/`amountOn` over `AssetRef`, and the two * vocabularies are expected to converge on v0.5; do not treat this field as * stable 0.4.x API. * * The asset view; absent means BTC only. The sats fields keep their * meaning — an asset rides a sats-carrying output, so `amount` is the * carrier, not zero. * * A pair, not a triple: on a cross-asset route these name DIFFERENT * assets, so `total = amount + fee` cannot hold across the two units — * which forces the purchase price into `fee`. Prefer `assets.spent` for a * cost display. */ assets?: { delivered: Asset; spent: Asset; }; /** Execute. Returns an observable handle, never a bare result. */ send(): Promise; meta?: Record; } interface RouterPreferences { /** Ordered rail ids; first matching+available wins. Default ships in the factory. */ priority?: string[]; disabled?: string[]; caps?: Record; /** route() behaviour when >1 option survives ranking. Default "first". */ tieBreak?: "first" | "require-choice"; } interface RouterContext { /** A rail needing more than this takes it as a constructor dep, rather than * narrowing the context every other rail shares. */ wallet: IWallet; /** Loosely typed in core to avoid a dependency on boltz-swap; swap rails cast it. */ swaps?: unknown; prefs: RouterPreferences; } /** Mirrors {@link Recipient}: an Arkade address is the same string for BTC and * for an asset, so the AMOUNT names the asset, never the target. */ interface PaymentRequest { /** Raw target: bare address/invoice, or a BIP21 URI. */ raw: string; /** Explicit sats; supplements/overrides any amount encoded in `raw`. */ amount?: number; /** @experimental — provisional, see {@link RouteQuote.assets}. * * Additive: an asset transfer also moves sats, so a 500 USDX request * legitimately has both. A rail that cannot deliver assets must REFUSE. */ assets?: Asset[]; /** * Spend exactly these virtual outputs, with the meaning * {@link SendParams.selectedVtxos} already gives it: taken as given, so a * shortfall is an error rather than a top-up, and ungated like * `settle({ inputs })`. Normalized, i.e. what `getSpendableVtxos()` returns. * * Only the rails spending the wallet's own coins can honour it (`ark`, * `ark-asset`, `onchain`). A rail funding its payment through a counterparty * picks its own inputs, so it must REFUSE rather than ignore the selection. */ selectedVtxos?: NormalizedExtendedVirtualCoin[]; } /** A payment rail — registered by id, mirrors the ActivityRegistry resolver shape. */ interface PaymentRail { id: string; /** Classification only — amount-blind; takes the request for uniformity. */ match(req: PaymentRequest, ctx: RouterContext): boolean; /** Availability gate — where a rail drops itself for an out-of-limits amount. */ available?(req: PaymentRequest, ctx: RouterContext): boolean | Promise; quote(req: PaymentRequest, ctx: RouterContext): Promise; } interface PaymentOption { railId: string; /** Lazy — resolves fee/amount + prepares execution only when called. The * amount is fixed by the request, so this is no-arg. */ quote(): Promise; } /** * Target classification for the payment rails — format only. No bech32 * checksum, no bolt11 decode (core carries no bolt11 dependency), no network * match against the wallet. Rails re-validate before spending: `onchain` via * `Ramps.offboard` → `Address.decode`, `lightning` via `getInvoiceSatoshis`. */ /** True if the string decodes as an Arkade address (canonical SDK check). */ /** True for a BOLT11 invoice (with or without a `lightning:` prefix). HRPs: * mainnet, testnet, signet, regtest, simnet. The amount rides inside the HRP * (`lnbc2500u1…`), so the tail stays loose. */ declare const isLightningInvoice: (raw: string) => boolean; /** * True for an LNURL or a Lightning address (`user@host`). * * No rail here consumes it, and that is the layering rather than a gap: * routing an LNURL needs an LNURL client, so the rails ship from * `@arkade-os/lnurl-client/arkade` (`lnurlRails`) and are registered by the * app, as the solver rails from `@arkade-os/swap` are. With none registered, * `route()` throws "no rail for" on these targets. */ declare const isLnurl: (raw: string) => boolean; /** True for a bech32 (segwit) or base58 (legacy) Bitcoin address, any network — * legacy covers testnet/regtest `m`/`n`/`2` alongside mainnet `1`/`3`. */ declare const isBtcAddress: (raw: string) => boolean; /** The ark address in `raw`: bare, or the `ark=` param. */ declare const arkTarget: (raw: string) => string | undefined; /** The on-chain BTC address in `raw`: bare, or the URI's address. */ declare const btcTarget: (raw: string) => string | undefined; /** The bolt11 invoice in `raw` with any `lightning:` prefix stripped: bare, or * the `lightning=` param. */ declare const invoiceTarget: (raw: string) => string | undefined; /** Throw unless `amt` is a positive integer number of satoshis. */ declare function assertSendableAmount(railId: string, amt: number): void; /** * Resolve a rail's send amount from an explicit value or the BIP21 `amount=` * param, validated as a positive integer number of satoshis. Rejecting here * keeps the router from surfacing a `{ amount: 0 }` quote that only fails later * in wallet/swap code. */ declare function resolveSendAmount(railId: string, raw: string, amount?: number): number; /** * Non-throwing counterpart of {@link resolveSendAmount}, for availability * checks that must not reject the router: returns the positive-integer sats * from the explicit `amount` or the BIP21 `amount=` param, or `undefined` when * none is present or the value is not a positive integer. The missing-vs-invalid * distinction is left to `resolveSendAmount` at quote time. */ declare function tryResolveSendAmount(raw: string, amount?: number): number | undefined; /** The assets a request asks for — `[]` when it names none. */ declare function assetsOf(req: PaymentRequest): Asset[]; /** For a BTC-only rail: refusing is what leaves the router free to rank an * asset-capable one, instead of paying the carrier and dropping the asset. */ declare function assertNoAssets(railId: string, req: PaymentRequest): void; /** The inputs a request names. An EMPTY array is still a selection, covering nothing. */ declare function selectionOf(req: PaymentRequest): NormalizedExtendedVirtualCoin[] | undefined; /** For a rail whose counterparty funds the spend: ignoring a named input set * would spend coins the caller had promised elsewhere, so refuse instead. */ declare function assertNoSelection(railId: string, req: PaymentRequest): void; /** Exactly one: `Wallet.send` takes a list because one output can carry * several, but a routed PAYMENT is one thing being paid. */ declare function resolveAssetAmount(railId: string, req: PaymentRequest): Asset; type Update = { status: PaymentStatus; result?: RouteResult; error?: unknown; }; /** Build an observable handle. `run` starts immediately and gets an `emit` to * stream progress; its resolved value is the terminal result. */ declare function makeHandle(id: string, run: (emit: (u: Update) => void) => Promise): PaymentHandle; /** Thrown by `route()` when several options survive and `tieBreak` is * `"require-choice"` — the app must disambiguate via `options()`. */ declare class AmbiguousRouteError extends Error { readonly railIds: string[]; constructor(railIds: string[]); } /** * Registry-based payment router. Holds id-keyed {@link PaymentRail} entries * (mirroring `ActivityRegistry`): `use()` overwrites by id, `remove()` deletes. * It operates on a {@link PaymentRequest} (raw target + optional amount) — * rails self-extract their target via the shared helpers, and `available()` * gates on the amount. `options()` returns every matching+available rail ranked * by preferences; `route()` is the "take the top" convenience. */ declare class PaymentRouter { private readonly ctx; private readonly rails; constructor(ctx: RouterContext); /** Register a rail (or overwrite one with the same id). */ use(rail: PaymentRail): this; /** Remove a rail by id. */ remove(id: string): this; /** Rails matching `req`, filtered by `disabled`/`available()`, ranked by * `priority` (unlisted rails keep insertion order, after listed ones). */ options(req: PaymentRequest, prefs?: RouterPreferences): Promise; /** Top-ranked option's quote. Throws when nothing routes, or * {@link AmbiguousRouteError} when `tieBreak` is `"require-choice"` and * more than one option survives. */ route(req: PaymentRequest, prefs?: RouterPreferences): Promise; } /** * Off-chain Arkade send. Matches a bare ark address or the `ark=` param of a * unified BIP21 URI, and settles via `Wallet.send`. * * The only rail that is receiver-exact for free: an off-chain send delivers the * full amount and carries no counterparty or on-chain fee, so `fee` is 0 and * `total` equals `amount` (see {@link RouteQuote}). * * BTC only: a request naming an asset drops this rail rather than paying its * sats and dropping the asset, leaving `ark-asset` to rank. */ declare function arkRail(): PaymentRail; /** The sats carrier an asset rides on; mirrors `Recipient.amount`'s default. */ declare const ASSET_CARRIER_SATS = 330; /** * Off-chain Arkade send of an ASSET — the sibling of `ark`. * * They split on `available()` rather than `match()` because both legitimately * match the address; which one can pay is a question about the amount, so * `options()` returns exactly one and their relative priority is immaterial. */ declare function arkAssetRail(): PaymentRail; /** The canonical fee source for {@link onchainRail}: the operator's schedule, as * the wallet already reads it. Keeps the one `arkProvider` reach in one place, * so an app registering the rail itself need not know where fees live. */ declare const walletFeeSource: (wallet: Wallet) => () => Promise; /** * On-chain BTC send via collaborative exit — the Wallet-only on-chain path (no * swap). Matches a bare BTC address or the on-chain part of a unified BIP21 URI * and offboards VTXOs to the address via {@link Ramps.offboard}, which owns * fee-aware coin selection, dust-safe change, and settlement. * * The exit fee is priced on the DESTINATION output — the one arkd charges for — * and met from the balance, so the recipient receives exactly `quote.amount` * (see {@link RouteQuote}). {@link Ramps.offboardExact} carries that; the * gross-up this rail used to do could only approximate it. * * A request naming `selectedVtxos` offboards exactly those — so the exit need not * sweep the whole off-chain balance into one change output — and only then does * the quote carry the per-input intent fees: unselected, the coins are chosen * later at settlement and there is nothing here to price. * * An explicit amount is mandatory. To sweep the full balance, call * `Ramps.offboard(address, feeInfo)` directly — the router has no amountless path. * * @param deps.feeInfo - Source of the operator's fee schedule. Required: a rail * that cannot price an offboard must not be constructible — a silently dropped * rail on the money path is worse than a wiring error at startup. */ declare function onchainRail(deps: { feeInfo: () => Promise; }): PaymentRail; /** * Default payment router with the Wallet-only rails: `ark` (off-chain BTC send), * `ark-asset` (off-chain asset send) and `onchain` (collaborative exit). * Lightning and chain-swap rails live in `@arkade-os/boltz-swap`, which ships a * `createDefaultPaymentRouter(wallet, swaps)` overload composing the full set; * solver-routed rails live in `@arkade-os/swap` and are registered by the app. * * The default priority is `["ark", "ark-asset", "lightning", "onchain"]`. * `"lightning"` is listed so it ranks correctly once the boltz rail is added. * `ark` and `ark-asset` never compete — the amount decides which is * available — so their order between themselves is immaterial. */ declare function createDefaultPaymentRouter(wallet: Wallet): PaymentRouter; /** * Check if an absolute (CLTV) locktime is currently satisfied. * * Following the BIP65 convention: * - locktime < 500_000_000 → interpreted as a block height; compared against `context.blockHeight` * - locktime >= 500_000_000 → interpreted as a Unix timestamp (seconds); compared against `context.currentTime` * * Returns false if the relevant context field is missing. */ declare function isCltvSatisfied(context: PathContext, locktime: bigint): boolean; /** * Check if a CSV timelock is currently satisfied for the given context/virtual output. */ declare function isCsvSpendable(context: PathContext, sequence?: number): boolean; /** * Convert RelativeTimelock to BIP68 sequence number. */ declare function timelockToSequence(timelock: RelativeTimelock): number; /** * Convert BIP68 sequence number back to RelativeTimelock. */ declare function sequenceToTimelock(sequence: number): RelativeTimelock; /** * Normalize a secp256k1 public key to the 32-byte x-only form every tapscript, * covenant param and `params.serverPubKey` in this SDK is denominated in. * * A compressed key drops its parity prefix; an x-only key passes through. Both * arrive over the same wires — arkd advertises `signerPubkey` either way, * `Identity.xOnlyPublicKey` implementations vary, and solver-supplied fields * are whatever the peer sent — so the caller usually cannot know which it * holds. Anything else is refused rather than silently truncated: a 65-byte * uncompressed key, stripped of one byte, yields a plausible 32-byte value * that is not the key, which surfaces as an address nobody funded. * * The result may alias `key` rather than copy it; callers that mutate either * value after normalization should copy first. * * @param key - 32-byte x-only or 33-byte compressed public key * @param label - Name used in the error, e.g. `"ark signer key"` * @see toXOnlySignerHex for the hex-in, hex-out signer-rotation equivalent */ declare function toXOnly(key: Uint8Array, label?: string): Uint8Array; declare const buildVersion = "0.9.9"; /** * The SDK's own version string, sourced from package.json */ declare const sdkVersion: string; /** * Wraps a transport-level `fetch` rejection (DNS failure, connection refused, * TLS or CORS error) with the request method and URL, preserving the original * as {@link Error.cause}. */ declare class FetchError extends Error { /** The request URL that failed, when derivable from the `fetch` input. */ readonly url?: string; /** The HTTP method of the failed request (defaults to `"GET"`). */ readonly method?: string; constructor(message: string, options: { url?: string; method?: string; cause?: unknown; }); } /** * Deadline applied to a read that carries no `AbortSignal` of its own. * * `fetch` has no default timeout, so a connection the network dropped silently * stays pending until the runtime gives up — and every bound built on top of it * waits with it. A retry ladder, a bounded queue or a mutex only work if the * call underneath them terminates. */ declare const READ_TIMEOUT_MS = 30000; /** * How long a blocked upgrade waits before the open is reported as failed. * * An `IDBOpenDBRequest` that is blocked never settles on its own: it waits for * the other connection to close, however long that takes. A hang is worse than a * failure here, because the caller cannot retry what never settles. */ declare const BLOCKED_UPGRADE_TIMEOUT_MS = 10000; /** A database upgrade was held open by another connection past * {@link BLOCKED_UPGRADE_TIMEOUT_MS}. Retryable: closing the other tab and * calling again is the remedy, and the bookkeeping was cleared so it can be. */ declare class DatabaseUpgradeBlockedError extends Error { readonly dbName: string; readonly name = "DatabaseUpgradeBlockedError"; constructor(dbName: string); } /** * Opens an IndexedDB database and increments the reference count. * Handles global object detection and callbacks. * * @param dbName The name of the database to open. * @param dbVersion The database version to open. * @param initDatabase A function that migrates the database schema, called * on `onupgradeneeded` only. Receives the database, the previous version * (0 for fresh installs), and the upgrade transaction — the transaction is * required for data migrations (cursor/update on existing stores). * * @returns A promise that resolves to the database instance. */ declare function openDatabase(dbName: string, dbVersion: number, initDatabase: (db: IDBDatabase, oldVersion: number, transaction: IDBTransaction | null) => void): Promise; /** * Decrements the reference count and closes the database when no references remain. * * Refcount contract: call once per *successful* {@link openDatabase}, never per * rejection. A rejected open already cleared its own bookkeeping, so a call * paired with it decrements whatever entry a successor installed under the same * name. {@link createManagedConnection} honours this — it only closes while it * holds a promise, and every rejection path drops that promise first. * * @param dbName The name of the database to close. * * @returns True if the database was closed, false otherwise. */ declare function closeDatabase(dbName: string): Promise; /** A disposed {@link ManagedConnection} was read from. Reopening instead would * take a refcount nothing will ever release, so this is loud rather than * silently leaky: the fix belongs at the caller, which is using a repository it * already tore down. */ declare class ConnectionDisposedError extends Error { readonly dbName: string; readonly name = "ConnectionDisposedError"; constructor(dbName: string); } /** Lazily-opened, self-healing handle on one IndexedDB database. */ interface ManagedConnection extends AsyncDisposable { /** * The open connection, opening it on first call. * * Call this once per operation and use the handle for the transaction you * start right away — never store it on the repository, and never carry it * across an `await`. The connection can be closed underneath you at any * time (a `versionchange` from another tab, eviction, "clear site data"), * after which every transaction on that handle throws `InvalidStateError`; * a later `get()` transparently reopens, but only for callers that ask * again. Holding the resolved database is exactly the defect this type * exists to remove. * * ## The dispose boundary * * `get()` is valid only before `[Symbol.asyncDispose]`. Afterwards it * rejects with {@link ConnectionDisposedError}, permanently: dispose has * released this connection's single reference, and reopening would take a * refcount nothing is left to release. Disposal is final by design — there * is no revive; construct a new repository, which constructs a new * connection, instead. * * Dispose neither awaits nor cancels work already in flight. A transaction * started just before it can abort when the underlying handle closes, and * an in-flight `get()` may resolve to a database that is closing. So * sequence disposal after the operations you care about have settled — * `await` the repository's outstanding calls first — rather than racing it * against them. * * Callers must not close the database themselves; the reference this * connection holds is released by its own dispose, once. */ get(): Promise; } /** * Forget-and-reopen around {@link openDatabase}, shared by every IndexedDB * repository. Caching the resolved `IDBDatabase` is the defect this exists to * remove: once the manager closes on `versionchange`, every later transaction on * that handle throws `InvalidStateError` with no path back. * * Opens nothing until the first {@link ManagedConnection.get} — repositories * construct their storage in their own constructors, and an eager open would * move the "IndexedDB is not available in this environment" throw to * construction time. */ declare function createManagedConnection(dbName: string, version: number, initDatabase: (db: IDBDatabase, oldVersion: number, transaction: IDBTransaction | null) => void): ManagedConnection; /** Resolve with an {@link IDBRequest}'s result, or reject with its error. */ declare const promisifyRequest: (request: IDBRequest) => Promise; /** * Resolve once a transaction commits, or reject if it errors or aborts. * * A write is durable at *commit*, not at request success — quota pressure and * storage eviction abort a transaction whose every request already succeeded. * Reads may resolve on {@link promisifyRequest}; writes must await this. */ declare const awaitTransaction: (transaction: IDBTransaction) => Promise; /** * Queue a delete for every row an index matches. Fire-and-forget by design: * the requests are queued as the cursor walks and the caller learns they * committed by awaiting the transaction, not this. A failing cursor or delete * aborts the transaction, which {@link awaitTransaction} reports. */ declare const deleteByIndex: (store: IDBObjectStore, indexName: string, value: IDBValidKey) => void; /** Read every row matching any of `values` on an index, flattened. */ declare const getAllByIndexValues: (store: IDBObjectStore, indexName: string, values: readonly IDBValidKey[]) => Promise; declare const MESSAGE_BUS_NOT_INITIALIZED = "MessageBus not initialized"; /** * Message for the "an init is in flight" variant. * It is a superset of MESSAGE_BUS_NOT_INITIALIZED so the existing consumers * keeps classifying it as "not initialized". */ declare const MESSAGE_BUS_INITIALIZING = "MessageBus not initialized: initializing"; declare class MessageBusNotInitializedError extends Error { constructor(message?: string); } declare class MessageBusInitializingError extends MessageBusNotInitializedError { constructor(); } declare class ServiceWorkerTimeoutError extends Error { constructor(detail: string); } declare function isMessageBusNotInitializedError(error: unknown): boolean; declare function isMessageBusInitializingError(error: unknown): boolean; declare class ReadonlyAssetManager implements IReadonlyAssetManager { readonly indexer: IndexerProvider; constructor(indexer: IndexerProvider); getAssetDetails(assetId: string): Promise; } declare class AssetManager extends ReadonlyAssetManager implements IAssetManager { readonly wallet: Wallet; constructor(wallet: Wallet); /** * Issue a new asset. * @param params - Parameters for asset issuance * @param params.amount - Amount of asset units to issue * @param params.controlAssetId - Optional control asset ID (for reissuable assets) * @param params.metadata - Optional metadata to attach to the asset * @returns Promise resolving to the Arkade transaction ID and asset ID * * @example * ```typescript * // Issue a simple non-reissuable asset * const result = await wallet.assetManager.issue({ amount: 1000 }); * console.log('Asset ID:', result.assetId); * * // Issue a reissuable asset with an existing control asset * const result = await wallet.assetManager.issue({ * amount: 1000, * controlAssetId: 'existingControlAssetId' * }); * console.log('Asset ID:', result.assetId); * ``` */ issue(params: IssuanceParams): Promise; /** * Reissue more units of an existing asset. * Requires ownership of the control asset. * * @param params - Parameters for asset reissuance * @param params.assetId - The asset ID to reissue (control asset ID is resolved via getAssetDetails) * @param params.amount - Amount of additional units to issue * @returns Promise resolving to the Arkade transaction ID * * @example * ```typescript * const txid = await wallet.assetManager.reissue({ * assetId: 'def456...', * amount: 500 * }); * ``` */ reissue(params: ReissuanceParams): Promise; /** * Burn assets. * @param params - Parameters for burning * @param params.assetId - The asset ID to burn * @param params.amount - Amount of units to burn * @returns Promise resolving to the Arkade transaction ID * * @example * ```typescript * const txid = await wallet.assetManager.burn({ * assetId: 'abc123...', * amount: 100 * }); * ``` */ burn(params: BurnParams): Promise; } export { ARKADE_MAGIC, ARKADE_SALTED_PREIMAGE_TAG, ARKADE_SWAP_PREIMAGE_TAG, ASSET_CARRIER_SATS, Activity, ActivityRegistry, AddressAllocationCapable, AmbiguousRouteError, type AnchorBumper, ArkAddress, ArkInfo, ArkIntent, ArkNote, ArkProvider, type ArkPsbtFieldCoder, ArkPsbtFieldKey, ArkPsbtFieldKeyType, ArkTransaction, type ArkTxInput, type ArkadeBatchInput, ArkadeCash, ArkadeContractParams, type ArkadeExtendedCoin, type ArkadeExtendedVirtualCoin, Asset, AssetDetails, AssetManager, BIP21, BIP21Error, type BIP21Params, type BIP21ParseResult, BIP322, BLOCKED_UPGRADE_TIMEOUT_MS, Batch, BatchExpiryPolicy, BurnParams, CSVMultisigTapscript, CachingArkProvider, ChainTx, Coin, ConditionWitness, ConnectionDisposedError, type ConnectorTreeNode, Contract, ContractEvent, ContractRepository, ContractRepositoryImpl, ContractSyncState, ContractWatcherConfig, ContractWithVtxos, CosignerPublicKey, CreateContractParams, DEFAULT_MESSAGE_TIMEOUTS, DatabaseUpgradeBlockedError, DefaultVtxo, DelegateInfo, DelegateNotConfiguredError, DelegatorNotConfiguredError, DescriptorIdentity, type DescriptorIdentityOptions, type DescriptorOptions, type DescriptorSigner, DescriptorSigningProviderMissingError, type DeterministicSigner, DustChangeError, ELECTRUM_TCP_HOST, ELECTRUM_WS_URL, type BlockHeader as ElectrumBlockHeader, ElectrumOnchainProvider, type TransactionHistory as ElectrumTransactionHistory, type Unspent as ElectrumUnspent, type EmulatorEntry, type EmulatorInfo, EmulatorPacket, type EmulatorProvider, EncodedVtxoScript, Estimator, type ExcludableVtxo, type ExecutorEvent, type ExecutorOptions, type ExitDelay, type ExitFeeWallet, type ExitMode, type ExitOptions, type ExitPackage, type ExitQuote, type ExitStep, type ExitTotals, type ExitVtxoInfo, ExplorerTransaction, ExtendedCoin, ExtendedVirtualCoin, Extension, ExtensionNotFoundError, type ExtensionPacket, FeeAmount, FeeInfo, FeeOutput, FetchError, GetNewAddressesOptions, GetSpendableVtxosFilter, GetVtxosFilter, HDAllocationCapable, HDDescriptorProvider, type HDDeterministicSignCapable, HDWalletCapable, IAssetManager, IContractManager, IDelegateManager, IReadonlyAssetManager, IReadonlyWallet, IVtxoManager, IWallet, Identity, InMemoryContractRepository, InMemoryIntentRepository, InMemoryVirtualTxRepository, InMemoryWalletRepository, IndexedDBContractRepository, IndexedDBIntentRepository, IndexedDBVirtualTxRepository, IndexedDBWalletRepository, IndexerProvider, Intent, IntentFeeConfig, IntentFilter, IntentRepository, IssuanceParams, IssuanceResult, MESSAGE_BUS_INITIALIZING, MESSAGE_BUS_NOT_INITIALIZED, MIGRATION_KEY, type ManagedConnection, MessageBus, MessageBusInitializingError, MessageBusNotInitializedError, type MessageHandler, type MessageTimeouts, MigrationGlobalSkipReason, MigrationLegSkipReason, type MigrationStatus, MissingSigningDescriptorError, MnemonicIdentity, type MnemonicOptions, Network, NetworkName, type NetworkOptions, NewAddress, NormalizedExtendedVirtualCoin, OffchainInput, type OffchainTx, type OnExitObserved, OnchainInput, OnchainProvider, OnchainWallet, Outpoint, OversizedChangeError, P2A, type ParsedArkContract, PathContext, PathSelection, type PaymentHandle, type PaymentOption, type PaymentRail, type PaymentRequest, PaymentRouter, type PaymentStatus, PrevArkTxField, PrevoutTxField, ProviderConnectionState, type ProvisionedClaimSecret, type ProvisionedKey, READ_TIMEOUT_MS, Ramps, ReadonlyAssetManager, ReadonlyDescriptorIdentity, ReadonlyIdentity, ReadonlySingleKey, ReadonlyWallet, ReadonlyWalletError, Recipient, ReissuanceParams, RelativeTimelock, RenewVtxosOptions, type RequestEnvelope, type ResponseEnvelope, RestEmulatorProvider, type RouteQuote, type RouteResult, type RouterContext, type RouterPreferences, SeedIdentity, type SeedIdentityOptions, SendBitcoinParams, SendParams, ServiceWorkerReadonlyWallet, ServiceWorkerTimeoutError, ServiceWorkerWallet, type ServiceWorkerWalletMode, SettleParams, SettlementConfig, SettlementEvent, SignedIntent, SignerSession, SignerStatus, SingleKey, Status, StorageConfig, TapLeafScript, Transaction, TreeNonces, TreePartialSigs, TxNotificationEvent, TxTree, TxWeightEstimator, UnannotatableInputError, UnilateralExit, UnknownPacket, Unroll, type VSize, type VerifyServerSignatures, VirtualCoin, VirtualTx, VirtualTxRepository, VtxoBranch, type VtxoExclusion, VtxoScript, VtxoTaprootTree, VtxoTreeExpiry, Wallet, WalletBalance, WalletCannotSignError, WalletMessageHandler, WalletNotInitializedError, WalletRepository, WalletRepositoryImpl, type WalletRestoreHook, WatchedScript, WsElectrumChainSource, adoptContractDescriptor, arkAssetRail, arkRail, arkTarget, index as arkade, assembleBtcdTaprootTree, assertFinalCommitmentMatchesValidated, assertNoAssets, assertNoSelection, assertSendableAmount, assertSubmittedArkTxid, index$1 as asset, assetsOf, awaitTransaction, btcTarget, buildForfeitTx, buildOffchainTx, buildPreimageMessage, buildSaltedPreimageMessage, buildVersion, claimWithPreimageIdentity, closeDatabase, combineTapscriptSigs, contractFromArkContract, contractFromArkContractWithAddress, contractPreimage, contractSigner, createAssetPacket, createDefaultPaymentRouter, createManagedConnection, decodeArkContract, deleteByIndex, deriveDescriptorLeafCompressedPubKey, deriveDescriptorLeafPubKey, deserializeExitPackage, encodeArkContract, exitObserverFor, gateExclusion, gatedContracts, getAllByIndexValues, getArkPsbtFields, getMigrationStatus, hasBoardingTxExpired, identityDescriptor, invoiceTarget, isArkContract, isBtcAddress, isCltvSatisfied, isContractGenericallySpendable, isCsvSpendable, isDeterministicSigner, isHDDeterministicSignCapable, isLightningInvoice, isLnurl, isMessageBusInitializingError, isMessageBusNotInitializedError, isPerArtifactDescriptor, isRetryableProviderError, isValidArkAddress, logExcludedVtxos, makeHandle, matchServerCheckpoints, migrateWalletRepository, normalizeToDescriptor, notifyExitObserved, onchainRail, openDatabase, outpointExclusion, outpointReasons, parseHDDescriptor, promisifyRequest, provisionClaimSecret, provisionRefundKey, registerWalletRestoreHook, requiresMigration, resolveAssetAmount, resolveSendAmount, rollbackMigration, sdkVersion, selectCoinsWithAsset, selectionOf, sequenceToTimelock, serializeExitPackage, setArkPsbtField, setupServiceWorker, signAndSubmitOffchainTx, timelockToSequence, toXOnly, tryResolveSendAmount, validateBatchRecipients, validateConnectorsTxGraph, validateVtxoTxGraph, verifyTapscriptSignatures, walletFeeSource };