The UTXO model explained with examples

Since I have been mainly in the Ethereum world which uses account based model, I've forgotten what the UTXO model was, so I needed to do a bit of research myself to get back up to speed.

Since I have been mainly in the Ethereum world which uses account based model, I've forgotten what the UTXO model was, so I needed to do a bit of research myself to get back up to speed.

What is the UTXO model

Taken from the Wikipedia article:

In cryptocurrencies such as Bitcoin, an unspent transaction output (UTXO) is an abstraction of electronic money. Each UTXO is analogous to a coin, and holds a certain amount of value in its respective currency. Each UTXO represents a chain of ownership implemented as a chain of digital signatures where the owner signs a message (transaction) transferring ownership of their UTXO to the receiver's public key.
A UTXO defines an output of a blockchain transaction that has not been spent, i.e. can be used as an input in a new transaction. Bitcoin is an example of a cryptocurrency that uses the UTXO model.
Wikipedia Article Link

Clear?

The last paragraph really captures what it is, an output from a transaction that has not been spent.

Let's give some examples.

The Dollar example

Each and every UTXO is like a single fiat bill. You can't rip a $20 bill in half to give someone $10. So you always get some sort of change.

For our example,

  • Alice has a $10 note
  • Bob has $5 in his register and an everything bagle to sell

Alice would love to buy the bagle from Bob.  It costs $5 at Bob's Bagels, Alice gives Bob her $10 note [all UTXOs have to be fully spent].  Bob give Alice the bagel and $5 in change that she drops into her wallet.

  • Alice now has a $5 note [UTXO] in her wallet, and a yummy everything bagel to eat
  • Bob now has a $10 [UTXO] in the register

In another example, if Alice had ten $1 notes she would just combine five of them to pay Bob and receive no change back from Bob.

Cardano-cli Example

For giggles let's fire up the cardano-cli and spend some ADA.

Pre-work

Setup our environment on a Mac.

export CARDANO_NODE_SOCKET_PATH=~/Library/Application\ Support/Daedalus\ Testnet/cardano-node.socket
export TESTNET_ID=1097911063
alias cardano-cli="/Applications/Daedalus\ Testnet.app/Contents/MacOS/cardano-cli"

Hopefully you have Daedalus Testnet installed and funded with some test ADA.

If not here are some links.

Daedalus Testnet:
https://developers.cardano.org/en/testnets/cardano/get-started/wallet/

Test ADA faucet:
https://developers.cardano.org/en/testnets/cardano/tools/faucet/

Export Protocol Parameters

We also need to export the protocol parameters to a file for later use in determining the fee for our transactions.  We save this into a file called protocol.json

cardano-cli query protocol-parameters --testnet-magic $TESTNET_ID --mary-era --out-file protocol.json

OK, Now we will use Daedalus to fund our command line wallets.

  • Setup Alice Wallet

Generate our Alice Wallet stake keys

cardano-cli stake-address key-gen --verification-key-file alice-stake.vkey --signing-key-file alice-stake.skey

Generate our Alice Wallet payment keys

cardano-cli address key-gen --verification-key-file alice-payment.vkey --signing-key-file alice-payment.skey

Use payment and stake verification keys to generate a new payment address for Alice

cardano-cli address build --payment-verification-key-file alice-payment.vkey --stake-verification-key-file alice-stake.vkey --out-file alice-payment.addr --testnet-magic $TESTNET_ID

Open up alice-payment.addr and you will see a address to send to.

cat alice-payment.addr
addr_test1qzqlcxaurcqgkq9ca9qpnzcyenxhau3f4uw870g7lnjt6n7jt0f89d7xqutxh5j57c2yuznrru04qc6mnleyfxj8wnpss7rlfl
  • Setup Bob Wallet

Generate our Bob Wallet stake keys

cardano-cli stake-address key-gen --verification-key-file bob-stake.vkey --signing-key-file bob-stake.skey

Generate our Bob Wallet payment keys

cardano-cli address key-gen --verification-key-file bob-payment.vkey --signing-key-file bob-payment.skey

Use payment and stake verification keys to generate a new payment address for Bob

cardano-cli address build --payment-verification-key-file bob-payment.vkey --stake-verification-key-file bob-stake.vkey --out-file bob-payment.addr --testnet-magic $TESTNET_ID

Open up bob-payment.addr and you will see a address to send to.

cat bob-payment.addr
addr_test1qr99rzzmdlzfy8my0z4mazrljnjqn8zw69zqjmpfyk4c0p6neww6g6d9yr3735t3f6mzz850ucxqxy86ldywyx6ygryqtkvlwz
  • Send some test ADA to both Wallets

Open up Daedalus Testnet and send a few different transactions to both Alice and Bob's wallets.  I chose to send 5, 10, 50 ADA to each.  You could get away with just sending Alice 10 ADA for the example.

Check for UTXO's in the wallet

Alice Wallet

cardano-cli query utxo --address $(< alice-payment.addr) --testnet-magic $TESTNET_ID --mary-era

You should see something like the following:

                           TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
1d59d3e9bc5300df465191dce37f4985a67e2c9081d21d392ac92bfe5cf1098b     0        50000000 lovelace
be331f3164cb10af076035f1e5ca09a86d6e709db736a54ce5ad7c18d3cff84b     0        5000000 lovelace
dd274484a45259ec210a7e2011863d0ce9fe4e6f6070828a691d9761d9dbffd7     0        10000000 lovelace

Bob Wallet

cardano-cli query utxo --address $(< bob-payment.addr) --testnet-magic $TESTNET_ID --mary-era

You should see something like the following:

                           TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
428bedd716e431f6ea79cf7970c0b6c6e80c8a8d7cbfd9dc3832d2c35521391b     0        5000000 lovelace
8ad6b814538e497999206f7c80d8f475d7afc80e3a2c8d89554741ca04d50044     0        10000000 lovelace
b78767219de398a0f7fc5f6669d93d6e32b2127adf972666320e81f63c5c2cdc     0        50000000 lovelace

Select a UTXO to burn

OK, in our example above Alice wanted to buy an everything bagel from Bob and it was $5. In this example we will have the bagel cost 5 ADA because we live in the future.  So we will use the transaction with 10 ADA in it as we have to pay fees to own our own financial freedom to buy everything bagel's with ADA.

From the command above you see the TX:

dd274484a45259ec210a7e2011863d0ce9fe4e6f6070828a691d9761d9dbffd7     0        10000000 lovelace

We need the TxHash, TxIx, and the available lovelace to burn.  Copy them into vars to make scripting easier.

export TX_HASH=dd274484a45259ec210a7e2011863d0ce9fe4e6f6070828a691d9761d9dbffd7
export TX_IX=0
export AVAILABLE_LOVELACE=10000000
export EVERYTHING_BAGEL=5000000
export TX_FEE=0

Build the raw transaction

We set the --fee to zero as we do not know it yet, but we need to build up a transaction to figure it out.

We will need to calculate out how much change Alice will get back in change.

echo $(($AVAILABLE_LOVELACE - $EVERYTHING_BAGEL - 0))
5000000

So, we take our UTXO in and send 5 ADA (5000000 lovelace) to Bob and the rest as change to Alice as a new UTXO.

cardano-cli transaction build-raw \
  --mary-era \
  --fee $TX_FEE \
  --tx-in $TX_HASH#$TX_IX \
  --tx-out $(< alice-payment.addr)+$(($AVAILABLE_LOVELACE - $EVERYTHING_BAGEL - $TX_FEE)) \
  --tx-out $(< bob-payment.addr)+$EVERYTHING_BAGEL \
  --out-file matx.raw

Check for fees

cardano-cli transaction calculate-min-fee \
  --tx-body-file matx.raw \
  --tx-in-count 1 \
  --tx-out-count 2 \
  --witness-count 1 \
  --testnet-magic $TESTNET_ID \
  --protocol-params-file protocol.json

The terminal should return something around the following:

176985 Lovelace

Re-build the raw transaction with the fee

Change our fee variable

export TX_FEE=176985
cardano-cli transaction build-raw \
  --mary-era \
  --fee $TX_FEE \
  --tx-in $TX_HASH#$TX_IX \
  --tx-out $(< alice-payment.addr)+$(($AVAILABLE_LOVELACE - $EVERYTHING_BAGEL - $TX_FEE)) \
  --tx-out $(< bob-payment.addr)+$EVERYTHING_BAGEL \
  --out-file matx.raw

Sign the transaction

Sign the transaction with Alice's private key

cardano-cli transaction sign \
  --signing-key-file alice-payment.skey \
  --testnet-magic $TESTNET_ID \
  --tx-body-file matx.raw \
  --out-file matx.signed

Send it

Send our transaction to the blockchain

cardano-cli transaction submit --tx-file  matx.signed --testnet-magic $TESTNET_ID

ERRORS FRACK!!!

We all make mistakes, just roll with it.

Shelley command failed: transaction submit  Error: Error while submitting tx: ApplyTxError [LedgerFailure (UtxowFailure (UtxoFailure (ValueNotConservedUTxO (Value 10000000 (fromList [])) (Value 10176985 (fromList [])))))]

This looks scary.

Oops! I forgot to add the fee in the original command. Re-export the TX_FEE and resend.

Then Boom! I got this error:

Shelley command failed: transaction submit  Error: Error while submitting tx: ApplyTxError [LedgerFailure (UtxowFailure (UtxoFailure (FeeTooSmallUTxO (Coin 167657) (Coin 0))))]

Looks like my calculations were off even though I ran the calculate-min-fee, better pay more to the man I guess.

Add (Coin 167657) to my original TX_FEE=176985

176985 + 167657 = 344642

export TX_FEE=344642

Run the transaction build-raw again, sign it and send it. As with everything it always works to spend more.

Check out the situation

Let's check on Alice Wallet

cardano-cli query utxo --address $(< alice-payment.addr) --testnet-magic $TESTNET_ID --mary-era

Notice the new TxHash with 4655358 lovelace.


                           TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
1d59d3e9bc5300df465191dce37f4985a67e2c9081d21d392ac92bfe5cf1098b     0        50000000 lovelace
be331f3164cb10af076035f1e5ca09a86d6e709db736a54ce5ad7c18d3cff84b     0        5000000 lovelace
f77f69efa302f1d293e215b8550b19d771c43c349d1a44659aa7e83389496ef5     0        4655358 lovelace

Let's check on Bob Wallet.

cardano-cli query utxo --address $(< bob-payment.addr) --testnet-magic $TESTNET_ID --mary-era

Notice the new TxHash with 5000000 lovelace.

                           TxHash                                 TxIx        Amount
--------------------------------------------------------------------------------------
428bedd716e431f6ea79cf7970c0b6c6e80c8a8d7cbfd9dc3832d2c35521391b     0        5000000 lovelace
8ad6b814538e497999206f7c80d8f475d7afc80e3a2c8d89554741ca04d50044     0        10000000 lovelace
b78767219de398a0f7fc5f6669d93d6e32b2127adf972666320e81f63c5c2cdc     0        50000000 lovelace
f77f69efa302f1d293e215b8550b19d771c43c349d1a44659aa7e83389496ef5     1        5000000 lovelace

If we look on IOHK's testnet explorer we will see everything is on the up and up.

Transaction | Cardano Explorer

Link to the past TX

Now that you've seen how to send transactions using the cardano-cli you will have a better understanding of what is going on when you spend your ADA.  I have a greater appreciation for the GUI wallets Daedalus and Yorori!!

Alrighty, with this small example we went through UTXO's.