Building a disburse transaction¶
disburse-wizard resolves treasury state into a unified disburse
intent.json, and tx-build turns that intent into unsigned Conway
CBOR. The wizard supports both ADA and USDM; when --unit is omitted,
it defaults to USDM.
Wizard pipeline¶
export CARDANO_NODE_SOCKET_PATH=/path/to/cardano-node.socket
amaru-treasury-tx \
--node-socket "$CARDANO_NODE_SOCKET_PATH" --network mainnet \
disburse-wizard \
--wallet-addr addr1q... \
--metadata metadata-mainnet.json \
--scope network_compliance \
--beneficiary-addr addr1qvendor... \
--amount 100000000 \
--validity-hours 6 \
--description "Settle March vendor invoice" \
--justification "Approved network-compliance budget line" \
--destination-label "Vendor Ltd." \
--log disburse-wizard.log \
| amaru-treasury-tx \
--node-socket "$CARDANO_NODE_SOCKET_PATH" \
tx-build \
--log disburse-build.log \
--out disburse.cbor.hex
The example above pays 100000000 smallest USDM units, or 100 USDM.
To pay ADA instead, add --unit ada and pass lovelace in --amount:
amaru-treasury-tx ... disburse-wizard \
--unit ada \
--amount 50000000 \
...
What the wizard resolves¶
The wizard verifies the local metadata.json hint against the
on-chain registry, then resolves:
- the selected scope's treasury address, script hash, owner keyhashes, deployed scripts, and permissions reward account;
- wallet UTxOs for fuel and collateral;
- treasury UTxOs for the selected unit;
- current tip and validity upper bound;
- USDM policy and asset name constants.
For USDM, treasury UTxOs are selected largest-first by USDM quantity until both the requested USDM amount and the beneficiary ADA deposit are covered. The beneficiary output receives the requested USDM plus the required lovelace. The treasury leftover output receives leftover lovelace, leftover USDM, and any other non-USDM assets preserved from the selected treasury inputs.
Contingency disburses¶
Use contingency-disburse-wizard when ADA must move from the contingency
treasury to another scope treasury. This is intentionally narrower
than disburse-wizard:
- the source is always
contingency; - the unit is always ADA;
- the destination is selected by scope, not by manually pasting an address;
- the destination scope must be one of:
core_development,ops_and_use_cases,network_compliance, ormiddleware.
The contingency treasury has no owner key of its own. The command
therefore emits all four owned scope owners as required signers:
core_developmentops_and_use_casesnetwork_compliancemiddleware
For example, to top up Network Compliance with 200,000 ADA:
amaru-treasury-tx \
--node-socket "$CARDANO_NODE_SOCKET_PATH" --network mainnet \
contingency-disburse-wizard \
--wallet-addr addr1q... \
--metadata metadata-mainnet.json \
--destination-scope network_compliance \
--ada 200000 \
--description "Contingency disburse for Network Compliance" \
--justification "Treasury reallocation approved by scope owners" \
| amaru-treasury-tx \
--node-socket "$CARDANO_NODE_SOCKET_PATH" --network mainnet \
tx-build
--ada accepts an ADA decimal and converts it to lovelace in the
emitted intent. The command still emits the unified disburse intent
shape consumed by tx-build, but the public CLI surface enforces the
contingency disburse policy above.
Existing intent¶
If an intent has already been reviewed, build it directly:
amaru-treasury-tx \
--node-socket "$CARDANO_NODE_SOCKET_PATH" \
tx-build \
--intent disburse.intent.json \
--out disburse.cbor.hex \
--log disburse.log
The intent's top-level network field is the source of truth.
tx-build probes the socket against that network before querying
UTxOs or balancing.
Payload shape¶
The shipped disburse branch supports ADA and USDM disburse intents:
{
"schema": 1,
"action": "disburse",
"network": "mainnet",
"disburse": {
"unit": "usdm",
"amount": 100000000,
"beneficiaryAddress": "addr1...",
"usdmPolicy": "c48cbb3d...",
"usdmToken": "0014df105553444d"
}
}
The full intent also carries the shared wallet, scope,
signers, validityUpperBoundSlot, and rationale blocks
described by docs/assets/intent-schema.json.
Validation¶
The build path queries a live ChainContext, builds the transaction,
aligns the fee with the bash/cardano-cli oracle behaviour, and
re-runs the evaluator against the final body. A successful log ends
with:
tx-build: re-evaluated 2 redeemers, 0 failed
tx-build: cbor -> disburse.cbor.hex
tx-build: VALIDATION OK
Golden and regression evidence¶
test/fixtures/disburse/ada/ pins the ADA disburse
bash/cardano-cli oracle:
body.cboris the expected body hex;bash.oracle.tx.jsonis the original cardano-cli JSON wrapper;pparams.json,utxos.json, andexunits.jsonfreeze the chain context used to rebuild it offline.
The golden suite asserts both body.cbor ==
bash.oracle.tx.json.cborHex and runFromIntent against the
frozen fixture rebuilds that same oracle byte-for-byte.
USDM coverage is structural: unit tests assert intent translation, beneficiary and treasury leftover values, treasury UTxO selection until the beneficiary ADA deposit is covered, and beneficiary network mismatch rejection.