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.
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.
| 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).
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).