A manual BTC → LEZ swap through the two Basecamp desks did not complete. The Maker’s LEZ lock was rejected by the sequencer because the escrow program was not deployed on the chain the stack was running; the Maker waited out its 30-minute lock window without reporting the rejection, and the Taker was left to refund its Bitcoin.

The same chain had also stopped finalizing 74 minutes before the swap was taken, which by deploy/README.md stalls every swap on its own. Neither condition was visible on the desks or in the Nodes’ health: both reported ready throughout, and the Taker was allowed to lock Bitcoin.

Candidate: Gateway v0.2.5, c99ff8ce86a7c5d2ad343d62232c843f627c9d0c, run from the release bundle (lez-swap-stack-v0.2.5-arm64, images pulled by tag, escrow ImageID c22d61fc…).

Swap: c13c2d3ece99f449b055cdea2746daa9f49bc343de1cea55275f2a865b8e1392, offer offer-sell-lez-1790910281, direction TakerSellsForeign, 0.54250000 BTC for 31543 LEZ units.

Run date: 2026-10-02. All times below are UTC.

1. Setup

The bundle ran on the reviewer’s machine (arm64, Docker) with Bitcoin Regtest and a local LEZ devnet on the default local timing profile (Maker lock cutoff 1800 s, one Bitcoin block per 120 s). runtime/ had been removed and recreated that morning while market/ was kept, as deploy/README.md:26 instructs (“--wipe removes chains and Node state (keep market/, it holds the funded wallets)”). The exact commands were not recorded; the state on disk matches down.sh --wipe followed by start.sh. The sequencer started from genesis at 01:45:00 (Database not found … starting from genesis), so every swap that day ran on a new LEZ chain.

The swap was driven by hand through the Maker and Taker desks, not by node-e2e.py. The twelve driver runs of 2026-10-01 (rfp-003-btc-test-case-run-20261001.md) ran on the previous chain, where the program was deployed, and are not affected.

2. What happened

Time Where Event
01:45:00 sequencer New chain from genesis.
01:46:04–01:46:47 sequencer Four two-transaction blocks, consistent with the four vault claims of the market bootstrap. No other non-empty block before the swap: nothing was deployed.
01:50:22 indexer Last finalized block indexed (31). The finalized height does not move again on this chain.
03:04:55 both desks Offer taken; swap accepted. Maker lock cutoff 03:34:54.
03:05:00–03:05:19 Taker Locks 0.5425 BTC (32d2c7bd…:1), then waits for the Maker’s lock.
03:05:09 Maker Sees the Bitcoin lock confirmed, submits lez.initialize (1d1b06e9…). The bridge sidecar records the submission as accepted.
03:05:20 sequencer Drops the transaction while building block 466 (below).
03:05:20–03:34:54 Maker Desk reads “Funding the LEZ escrow”. lez.fund (d1d503e9…) is never sent.
03:34:55 Taker awaiting_maker_lock → refund_ready.
03:34:59 Maker funding_lez → recovering, “Lock window missed”.
03:57:49 Taker The reviewer requests the refund the desk had offered since 03:34:55: the swap had made no progress in the 52 minutes since the Bitcoin lock.

The sequencer’s only error of the session:

[2026-10-02T03:05:20Z ERROR sequencer_core] Transaction with hash
1d1b06e938bfa3e8e8af536fccbf023fa999b882460c42c14aaa0868d990d04a failed
execution check with error: InvalidInput("Unknown program"), skipping it

The transaction is addressed to the escrow program c22d61fc…. The deployment that market/bootstrap/deployment.json records for it, 5d9b9f8f…, is unknown to the running sequencer (getTransaction returns null) and does not appear in its log. The record is dated 2026-10-01 04:50 and belongs to the chain of that day’s test-case run.

Independently of that, the devnet had frozen. The indexer indexed its last block, 31, at 01:50:22, five minutes after the chain started; at 05:19 its finalized height was still 31 with the sequencer at block 1097. This is the upstream freeze deploy/README.md:94-104 describes (“the indexer’s finalized height stands still and every swap waits”). The rejection is what stopped this swap, since the Maker’s lock never entered a block; with the program deployed the swap would have met the frozen finality instead. It was found only after the first recovery attempt (section 4).

3. Findings

3.1 The market bootstrap skips the escrow deployment on a recreated chain

deploy/scripts/market-bootstrap.sh:54-57 decides whether the escrow program is deployed by reading its own evidence file and comparing .preflight.image_id with the pinned program. It does not ask the chain. A deployment.json left by an earlier chain therefore reads as “escrow program already deployed”, and the bootstrap goes on to hand both Nodes a manifest (market-bootstrap.env) that names a deployment transaction the chain has never seen.

The comment above that check says evidence from “a chain recreated since” does not count, but nothing in the check can tell. The genesis block hash in the evidence cannot either: a recreated local chain has the same genesis hash (block 1 is adbd31a0… on both days), and the sidecar reported itself bound to it (stable_finalized_tip_bound_to_runtime_genesis).

The record is retired only by up.sh --fresh and up.sh --fresh-lez (deploy/scripts/up.sh:40-45, :99-100), and only while runtime/runtime.env still exists. down.sh --wipe removes runtime/ and leaves market/; start.sh, the bundle’s documented entry point, has no fresh option and calls up.sh without one. So the documented sequence — wipe, keep market/, start again — produces a stack that starts cleanly, reports both Nodes ready, publishes and takes offers, and cannot lock LEZ, so no swap on it can complete.

The vault-claim step of the same script does this correctly: it reads the vault balance from the chain to decide whether a claim already ran (market-bootstrap.sh:75-78).