ReadonlyactivityActivity resolvers consumed by getActivityHistory.
ReadonlyarkRe-widened to public: a full wallet's provider is part of its API
(ExpoWallet and the delegate manager read it), while ReadonlyWallet
keeps it protected so a readonly view cannot hand out submitTx.
ReadonlycontractOptional ReadonlydelegateReadonlydustOptionalexitOpt-in exit-data capture settings; see StorageConfig.exitDataCapture.
ReadonlyforfeitReadonlyforfeitReadonlyidentitySigning identity associated with the wallet.
A real signer, not a ReadonlyIdentity that structurally fits: contract
corridors need all four members — sign, signMessage, signerSession
and xOnlyPublicKey — and signerSession is the one a watch-only
identity lacks. isSigningIdentity is the check; a wallet that fails it
is refused as WalletCannotSignError before anything is funded, rather
than at the push that discovers there is no signer.
ReadonlyindexerOptionalintentOpt-in intent-lifecycle repository. Assigned by the create()
factories from config.storage.intentRepository; undefined ⇒ all
intent-persistence code paths are no-ops (default behaviour unchanged).
ReadonlynetworkReadonlyonchainReadonlysettlementOptionalvirtualExperimental / inert. Opt-in virtual-tx / exit-branch repository,
exposed so callers can pass it to Unroll.Session.create as a
best-effort raw-tx cache. Assigned by create() from
config.storage.virtualTxRepository. Normal sync never writes it
(ContractManager isn't given it); undefined ⇒ no-op.
ReadonlywalletReadonlywalletStaticMIN_The wallet's current active server signer (x-only, 32 bytes). Read-only from the outside; mutated only via Wallet.setArkServerPublicKeyForRotation during mid-session server-signer rotation (plan §4). Single-valued for wallets that never span a rotation.
Asset manager bound to this wallet instance.
The wallet's current boarding tapscript (the on-chain onboarding
target). Read-only from the outside; mutated only via
Wallet.setBoardingTapscriptForRotation when a fresh boarding
address is explicitly allocated. Single-valued for static / auto
wallets.
Get the pkScript hex for the wallet's primary offchain address. For the full wallet-owned script set registered in ContractManager, use getWalletScripts().
Currently-active receive tapscript. Read-only from the outside; mutated only via Wallet.setOffchainTapscriptForRotation by WalletReceiveRotator.rotate.
Async-dispose hook that forwards to dispose().
Build an offchain transaction from the given inputs and outputs, sign it, submit to the Arkade provider, and finalize.
Signs whatever it is handed, ungated — see settle for the trade.
The Arkade transaction id and server-signed checkpoint PSBTs (for bookkeeping)
Claim an ArkadeCash bearer instrument: sweep what can be swept, report the rest.
Every spendable VTXO at the arkadeCash address is swept to this wallet in
its own offchain transaction, signed with the key carried by the arkadeCash
string. Nothing is ever persisted: no contract is imported, and the
arkadeCash key is not stored. Anything that cannot be swept — server-swept,
subdust, already claimed, or asset-bearing — is returned in unclaimed
with a per-VTXO reason and otherwise ignored.
The arkadeCash string is the recovery token: a claim interrupted between
submit and finalize is completed by simply re-running claimCash, which
drains any pending arkadeCash transaction on the server before sweeping.
The encoded arkadeCash string (e.g., "arkadecash1...")
The swept total and the report of what was left behind. The
shape is open: further buckets may be added alongside unclaimed.
Wipe all locally persisted wallet data (VTXOs, UTXOs, history, sync cursor, contracts). Create a fresh wallet instance afterward.
Clear the global VTXO sync cursor, forcing a full re-bootstrap on next sync. Useful for recovery after indexer reprocessing or debugging.
Create a batch event handler for settlement flows.
The intent ID.
Inputs used by the intent.
Expected recipients to validate in the virtual output tree.
Optionalsession: SignerSessionOptional musig2 signing session. When omitted, signing steps are skipped.
Create an ArkadeCash bearer instrument.
Generates a fresh keypair, sends the specified amount to a DefaultVtxo
controlled by the new key, and returns the encoded arkadeCash string.
The receiver can claim the funds using claimCash() without ever
sharing their address.
ArkadeCash is a short-lived instrument: it carries a private key, so it is
meant to be handed over and claimed promptly, not held. A note left
unclaimed past its batch expiry is swept by the server and claimCash
can only report it, not move it.
Amount in satoshis to send. Must be a whole number of sats at or above the dust threshold — a below-dust amount would mint an OP_RETURN output that is unspendable as cash.
The encoded arkadeCash string (e.g., "arkadecash1...")
Dispose wallet-owned managers and release background resources.
Fetch Arkade transaction ids that are still pending final settlement.
Finalizes pending transactions by retrieving them from the server and finalizing each one. Skips the server check entirely when no send was interrupted (no pending tx flag set).
Optionalvtxos: ExtendedVirtualCoin[]Optional list of virtual outputs to use instead of retrieving them from the server
Array of transaction IDs that were finalized
Wallet history grouped by registered activity resolvers. With no resolver match, rows bucket by transaction key so send/change pairs stay together.
Returns the wallet's Arkade address.
Broadcast access bound to this wallet's server, so a plugin needs only the wallet.
Defined here and not on ReadonlyWallet for the same reason
arkProvider is protected there: a readonly wallet, and every
toReadonly() view, must not be able to submit.
Server info for the Arkade server this wallet is connected to, resolved exactly as construction resolves it: live wins, a retryable failure falls back to the snapshot persisted at boot, a terminal one propagates.
Live rather than the pinned boot snapshot because the fields callers
come here for — signerPubkey, checkpointTapscript, fees — are the
ones a mid-session rotation moves, and a covenant built against a
superseded signer is unspendable.
One rotation caveat: reading does NOT re-pin the wallet —
arkServerPublicKey, dustAmount and the tapscripts move
only through handleServerInfoChanged/rotateServerSigner — so inside
a rotation window this can report epoch N+1 while the wallet still
spends on N. The window closes on its own: a read that observes a moved
digest makes the provider emit onServerInfoChanged, which is what
drives that rotation. A caller about to bind the answer into a covenant
passes { requireLive: true } and fails closed instead of receiving
the boot snapshot.
Optionalopts: GetArkadeInfoOptionsThe Arkade server's info
Chain reads against this wallet's server for scripts it does not own.
Binds the wallet's own indexerProvider — which may be an Expo or
injected one that a caller's hand-built RestIndexerProvider would
silently bypass. getVtxos goes through
getNormalizedVtxos, which is what makes every VTXO leaving the
seam carry its canonical facts.
A bound object rather than the provider itself, so an IReadonlyWallet
holder gets getVtxos/getVirtualTxs and nothing else at runtime.
(indexerProvider is still public on the concrete classes, unlike
arkProvider, so this narrows the interface rather than the field.)
Return the wallet's combined onchain and offchain balances.
Returns the onchain boarding address used to move funds into Arkade.
The on-chain (P2TR) addresses of every boarding tapscript this wallet uses — the current address plus any historical rotated boarding addresses. The aggregating boarding readers (history, notifications) fan out over this set so deposits at previous boarding addresses are still surfaced (plan §6-IV); getBoardingAddress stays single-valued.
Build a transaction history view across the wallet's boarding addresses (current + historical rotated; plan §6-IV.1).
Fetch and cache onchain inputs (UTXOs) received at the wallet's boarding addresses — the current address plus any historical rotated boarding addresses that still hold unspent UTXOs (plan §6-III.1). Each UTXO is annotated with the tapscript of the address it actually sits on, so the spending path forfeits / exits it with the correct per-index leaves.
Current-signer only: a flatten of getBoardingUtxosForSigners over
the wallet's current signer, so the two paths cannot drift. Old-signer
boarding recovery goes through the deprecated-signer migration API
instead (it would otherwise pull EXPIRED-signer inputs into a plain
settle() that the server must reject).
Fetch and cache onchain inputs (UTXOs) received at the boarding addresses
of the given signer set, grouped per boarding address so the caller keeps
the address↔signer association that ExtendedCoin cannot carry
(it retains only the encoded leaves/tapTree the spend needs, not the
DefaultVtxo.Script and its serverPubKey/CSV delay).
Per group it does exactly what getBoardingUtxos does per tapscript:
getCoins → extendCoinWithTapscript → saveUtxos. Offline-first:
it does not call getInfo(); the caller supplies the allowed signer set,
so the only network calls are the per-address getCoins.
x-only-hex server keys whose boarding addresses to fetch (passed through to getBoardingTapscripts).
Get the ContractManager for managing contracts including the wallet's default address.
The ContractManager handles:
const manager = await wallet.getContractManager();
// Create a contract for a Boltz swap
const contract = await manager.createContract({
label: "Boltz Swap",
type: "vhtlc",
params: { ... },
script: swapScript,
address: swapAddress,
});
// Start watching for events (includes wallet's default address)
const stop = await manager.onContractEvent((event) => {
console.log(`${event.type} on ${event.contractScript}`);
});
The contract-manager's current provider-sync health without forcing it
to initialize — reads the already-constructed manager, or reports
online when none exists yet. Unlike getContractManager, this
never triggers a remote sync, so it is safe on a pure diagnostics path
(e.g. the service-worker sync-state message).
Returns the delegate manager when delegation support is configured.
Allocate a fresh address of each requested type, all derived from one newly allocated HD index.
This is the explicit allocator the receive path otherwise lacks:
getAddress and getBoardingAddress are stable reads of the
wallet's display addresses, and the display receive address only advances
when a payment arrives (WalletReceiveRotator rotates on
vtxo_received). Issuing a second address before the first is paid — one
invoice for Alice, another for Bob — has to go through here.
Each call:
default + boarding pair are siblings rather
than two burnt indices;delegate shape);active contract carrying its signingDescriptor,
so the ContractWatcher monitors it, the balance counts it, and
signerForDescriptor can recover the key;The minted rows are deliberately left untagged: the boot lookups adopt the newest WALLET_RECEIVE_SOURCE-tagged row as the display address, and a side address issued to one counterparty must not become the address the wallet advertises to everyone else. getAddress and getBoardingAddress are unchanged by this call.
A wallet with no HD stream (walletMode: 'static' / 'auto') has one
address per flavour for its lifetime. Without forceNew it returns those
— the real persisted rows, no index burned; with forceNew it throws
WalletCannotAllocateAddressError rather than hand back an address
that is not in fact fresh.
Optionalopts: GetNewAddressesOptionsAllocate and return a fresh on-chain boarding address, rotating the wallet's current boarding tapscript to a new HD index.
This is the explicit boarding allocator — the analogue of dotnet's
GetNextContract(NextContractPurpose.Boarding). Unlike
getBoardingAddress (a stable read of the current display
address that never burns an index), each call here:
active boarding contract tagged
WALLET_RECEIVE_SOURCE (with its signingDescriptor) so the
ContractWatcher monitors it, boot can restore it as the current
boarding address, and descriptor-aware signing can recover the
per-index key;boardingTapscript.Gated by walletMode: a static / auto wallet has no descriptor
provider and keeps a single index-0 boarding address for its lifetime,
so this returns the existing getBoardingAddress unchanged
(no rotation, no index burned).
Behaviour change. A wallet configured with a custom
DescriptorProvider (walletMode: <provider>) now rotates here.
It previously did not: allocation went through the contract manager,
which only wires an allocate hook for HDDescriptorProvider, so
such a wallet read as "declined to allocate" and silently kept its
index-0 boarding address forever. Those wallets now burn an index per
call — including via maybeRotateBoardingAfterBoard, which fires
on every settle that consumes a boarding UTXO. Funds at retired
addresses stay reachable: each rotation persists its boarding contract
before swapping, and getBoardingUtxos fans out over the full
historical set.
Use getNewAddresses — it mints any combination of
address types at one shared index, and reports the contract row behind
each. Note the difference in display behaviour: this method swaps the
advertised boarding address, where getNewAddresses mints side
addresses and leaves getBoardingAddress alone. To keep this
method's behaviour, keep calling this method.
HDAllocationCapable.getNextSigningDescriptor
Every Wallet answers — this is where the wallet's key-provisioning
policy lives, so consumers never have to branch on wallet shape:
auto: the identity key as a bare tr(pubkey) descriptor —
the same answer every call, because that IS the static policy.Composed provider-connection freshness: the LATEST server-info
resolution (boot, or any later getArkadeInfo read) combined with
the contract-manager's indexer-sync health, if the manager has been
initialized. Reads no live provider state — it never forces a
ContractManager to construct — so it is safe for readonly callers
that only use address/balance APIs.
arkade (cache).indexer (repository).This only describes sync freshness; wallet balances/VTXOs are always read from the repository regardless of this state.
Build a map of scriptHex → VtxoScript for all wallet contracts, so virtual outputs can be extended with the correct tapscript per contract.
The subset of getVtxos that generic spending may select: the same
filter, minus contracts the generic-spending gate closes, minus funds
awaiting recovery under a past-cutoff signer, minus outpoints locked by an
in-flight intent. Every implicit coin selection in the SDK reads this;
getVtxos stays the raw reporting/recovery read.
Both exclusion sets are derived from one contract snapshot, so they cannot disagree about which VTXOs exist.
Optionalfilter: GetVtxosFilterSame flags, same defaults, as getVtxos
Return wallet transaction history derived from Arkade state and boarding transactions.
Optionalopts: { lookAhead?: number }HDWalletCapable.getUsedSigningDescriptors
Union of the watermark band and the descriptors persisted on contracts: the band alone would miss rows a restore scan wrote, and the contracts alone would miss indices allocated for something the wallet never persisted (a swap, an externally issued invoice).
Return virtual outputs tracked by the wallet.
The raw reporting/recovery read: escrowed, locked and awaiting-recovery
funds are all present. Coin selection must use
getSpendableVtxos instead — feeding this straight into
settle({ inputs }) or send({ selectedVtxos }) bypasses the
generic-spending gate.
Optionalfilter: GetVtxosFilterOptional flags controlling whether recoverable or unrolled VTXOs are included
Get all pkScript hex strings for the wallet's own addresses (both delegate and non-delegate, current and historical).
Debug-log any explicit input generic selection would have excluded, for
each of the three reasons it excludes on. Explicit-input APIs stay ungated
on purpose (naming an outpoint is the intent the gate protects, and it
is how an escrowed deposit is recovered once generic selection stops
covering it), so this only makes the crossing visible — notably
settle({ inputs: await wallet.getVtxos() }), which launders the raw read
into a spend. Never throws into a spend path.
Public so the worker handler and plugins can report their own explicit-input crossings through the same three checks.
OptionalvalidAt: numberSubscribe to onchain and offchain notifications for newly received funds.
The onchain watcher tracks the full boarding-address set (current +
historical rotated). When boarding rotates after subscribing — e.g.
rotate-on-board allocates a fresh address via
getNewBoardingAddress — the watcher automatically re-subscribes
to widen its set, so a deposit to the new address fires a notification
within the same session (no watcher re-init required). The re-subscribe
is driven by onBoardingRotation; static / auto / readonly
wallets never rotate boarding, so it never fires for them.
Callback invoked when matching funds are detected
A function that stops the subscriptions
Outpoints of VTXOs whose deprecated signer is past its cutoff (EXPIRED) and
which have not yet been swept — unspendable until they recover. Offline:
classifies the repo's contracts against the cached signer set (active +
_deprecatedSigners, cutoffs included). Empty fast-path when no
signer is deprecated. Consumed by getBalance (the pendingRecovery
bucket) and by getSpendableVtxos so neither counts nor spends them.
Takes a fresh contractSnapshot, which syncs against the indexer. Callers that already hold a snapshot — or that must not sync at all, like the worker's balance read — pass it to pendingRecoveryOutpointsIn instead.
pendingRecoveryOutpoints over a snapshot the caller already has: pure classification against the cached signer set, no repository or network read of its own.
Refresh the cached deprecated-signer set from a fresh server-info
snapshot. Called by the create() factories at construction, by the
server-info-change handler mid-session, and by the deprecated-signer
migration pass (VtxoManager.migrateDeprecatedSignerVtxos). This set feeds
pendingRecoveryOutpoints, which drops EXPIRED (past-cutoff)
deprecated-signer VTXOs from the wallet's own coin selection. Lenient: a
malformed deprecated entry is skipped, never fatal to wallet creation.
Explicitly recover this wallet's contracts and balance on a fresh repo. HD wallets run a gap-limit scan across the index range; static / non-HD wallets restore based on the single default pubkey. Never throws because of identity/mode (a static identity is a valid, narrower restore); throws on operational failure (so a truncated restore is loud, not silent — the gap window may have closed early). Idempotent and safe to call concurrently (calls coalesce into one scan).
Ordering is deliberate (spec §3.B / §4): scan → advance the HD watermark → inline VTXO pull → only THEN surface aggregated handler errors, so safely-discovered funds are always recovered even when one discovery handler failed.
Optionalopts: { gapLimit?: number }OptionalgapLimit?: numberConsecutive-unused-index window. Default 20. A non-positive / non-integer value is a programmer error and throws synchronously (distinct from operational failure).
InternalMid-session server-signer rotation (plan §4). When arkd rotates its active signer mid-session — the case the long-lived service worker and Expo background processes that own automatic migration must handle — a wallet constructed before the rotation keeps deriving old-signer receive addresses. Building a migration output to such an address would produce a VTXO the server must reject, so the wallet must first re-derive its own receive state under the new active signer.
Follows the WalletReceiveRotator.rotate write-path pattern with
the server key swapped instead of the user key: build the new offchain
and boarding tapscripts locally (preserving every other option),
register the matching default/delegate and boarding contract rows
through ContractManager.createContract, and only then commit the
new tapscripts and server key to the wallet's visible state. The signing
metadata of the current receive/boarding rows is carried onto the new
rows so a rotated (descriptor-backed) receive pubkey can still sign.
The old-signer contract rows are intentionally left active and watched
— they are exactly the deprecated-signer contracts the migration pass
drains. Idempotent: a no-op when the wallet already tracks xonly.
Serialized against HD receive rotation so the two paths (both of which
rebuild and swap offchainTapscript) cannot interleave.
Invoked by the VtxoManager migration pass; not part of the stable public API.
Send BTC and/or assets to one or more recipients, passed either as
variadic Recipients or as a single SendParams object — the latter
also carries the virtual outputs to spend.
Recipients, or a SendParams object
Promise resolving to the Arkade transaction ID
const txid = await wallet.send({
address: 'ark1q...',
amount: 1000, // (optional, default to dust) btc amount to send to the output
assets: [{ assetId: 'abc123...', amount: 50n }] // (optional) list of assets to send
});
// choosing the inputs as well as the outputs
const txid = await wallet.send({
recipients: [{ address: 'ark1q...', amount: 1000 }],
selectedVtxos: mine, // spent as given, nothing added
});
InternalMigration primitive (deprecated-signer plan, step 1). Spend an
explicit set of the wallet's own deprecated-signer VTXOs into a single
full-value output on the wallet's active signer, through the Ark send
path (not settle) so arkd builds checkpoints against the active server
epoch. Consumed in-process by VtxoManager's migration pass; not
part of the public IWallet API and never accepts boarding ExtendedCoin
inputs.
The caller (migrateCore) must have already moved the wallet onto the
active signer (ensureReceiveOnActiveSigner) and sized the batch (caps +
dust floor); this method validates the inputs, preserves all input assets
on the self output, and persists the new active-signer VTXO even though
there is no separate change output. It records no TxSent history — the
funds never leave the wallet.
Optionalnow: TimeHeightInternalSole write path for arkServerPublicKey after construction.
Called by Wallet.rotateServerSigner once the rotated offchain and
boarding contract rows have been persisted. External code must treat
arkServerPublicKey as read-only.
InternalSole write path for boardingTapscript after construction.
Called by Wallet.getNewBoardingAddress once the rotated
boarding contract has been persisted. External code must treat
boardingTapscript as read-only.
InternalSole write path for offchainTapscript after construction.
Called by WalletReceiveRotator.rotate once the rotated
display contract has been persisted. External code must treat
offchainTapscript as read-only.
InternalSole write path for serverUnrollScript after construction.
Called by Wallet._doRotateServerSigner with the checkpoint script
sourced from the fresh ArkadeInfo that triggered the rotation, so the send
path builds checkpoints against the new server epoch. External code must
treat serverUnrollScript as read-only.
Settle boarding inputs and/or virtual outputs into a finalized mainnet transaction.
params.inputs is ungated: whatever the caller names is settled,
including VTXOs generic selection would skip. That is what makes an
escrowed or otherwise gated deposit recoverable by hand — but it also
means settle({ inputs: await wallet.getVtxos() }) silently bypasses the
gate. Pass getSpendableVtxos instead when the intent is "settle
whatever is spendable".
Optionalparams: SettleParamsOptional settlement inputs and outputs. When omitted, the wallet settles all eligible funds.
OptionaleventCallback: (event: SettlementEvent) => voidOptional callback invoked for settlement stream events.
The finalized Arkade transaction id
InternalAwait an onServerInfoChanged handler still applying a rotation.
The provider emits synchronously but handleServerInfoChanged runs
off the emit, so between the two a caller can observe a half-applied
rotation: rotateServerSigner persists the active signer's contract
rows before committing the tapscripts, so arkServerPublicKey still
reads the old key while the new key's rows are already in the repository.
A path that refreshes server info and then reads signer-derived state must
drain the chain first, or it both reads that torn state and races the
handler for the rotation itself.
Resolves once the chain is idle; a handler that threw already logged, and its rejection is swallowed here.
Invoked by dispose and the VtxoManager migration pass; not part of the stable public API.
HDWalletCapable.signerForDescriptor
Fail-loud contract: the returned identity is always the descriptor's own key. A descriptor this wallet cannot sign for throws ForeignDescriptorError instead of silently substituting the baseline identity — that identity would sign happily with the wrong key, and the failure would only surface as a rejected transaction or a dead script, far from the call that caused it.
Convert this wallet to a readonly wallet.
A readonly wallet with the same configuration but readonly identity
const wallet = await Wallet.create({ identity: MnemonicIdentity.fromMnemonic('abandon abandon...'), ... });
const readonlyWallet = await wallet.toReadonly();
// Can query balance and addresses
const balance = await readonlyWallet.getBalance();
const address = await readonlyWallet.getAddress();
// But cannot send transactions (type error)
// readonlyWallet.send(...); // TypeScript error
StaticcreateCreate a full wallet and initialize its background managers.
Wallet configuration
A wallet ready to query balances and send transactions
Main wallet implementation for Bitcoin transactions with Arkade protocol support. The wallet does not store any data locally and relies on Arkade and onchain providers to fetch onchain and virtual outputs.
Example