Handler for the boarding contract (registered type boarding).
The boarding contract derives the on-chain Bitcoin address used to
board funds onto Arkade. It shares the exact DefaultVtxo.Script
shape with the default contract — a Taproot output co-owned by the
wallet and the Ark server, with a CSV exit path back to the wallet —
and therefore reuses the default handler's path logic (forfeit via
server cooperation, exit after the boarding CSV).
Boarding semantics come entirely from the contract type and from
sourcing the CSV timelock from the server's boarding-exit delay
(ArkInfo.boardingExitDelay), not from renamed parameters. The
offchain default contract is built from ArkInfo.unilateralExitDelay
instead, so the two share a script shape but differ in their CSV
timelock value. Parameters round-trip through the same
timelockToSequence / sequenceToTimelock helpers and BIP68 sequence
encoding as default / delegate.
Like default / delegate, the boarding handler implements
Discoverable.discoverAt so wallet.restore() can rediscover
used boarding indices from authoritative on-chain data. It differs from
the L2 handlers in its source of truth: boarding probes the on-chain
UTXO set at its P2TR address (OnchainProvider.getCoins) rather than the
Ark indexer, and builds its candidate from the boarding-exit CSV
(deps.boardingTimelock). When boarding discovery is not plumbed (no
deps.boardingTimelock / deps.onchainNetwork) discoverAt no-ops.
Identity & the default/boarding collision: a contract's script (pkScript)
is its unique identity — a script owns exactly one repository row. boarding
is a first-class type with its own row when its script is distinct from
the wallet's default baseline — the real-world case, since a sound Ark
server keeps boardingExitDelay strictly longer than unilateralExitDelay (equal
delays would expose the provider to a double-spend). Should those delays ever
coincide (a misconfigured/malicious server), the boarding script is
byte-identical to the default script. discoverAt does NOT pre-coalesce that
collision: it always emits type: "boarding", and the single shared row is
resolved FIRST-WINS at the persistence layer
(ContractManager.upsertContract) — whichever purpose is persisted
first keeps the row, and the scan deliberately probes boarding first
(see docs/hd-wallets_onchain_rotation_collision_fix.md §5.1–5.2). Either way
the funds are equally spendable through the shared DefaultVtxo.Script paths.
Consumers must NOT rely on contract.type === "boarding" to identify the
boarding purpose — resolve the boarding script via
wallet.getBoardingAddress() / wallet.boardingTapscript (which never depend
on the persisted contract's type) and match by script when needed.
Handler for the boarding contract (registered type
boarding).The boarding contract derives the on-chain Bitcoin address used to board funds onto Arkade. It shares the exact
DefaultVtxo.Scriptshape with thedefaultcontract — a Taproot output co-owned by the wallet and the Ark server, with a CSV exit path back to the wallet — and therefore reuses thedefaulthandler's path logic (forfeit via server cooperation, exit after the boarding CSV).Boarding semantics come entirely from the contract type and from sourcing the CSV timelock from the server's boarding-exit delay (
ArkInfo.boardingExitDelay), not from renamed parameters. The offchaindefaultcontract is built fromArkInfo.unilateralExitDelayinstead, so the two share a script shape but differ in their CSV timelock value. Parameters round-trip through the sametimelockToSequence/sequenceToTimelockhelpers and BIP68 sequence encoding asdefault/delegate.Like
default/delegate, the boarding handler implements Discoverable.discoverAt sowallet.restore()can rediscover used boarding indices from authoritative on-chain data. It differs from the L2 handlers in its source of truth: boarding probes the on-chain UTXO set at its P2TR address (OnchainProvider.getCoins) rather than the Ark indexer, and builds its candidate from the boarding-exit CSV (deps.boardingTimelock). When boarding discovery is not plumbed (nodeps.boardingTimelock/deps.onchainNetwork)discoverAtno-ops.Identity & the default/boarding collision: a contract's
script(pkScript) is its unique identity — a script owns exactly one repository row.boardingis a first-class type with its own row when its script is distinct from the wallet'sdefaultbaseline — the real-world case, since a sound Ark server keeps boardingExitDelay strictly longer than unilateralExitDelay (equal delays would expose the provider to a double-spend). Should those delays ever coincide (a misconfigured/malicious server), the boarding script is byte-identical to the default script.discoverAtdoes NOT pre-coalesce that collision: it always emitstype: "boarding", and the single shared row is resolved FIRST-WINS at the persistence layer (ContractManager.upsertContract) — whichever purpose is persisted first keeps the row, and the scan deliberately probes boarding first (see docs/hd-wallets_onchain_rotation_collision_fix.md §5.1–5.2). Either way the funds are equally spendable through the sharedDefaultVtxo.Scriptpaths. Consumers must NOT rely oncontract.type === "boarding"to identify the boarding purpose — resolve the boarding script viawallet.getBoardingAddress()/wallet.boardingTapscript(which never depend on the persisted contract's type) and match by script when needed.