Skip to main content

Monolithic Workflow

This tutorial will demonstrate a monolithic workflow in Marlowe. Monolithic refers to all the inputs passed to marlowe-cli transformed to a JSON file then parsed as a transaction. cardano-cli will be used to query the state of the transaction.

There will cover these main topics:

  • Getting and passing all the inputs needed for marlowe-cli contract marlowe
  • Parsing the Marlowe JSON file to create a transaction with marlowe-cli transaction
  • Obtaining and retuning testnet ADA from the faucet


An installation of the following tools is required:

  • marlowe-cli
  • cardano-cli
  • jq

Start a Cardano Node with Docker

Open a terminal and start a Cardano node in a container with the node-ipc volume:

docker run -e NETWORK=preprod -v node-ipc:/ipc inputoutput/cardano-node

Development Environment

If the tools above are not yet installed, open a new terminal. Start a nix shell from the Marlowe starter kit:

nix develop github:input-output-hk/marlowe-starter-kit

Set the node socket and testnet

Using the node-ipc volume name, inspect the node socket and set the path to CARDANO_NODE_SOCKET_PATH.

Then allow read and write from that path.

export CARDANO_NODE_SOCKET_PATH=$(docker volume inspect node-ipc | jq -r '.[0].Mountpoint')/node.socket

while [[ $f != / ]]; do sudo chmod a+rx "$f"; f=$(dirname "$f"); done
sudo chmod a+rwx $CARDANO_NODE_SOCKET_PATH

Assuming the preprod testnet is used, set the environment variable:


This enviroment variable will be used across marlowe-cli and cardano-cli for building transactions.



if [[ ! -e "$PAYMENT_SKEY" ]]
cardano-cli address key-gen \
--signing-key-file "$PAYMENT_SKEY" \
--verification-key-file "$PAYMENT_VKEY"
PAYMENT_ADDR=$(cardano-cli address build --testnet-magic "$CARDANO_TESTNET_MAGIC" --payment-verification-key-file "$PAYMENT_VKEY" )

echo "$PAYMENT_ADDR" payment.address

Go to the testnet faucet, select the preprod network and paste the payment address. It may take a few minutes for the transaction to complete. Check the status using the cardano-cli.

cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address $PAYMENT_ADDR

It is also possible the node is not fully synced. Check the status of the node with cardano-cli query tip --testnet-magic $CARDANO_TESTNET_MAGIC.

Create a contract

echo \"close\" >> monolithic.contract

cat << EOI > $STATE_FILE
"choices": [],
"accounts": [
"address": "$PAYMENT_ADDR"
"currency_symbol": "",
"token_name": ""
"minTime": 10,
"boundValues": []
marlowe-cli contract marlowe --testnet-magic $CARDANO_TESTNET_MAGIC  \
--contract-file $CONTRACT_FILE \
--state-file $STATE_FILE \
--out-file $MARLOWE_FILE \
Bare-validator cost: ExBudget {exBudgetCPU = ExCPU 18745100, exBudgetMemory = ExMemory 81600}
Validator size: 12296
Datum size: 85
Redeemer size: 3
Total size: 12384

Preparing a transaction

Check the Marlowe JSON file has the expected output. These will be passed into marlowe-cli once again.

jq 'to_entries[] | .key' $MARLOWE_FILE

If set up correctly, there should be three expected fields.


Validator Plutus File

Create a monolithic.plutus file.

jq '.validator.script' $MARLOWE_FILE > $PLUTUS_FILE

Validator Address

Create a script address.

ADDRESS_S=$(jq -r '.validator.address' $MARLOWE_FILE)

Datum Hash

DATUM_HASH=$(jq -r '.datum.hash' $MARLOWE_FILE)


Create a monolithic.datum file.

jq '.datum.json' $MARLOWE_FILE > $DATUM_FILE


jq '.redeemer.json' $MARLOWE_FILE > $REDEEMER_FILE

Funds to send

Check the address has been funded with ADA from the previous step.

cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address $PAYMENT_ADDR

If succcessful, there will be a TxHash for the address.

                           TxHash                                 TxIx        Amount
4ea15f2c853535cc7fa16f9b9b4184d02bac75812a5448ec62be848ef3519572 0 10000000000 lovelace + TxOutDatumNone

Set TX_0 to the TxHash of this transaction appended by # along with the TxIx.

marlowe-cli transaction create --testnet-magic $CARDANO_TESTNET_MAGIC    \
--socket-path "$CARDANO_NODE_SOCKET_PATH" \
--script-address "$ADDRESS_S" \
--tx-out-datum-file $DATUM_FILE \
--tx-out-marlowe $DATUM_LOVELACE \
--tx-in "$TX_0" \
--change-address "$PAYMENT_ADDR" \
--out-file tx.raw \
--required-signer $PAYMENT_SKEY \
--print-stats \

Once the transaction is successfully created, an associated TxId` will be available.

Fee: Lovelace 176721
Size: 291 / 16384 = 1%
Execution units:
Memory: 0 / 14000000 = 0%
Steps: 0 / 10000000000 = 0%
TxId "57d01eaf51bc49ce58961e4353016ac2c9467075b783c12cef6f973ef82835cf"

Set TX_1 to be the TxId of the previous output.


Verify the contract address now contains the 3 ADA.

cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address "$ADDRESS_S" | sed -n -e "1p; 2p; /$TX_1/p"

The output should be similar to below. Observe that TxOutDatumHash is identical to $DATUM_HASH from the contract inputs earlier.

                           TxHash                                 TxIx        Amount
57d01eaf51bc49ce58961e4353016ac2c9467075b783c12cef6f973ef82835cf 1 3000000 lovelace + TxOutDatumHash ScriptDataInBabbageEra "960c278f6524c93de651f314304ccc6c8d30bf34c986834cc27e4bdff59527d0"

Close the contract

Before closing the contract, a redeem slot range must be provided.

$ cardano-cli query tip --testnet-magic $CARDANO_TESTNET_MAGIC

"block": 1042141,
"epoch": 75,
"era": "Babbage",
"hash": "19a58b0e3832e2857c1eeab01d7ab87f8cf98903ef29249148574df2817062d1",
"slot": 30864376,
"syncProgress": "100.00"

Choose a valid slot range so that the transaction can close in the next step. Depending on the time of execution and testnet environment, the values may appear different.

$ marlowe-cli transaction close --testnet-magic $CARDANO_TESTNET_MAGIC  \
--socket-path "$CARDANO_NODE_SOCKET_PATH" \
--tx-in-script-file $PLUTUS_FILE \
--tx-in-redeemer-file $REDEEMER_FILE \
--tx-in-datum-file $DATUM_FILE \
--tx-in-marlowe "$TX_1#1" \
--tx-in "$TX_1#0" \
--tx-in-collateral "$TX_1#0" \
--change-address "$PAYMENT_ADDR" \
--invalid-before $REDEEM_MIN_SLOT \
--invalid-hereafter $REDEEM_MAX_SLOT \
--out-file tx.raw \
--required-signer $PAYMENT_SKEY \
--print-stats \

Fee: Lovelace 947046
Size: 12738 / 16384 = 77%
Execution units:
Memory: 2818820 / 14000000 = 20%
Steps: 738349482 / 10000000000 = 7%
TxId "931d9088986a8cac42210631936348b78bac7a19ab24bcdf63bf0e6a4edaccde"

Checking the address again, there will be a new transaction with the 3 ADA.

cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address $PAYMENT_ADDR

TxHash TxIx Amount
931d9088986a8cac42210631936348b78bac7a19ab24bcdf63bf0e6a4edaccde 0 9995876233 lovelace + TxOutDatumNone
931d9088986a8cac42210631936348b78bac7a19ab24bcdf63bf0e6a4edaccde 1 3000000 lovelace + TxOutDatumNone


It is considered a good practice to return test tokens to maintain the health of the testnet.

Querying the state of the address that received the testnet ADA, obtain the TxHash. In this example, it is a2c9c87c65c1283da11240a35159d581149d4f214cb1b190262b95b77a0bc98a.

cardano-cli query utxo --testnet-magic $CARDANO_TESTNET_MAGIC --address $PAYMENT_ADDR
TxHash TxIx Amount
931d9088986a8cac42210631936348b78bac7a19ab24bcdf63bf0e6a4edaccde 1 3000000 lovelace + TxOutDatumNone
a2c9c87c65c1283da11240a35159d581149d4f214cb1b190262b95b77a0bc98a 0 9995705936 lovelace + TxOutDatumNone

Build a transaction with the testnet address to return the ADA in --tx-out. The output, represented by +0 will be updated after calculating the --fee. To obtain a slot for --invalid-hereafter, run cardano-cli query tip --testnet-magic $CARDANO_TESTNET_MAGIC for a valid slot.

cardano-cli transaction build-raw --tx-in a2c9c87c65c1283da11240a35159d581149d4f214cb1b190262b95b77a0bc98a#0 \
--tx-out addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3+0 \
--invalid-hereafter 30960000 \
--fee 0 \
--out-file tx.draft

Next, obtain the protocol parameters.

cardano-cli query protocol-parameters --testnet-magic $CARDANO_TESTNET_MAGIC --out-file protocol.json

Then calculate the fee. Subtract the free from the amount above 9995705936 - 170297.

cardano-cli transaction calculate-min-fee --tx-body-file tx.draft --tx-in-count 1 --tx-out-count 1 --witness-count 1 --testnet-magic $CARDANO_TESTNET_MAGIC --protocol-params-file protocol.json 
170297 Lovelace

Replace the +0 with the calulated amount and set the fee accordingly. It is important that the fee and output is equal to the inputs otherwise the transaction will fail.

cardano-cli transaction build-raw --tx-in a2c9c87c65c1283da11240a35159d581149d4f214cb1b190262b95b77a0bc98a#0 \
--tx-out addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3+9995534759 \
--invalid-hereafter 30960000 \
--fee 171177 \
--out-file tx.draft

Finally sign and submit the transaction.

cardano-cli transaction sign  --tx-body-file tx.draft --signing-key-file payment.skey --testnet-magic $CARDANO_TESTNET_MAGIC --out-file tx.signed
cardano-cli transaction submit --tx-file tx.signed --testnet-magic $CARDANO_TESTNET_MAGIC
Transaction successfully submitted.