DevNet bootstrap¶
This page documents the DevNet-only bootstrap flow: how the
registry-init, stake-reward-init, governance-withdrawal-init, and
disburse sub-transactions are produced through the shipped
wizard → tx-build → witness → submit chain. It is operator
reference material for driving a fresh DevNet, and the live-DevNet
evidence captured along the way. For mainnet operation see the
Quickstart and the per-action recipe pages.
Bootstrap via tx-build --intent¶
After #157
the amaru-treasury-tx devnet <action> supercommand is retired.
The seven bootstrap sub-transactions previously orchestrated in
process by devnet {registry-init,stake-reward-init,
governance-withdrawal-init,disburse-submit} are now reachable
through the same wizard → tx-build → witness → submit chain used
by swap, withdraw, and disburse. Each multi-tx flow is split
into per-sub-step intents at the JSON layer so each invocation of
tx-build produces one unsigned tx:
registry-init-seed-split # 3 txs for the registry-init flow
registry-init-mint
registry-init-reference-scripts
stake-reward-init-script-account # 2 txs for the stake-reward flow
stake-reward-init-plain-account
governance-withdrawal-init-proposal # 2 txs for the gov flow
governance-withdrawal-init-materialization
The operator path per sub-step is:
amaru-treasury-tx tx-build \
--intent <bootstrap-intent.json> \
--out tx.cbor.hex
amaru-treasury-tx witness --tx tx.cbor.hex …
amaru-treasury-tx submit --tx tx.cbor.hex …
bootstrap-intent.json carries the same envelope as the existing
intent.json (action, schemaVersion, network, payload); the
action discriminator names one of the seven flat tags above. The
JSON schema is in docs/assets/intent-schema.json; per-sub-action
payload contracts are documented under
specs/157-flatten-devnet-cli/contracts/. The dispatcher rejects any
network other than devnet for these seven actions, before any
N2C connection (slice 4 of #157).
The wizards that produce bootstrap-intent.json files for the
registry-init flow ship as three subcommands of
amaru-treasury-tx registry-init-wizard, one per sub-action shipped
by #157. On a fresh DevNet, pass --bootstrap so the wizard does not
look for registry anchors that do not exist yet. After the three
submitted tx ids are known, run write-artifacts to materialize the
same registry-init/ handoff shape that the library smoke writes:
COMMON=(--network devnet --node-socket "$CARDANO_NODE_SOCKET_PATH")
RUN_DIR=runs/devnet/$(date -u +%Y%m%dT%H%M%SZ)
# Step 1: seed-split (no inter-tx inputs).
amaru-treasury-tx "${COMMON[@]}" registry-init-wizard seed-split \
--bootstrap \
--wallet-addr <devnet-bech32> \
--metadata <metadata.json> \
--scope <scope-id> \
--out seed-split.intent.json
amaru-treasury-tx "${COMMON[@]}" tx-build --intent seed-split.intent.json --out seed-split.tx.cbor.hex
amaru-treasury-tx witness ...
amaru-treasury-tx submit ...
# Operator records seed-split's submitted txid as <seed-split-txid>.
# Step 2: mint (operator hand-carries seed TxIns + owner key hash).
amaru-treasury-tx "${COMMON[@]}" registry-init-wizard mint \
--bootstrap \
--wallet-addr <devnet-bech32> \
--metadata <metadata.json> \
--scope <scope-id> \
--scopes-seed-txin <seed-split-txid>#0 \
--registry-seed-txin <seed-split-txid>#1 \
--owner-key-hash <hex28> \
--out mint.intent.json
amaru-treasury-tx "${COMMON[@]}" tx-build --intent mint.intent.json --out mint.tx.cbor.hex
amaru-treasury-tx witness ...
amaru-treasury-tx submit ...
# Operator records the mint txid as <registry-mint-txid>.
# Step 3: reference-scripts (operator hand-carries seed TxIns + funding seed).
amaru-treasury-tx "${COMMON[@]}" registry-init-wizard reference-scripts \
--bootstrap \
--wallet-addr <devnet-bech32> \
--metadata <metadata.json> \
--scope <scope-id> \
--scopes-seed-txin <seed-split-txid>#0 \
--registry-seed-txin <seed-split-txid>#1 \
--funding-seed-txin <wallet-utxo>#N \
--out reference-scripts.intent.json
amaru-treasury-tx "${COMMON[@]}" tx-build --intent reference-scripts.intent.json --out reference-scripts.tx.cbor.hex
amaru-treasury-tx witness ...
amaru-treasury-tx submit ...
# Operator records the submitted txid as <reference-scripts-txid>.
# Step 4: write the registry-init handoff artifacts from real tx ids.
amaru-treasury-tx "${COMMON[@]}" registry-init-wizard write-artifacts \
--run-dir "$RUN_DIR" \
--seed-split-txid <seed-split-txid> \
--registry-mint-txid <registry-mint-txid> \
--reference-scripts-txid <reference-scripts-txid> \
--scopes-seed-txin <seed-split-txid>#0 \
--registry-seed-txin <seed-split-txid>#1 \
--owner-key-hash <hex28>
Explicit inter-tx unsafe — the wizard does not simulate cross-step
tx bodies. The operator types every value that crosses a sub-step
boundary (submitted tx ids, seed TxIns, owner key hash, funding seed
UTxO). Mistyping any of them — swapping #0 and #1, pasting a stale
txid, or using the wrong key hash — yields invalid intents that fail at
tx-build (best case), at on-chain validation (worst case), or in
write-artifacts validation for malformed values. This is deliberate;
the resumable client state that supersedes this manual carry is parked in
#163.
The shipped CLI smoke in
#161
must hand-carry and persist: run directory, DevNet magic 42,
seed-split-txid, registry-mint-txid, reference-scripts-txid,
<seed-split-txid>#0, <seed-split-txid>#1, the owner key hash, and
the funding seed UTxO used by reference-scripts. The artifact writer
uses the first seven values; the funding seed remains live-chain
submission evidence for the reference-scripts transaction.
The wizards that produce bootstrap-intent.json files for the
stake-reward-init flow ship in
#159
as two independent subcommands of
amaru-treasury-tx stake-reward-init-wizard, one per sub-action
shipped by #157. Both subcommands consume the registry.json
artifact produced by registry-init-wizard reference-scripts plus
an operator-typed funding seed:
# Pre-requisite: complete the registry-init bootstrap above; you
# now have registry.json on disk.
# Step 1: script-account (registers treasury reward account).
amaru-treasury-tx stake-reward-init-wizard script-account \
--wallet-addr <devnet-bech32> \
--registry <path/registry.json> \
--funding-seed-txin <wallet-utxo-txid>#<ix> \
--out script-account.intent.json
amaru-treasury-tx tx-build --intent script-account.intent.json --out script-account.tx.cbor.hex
amaru-treasury-tx witness ...
amaru-treasury-tx submit ...
# Step 2: plain-account (registers permissions reward account;
# independent of step 1 — order does not matter).
amaru-treasury-tx stake-reward-init-wizard plain-account \
--wallet-addr <devnet-bech32> \
--registry <path/registry.json> \
--funding-seed-txin <wallet-utxo-txid>#<ix> \
--out plain-account.intent.json
amaru-treasury-tx tx-build --intent plain-account.intent.json --out plain-account.tx.cbor.hex
amaru-treasury-tx witness ...
amaru-treasury-tx submit ...
The two stake-reward sub-actions are independent at the wizard
layer — neither feeds into the other; the operator may invoke them
in either order. Common mistakes the wizard cannot detect: pointing
--registry at a stale or never-submitted bootstrap; reusing a
spent --funding-seed-txin; picking a registry from a different
devnet boot. These yield invalid intents that fail at tx-build
(best case) or at on-chain validation (worst case). The same
resumable client state parked in #163 will supersede this manual
carry.
The wizard that produces bootstrap-intent.json files for the
governance-withdrawal-init flow ships in
#160
as two independent subcommands of
amaru-treasury-tx governance-withdrawal-init-wizard. Both
subcommands consume the registry.json artifact produced by
#158,
the accounts.json artifact produced by
#159,
and an operator-typed funding seed:
# Pre-requisite: complete registry-init and stake-reward-init; you now
# have registry.json and accounts.json on disk. Your signing inventory
# contains the funding payment key, funding stake key, and voter/DRep key.
# Step 1: proposal (requests the treasury withdrawal, registers the
# funding stake credential, and self-votes through the DevNet DRep key).
amaru-treasury-tx governance-withdrawal-init-wizard proposal \
--wallet-addr <devnet-bech32> \
--registry <path/registry.json> \
--stake-reward-accounts <path/accounts.json> \
--funding-seed-txin <wallet-utxo-txid>#<ix> \
--funding-stake-key-hash <56-hex-char-funding-stake-vkey-hash> \
--voter-key-hash <56-hex-char-voter-vkey-hash> \
--withdrawal-amount-lovelace <N> \
--anchor-url <https://...> \
--anchor-hash <64-hex-char-anchor-content-hash> \
--validity-hours <hours> \
--log proposal.wizard.log \
--force \
--out proposal.intent.json
amaru-treasury-tx tx-build --intent proposal.intent.json --out proposal.tx.cbor.hex
amaru-treasury-tx witness ... # funding payment
amaru-treasury-tx witness ... # funding stake (= --funding-stake-key-hash)
amaru-treasury-tx witness ... # voter/DRep (= --voter-key-hash)
amaru-treasury-tx attach-witness ...
amaru-treasury-tx submit ...
# Wait for proposal enactment and observe the treasury reward balance.
# Step 2: materialization (withdraws the observed rewards into the
# treasury contract address).
amaru-treasury-tx governance-withdrawal-init-wizard materialization \
--wallet-addr <devnet-bech32> \
--registry <path/registry.json> \
--stake-reward-accounts <path/accounts.json> \
--funding-seed-txin <wallet-utxo-txid>#<ix> \
--rewards-lovelace <observed-balance> \
--validity-hours <hours> \
--log materialization.wizard.log \
--force \
--out materialization.intent.json
amaru-treasury-tx tx-build --intent materialization.intent.json --out materialization.tx.cbor.hex
amaru-treasury-tx witness ... # funding payment
amaru-treasury-tx attach-witness ...
amaru-treasury-tx submit ...
The proposal transaction requires three key witnesses: funding payment,
funding stake (--funding-stake-key-hash), and voter/DRep
(--voter-key-hash). The materialization transaction requires one key
witness: funding payment. The materialization transaction also uses
treasury and registry reference scripts from registry.json; those are
script witnesses, not operator key witnesses.
The DRep behavior is DevNet-specific. The proposal transaction bundles
submit-and-self-vote because the closed DevNet bootstrap has no
third-party DRep to wait for. On mainnet and preprod that bundling is
not protocol-required, and the wizard refuses those networks. For this
DevNet command the operator is the DRep voting on their own withdrawal
proposal, so their vault must contain the key whose hash equals
--voter-key-hash.
The proposal resolver checks wallet shortfall before writing an intent:
the wallet must cover govActionDeposit + stakeDeposit + drepDeposit
plus the estimated fee. The 100k ADA governance-action deposit is a
mainnet orientation point; DevNet values come from that network's
protocol parameters. Materialization uses the smaller materialization
floor and does not include governance deposits.
Explicit inter-tx unsafe — the wizard does not simulate cross-step
tx bodies. The operator hand-carries the registry path, accounts path,
funding seed UTxO, declared key hashes, anchor URL/hash pair, observed
post-enactment reward balance, and the real-world ordering that
proposal enactment must precede materialization. Key-hash typos are not
checked against vault identities by the wizard; they fail later at
witness time. The resumable client state that supersedes this manual
carry is parked in
#163.
The bash CLI smoke introduced by #161 chains the full bootstrap + disburse flow against a real DevNet through the shipped CLI:
nix develop --quiet -c just devnet-cli-smoke --phase full --timeout-seconds 900
Its final summary.json links registry/stake, governance, and disburse
phase summaries, the host chain-assertion transcripts, the run
directory, socket path, and every submitted tx id.
The end-to-end DevNet runners under
lib/Amaru/Treasury/Devnet/{RegistryInit,StakeRewardInit,
GovernanceWithdrawalInit,DisburseSubmit}.hs are no longer exposed
as a shipped CLI surface. They remain on disk as library functions
consumed by Amaru.Treasury.Devnet.SmokeSpec (via
Amaru.Treasury.Devnet.Runner), which keeps the library-level
DevNet proof gating CI through nix build .#checks.*.
Recent live-DevNet evidence (pre-#157 in-process runners)¶
The following runs were produced before the supercommand was
retired; the same CBOR is now built byte-for-byte from
tx-build --intent (proved by goldens
test/golden/{RegistryInit,StakeRewardInit,GovernanceWithdrawalInit}IntentSpec.hs).
- Registry init (
runs/devnet/20260516T193404Z): seed split tx82b1f12f0ceeae86c50753a61528599c4d7b8ccef769a56accd3011c0e24084d, registry mint tx1f427e73979ee6150e69944fb384cbe0809148e64307a2a75221bacea8cb4ff9, reference-script tx5c3227fe8511632669b5383246e7ff92ccc2add2988ee90ac1a24ecda6a10a44, scopes anchor1f427e73979ee6150e69944fb384cbe0809148e64307a2a75221bacea8cb4ff9#0, registry anchor1f427e73979ee6150e69944fb384cbe0809148e64307a2a75221bacea8cb4ff9#1, permissions reference5c3227fe8511632669b5383246e7ff92ccc2add2988ee90ac1a24ecda6a10a44#0, treasury reference5c3227fe8511632669b5383246e7ff92ccc2add2988ee90ac1a24ecda6a10a44#1. - Stake-reward init (
runs/devnet/20260517T005034Z): setup tx527c1b08fdfd1c41fe1237d4d5f1bc3572dee98a81b76b62257c958e50dc9cc8, treasury reward accountb2b7201c62e43ae8e03b61c96931379ebbcdce61befc3f4e4b1f4be4, permissions reward accountf9dc1d931a3f52eaf83891f8621cbba5ba64f6faa5792f1b00c17333, both registered onTestnet. - Governance / withdrawal init
(
runs/devnet/20260516T231003Z): proposal txbaffa774b368b1da8c3ff80be399bcf6fa63b5cff658b6889fc00109da218e23, governance actionbaffa774b368b1da8c3ff80be399bcf6fa63b5cff658b6889fc00109da218e23#0, vote tx009801303fc5cc3c3dfe474c30cc4b7d31e99b5af29467cc317072ea6b728c45, treasury reward accountb2b7201c62e43ae8e03b61c96931379ebbcdce61befc3f4e4b1f4be4, reward0 → 2000000 → 0lovelace, withdrawal tx4a87409b52b8104d51d41df7ee562196cf33621f64c4c40985b4aef5ff21e9bd, fee456417lovelace, materialized output4a87409b52b8104d51d41df7ee562196cf33621f64c4c40985b4aef5ff21e9bd#0, treasury ADA200000000 → 202000000. - Disburse submit (
runs/devnet/20260517T005034Z): submitted tx0008ab902b2f835624f453af0467d826b02519d7139ec8e84a04c8a9c000011b, beneficiary output0008ab902b2f835624f453af0467d826b02519d7139ec8e84a04c8a9c000011b#1with1000000lovelace, treasury input309e28ed5b95de38258bcc130d6390800b0719f6410b0d5fe6f3c33cc1b70817#0, treasury lovelace2000000 → 1000000.
The opt-in live-node smoke phases remain runnable as the library proof
layer
(just devnet-smoke {registry-init,stake-reward-init,
governance-withdrawal-init,disburse-submit} — these still drive the
library functions via SmokeSpec).
Run the swap readiness boundary check with:
nix develop --quiet -c just devnet-smoke swap-ready
The swap readiness phase uses the checked-in public
SundaeSwap-finance/sundae-contracts@be33466b7dbe0f8e6c0e0f46ff23737897f45835
order.spend artifact, publishes it as a local DevNet reference
script, and writes swap-ready/registry.json for the later #84
order-build slice. Latest local evidence for this branch is
runs/devnet/20260515T124545Z: script hash
02eee6c4d128c9700c178922163645f1fdb381bbdce071acbbd49465,
reference UTxO
490b9bc8a80e8a55434b895bea6ca47fc612105c0cf71b781a61e99cd2be46af#0,
and local order address
addr_test1xqpwaeky6y5vjuqvz7yjy93kghclmvuph0wwqudvh02fgegzamnvf5fge9cqc9ufygtrv303lkecrw7uupc6ew75j3jsdhyjpu.
This is readiness evidence only; it does not build, fund, submit, or
spend a swap order.
The DevNet release experiment is tracked in slices. The current bootstrap initiator recovery is orchestrated by #151: registry/reference-script publication #147, staking and reward-account setup #148, governance funding and treasury withdrawal setup #149, and disburse action/beneficiary receipt #150. Older evidence slices remain tracked as governance action #82, withdrawal #83, disburse #86, SundaeSwap V3 contract readiness #132, SundaeSwap V3 order build/funding #84, and SundaeSwap V3 order spend #85, then reorganize #87.