Skip to content

How to submit transaction via cardano tx CLI

piotr-iohk edited this page Oct 1, 2020 · 12 revisions

Overview

In this example we will be using cardano-address, cardano-tx and cardano-wallet command line tools to construct and then send transaction on Cardano Testnet. We will show how to:

  • derive addresses from the known mnemonic sentences with cardano-address
  • construct transaction using cardano-tx
  • and, finally, submit this transaction on chain using cardano-wallet transaction submit

Above operations will be presented for:

cardano-address, cardano-tx and cardano-wallet CLI utilites can be found on their respective release pages on GitHub.

Starting cardano-node and cardano-wallet server

Please note that for some steps of this tutorial we need to have cardano-node and cardano-wallet running. In particular this is needed for submitting a transaction or checking payment fee using cardano-wallet. Instructions for running cardano-wallet and cardano-node can be found here

It is however not needed for constructing a transaction via cardano-tx.

Shelley wallet

top ⤴️

Constructing transaction

The wallet

In the example we will be using an Shelley wallet identified by following mnemonic sentence:

tooth extend water monkey hub magnet uncover cherry shrug sheriff sugar unique memory nurse solve nature pepper phrase hour stick slight anchor earth cave

The balance of the wallet is 10 ADA (10 000 000 Lovelace). All funds are placed on the address of the wallet, which we can derive using cardano-address utility:

Let's assume that our mnemonic phrase is saved in a file: shelley_phrase.prv.

cat shelley_phrase.prv \
| cardano-address key from-recovery-phrase Shelley \
| cardano-address key child 1852H/1815H/0H/0/0 \
| cardano-address key public \
| cardano-address address payment --network-tag testnet

addr_test1vr5egevl9mmukz5ccwlnjl2tsftn0fa7d9gdt7ngpyfes9g434s4t

ℹ️ Since this was the first external address of our Shelley wallet, we know that the derivation path of the underlying private key is 1852H/1815H/0H/0/0. The second address would be 1852H/1815H/0H/0/1 and so forth...

In this example, funds were sent to this address as the first output of a transaction with id 17e21cf64c07bfee7ff5e19fbc0a99ab13c9fb1eef62ed6101846de5b2459de5. On a 0-based index, it makes it the output #0.

Signing key

The transaction needs to be signed with the key owning funds at the address.

We can use cardano-addresses CLI utility to get the key that we'll use for signing our transaction.

XPRV=`cat shelley_phrase.prv \
| cardano-address key from-recovery-phrase Shelley \
| cardano-address key child 1852H/1815H/0H/0/0 --base16`

⚠️ Change address ⚠️

Before we send transaction we need to specify the change, i.e. the how much of the remaining funds need to go back to our wallet.

This is extremely important! if we don't specify the change all remaining funds will be lost and we end up with balance 0 on our wallet after the transaction.

First, let's derive a change address using cardano-address:

cat shelley_phrase.prv \
| cardano-address key from-recovery-phrase Shelley \
| cardano-address key child 1852H/1815H/0H/0/1 \
| cardano-address key public \
| cardano-address address payment --network-tag testnet

addr_test1vq7jm88trfrmcxmzkaycefykk948kjaucmvhah5ph2rzr6ccwd4rn

The change is calculated as total_balance - amount_to_be_sent - fee.

In this example we will be sending 1 ADA (1000000 lovelace) to a destination address 2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1. As stated before the source wallet has total balance of 10 ADA (10 000 000 lovelaces), therefore the amount to be send back as a change should be:

10000000 - 1000000 - fee

We can check the fee using cardano-wallet API:


curl  -X POST http://localhost:8090/v2/wallets/4157603597d008fd8fe88b84f72696809e9a6a06/payment-fees \
-H "Content-Type: application/json; charset=utf-8" \
-d '{
"payments": [
{
"address": "2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1",
"amount": { "quantity": 1000000, "unit": "lovelace" }
} ] }' | jq

......
{
  "estimated_min": {
    "quantity": 167965,
    "unit": "lovelace"
  },
  "estimated_max": {
    "quantity": 167965,
    "unit": "lovelace"
  }
}


Ok, so finally the change should be:

10000000 - 1000000 - 167965 = 8832035

Constructing a transaction

Finally we can construct transaction using cardano-tx CLI.

cardano-tx empty 1097911063 \
  | cardano-tx add-input 0 17e21cf64c07bfee7ff5e19fbc0a99ab13c9fb1eef62ed6101846de5b2459de5 \
  | cardano-tx add-output 1000000 2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1 \
  | cardano-tx add-output 8832035 addr_test1vq7jm88trfrmcxmzkaycefykk948kjaucmvhah5ph2rzr6ccwd4rn \
  | cardano-tx lock \
  | cardano-tx sign-with $XPRV \
  | cardano-tx serialize --base16
command explanation
cardano-tx empty 1097911063 Specify Network Magic for Testnet.
cardano-tx add-input 0 17e21cf6... The input for the transaction to be send will come from this particular transaction id that is already in_ledger for the source wallet.
cardano-tx add-output 1000000 2cWKMJemoBaj... We will be sending 1 ADA to this destination address.
cardano-tx add-output 8832035 addr_test1... We need to send the remaining change back to the source wallet 10000000 - 1000000 - 167965 = 8832035
cardano-tx lock Lock the transaction, so it can be signed.
cardano-tx sign-with $XPRV Sign transaction.
cardano-tx serialize --base16 Serialize it, so it can be submitted via cardano-wallet.

Submitting transaction

Now we can send the transaction over using cardano-wallet transaction submit:

cardano-wallet transaction submit 82839f8200d81858248258206fdbeda74dad6d75999b895573a13f78ab7629d4c93454b322dd97678425665a01ff9f8282d818582883581c8011d974e8746a3b83cf145cde56b0053ad3fbe6dc62c5ab97f42309a102451a4170cb17001afb04e5681a000f42408282d818584983581c78e321d18854e2a570408086fafcc10021f003e751c3598a17535813a201581e581c60040b5457954023e04c2f1d890342bb2b05b96535df583e8b9bc34202451a4170cb17001a1899b5fe1a0086b1bfffa0818200d8185885825840fe6b954427a3f5dc659cfb6c93a3dc882db4664844267b5e1dba6b71957b3e073f20b66f616fba86a983fae9541dcb8b77c28676cc32f80ac28445d8b58ce34c5840ec3fcc115c706d12daa16a146427fbfd1bbd1d670fae8b35e2ab58b2e5e1c0aea8a243abcef590354ad30af4fbab0e019a5fbe156ad8d85816afdc7aef2b150f
Ok.
{
    "id": "e26e56411e10282700313e66dbe04b2f3fc201378417d2d135bca0f34df52e5c"
}

Byron random wallet

top ⤴️

Constructing transaction

The wallet

In the example we will be using an Byron random wallet identified by following mnemonic sentence:

gym trend inject keen drink example truck enter treat ahead stock walnut

The balance of the wallet is 10 ADA (10 000 000 Lovelace). All funds are placed on the address of the wallet, which we can derive using cardano-address utility:

Let's assume that our mnemonic phrase is saved in a file: byron_phrase.prv.

cat byron_phrase.prv \
| cardano-address key from-recovery-phrase Byron > root.prv

cat root.prv\
| cardano-address key child --legacy 14H/42H \
| cardano-address key public \
| cardano-address address bootstrap --root $(cat root.prv | cardano-address key public) \
--network-tag testnet 14H/42H

37btjrVyb4KCjbmtj3biA1rRw4goVKUJJAHbnGExxUkuWT2ygshYCLowpQFAC6MxVVPcNXLDMZgBa1M1W6UYbAuCr4wx61NLZ7KGLGRZgwxq7T91wt

In this example, funds were sent to this address as the second output of a transaction with id 8a4419bc68dd5660ddae604d1469584b2cf39c088c0d9fc43fc52db179be249e. On a 0-based index, it makes it the output #1.

Signing key

The transaction needs to be signed with the key owning funds at the address.

We can use cardano-addresses CLI utility to get the key that we'll use for signing our transaction.

XPRV=`cat root.prv \
| cardano-address key child --legacy 14H/42H --base16`

⚠️ Change address ⚠️

Before we send transaction we need to specify the change, i.e. the how much of the remaining funds need to go back to our wallet.

This is extremely important! if we don't specify the change all remaining funds will be lost and we end up with balance 0 on our wallet after the transaction.

First, let's derive a change address using cardano-address:

cat byron_phrase.prv \
| cardano-address key from-recovery-phrase Byron > root.prv

cat root.prv\
| cardano-address key child --legacy 14H/43H \
| cardano-address key public \
| cardano-address address bootstrap --root $(cat root.prv | cardano-address key public) \
--network-tag testnet 14H/43H

37btjrVyb4KDPuJkYFzkHe7gxQRJi7PiUCM5xBPqUcvPxwneifgKf7BqwRwF6NetY4xMX6zaaNhY1wHhTR2duKvppSbgWwNUqKhsne3tt9AZSdHA9B

The change is calculated as total_balance - amount_to_be_sent - fee.

In this example we will be sending 1 ADA (1000000 lovelace) to a destination address 2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1. As stated before the source wallet has total balance of 10 ADA (10 000 000 lovelaces), therefore the amount to be send back as a change should be:

10000000 - 1000000 - fee

We can check the fee using cardano-wallet API:


curl  -X POST http://localhost:8090/v2/byron-wallets/b890d442ff66870fda819d34aed71393993b074b/payment-fees \
-H "Content-Type: application/json; charset=utf-8" \
-d '{
"payments": [
{
"address": "2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1",
"amount": { "quantity": 1000000, "unit": "lovelace" }
} ] }' | jq

......
{
  "estimated_min": {
    "quantity": 172673,
    "unit": "lovelace"
  },
  "estimated_max": {
    "quantity": 172673,
    "unit": "lovelace"
  }
}

Ok, so finally the change should be:

10000000 - 1000000 - 172673 = 8827327

Constructing a transaction

Finally we can construct transaction using cardano-tx CLI.

cardano-tx empty 1097911063 \
  | cardano-tx add-input 1 8a4419bc68dd5660ddae604d1469584b2cf39c088c0d9fc43fc52db179be249e \
  | cardano-tx add-output 1000000 2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1 \
  | cardano-tx add-output 8827327 37btjrVyb4KDPuJkYFzkHe7gxQRJi7PiUCM5xBPqUcvPxwneifgKf7BqwRwF6NetY4xMX6zaaNhY1wHhTR2duKvppSbgWwNUqKhsne3tt9AZSdHA9B \
  | cardano-tx lock \
  | cardano-tx sign-with $XPRV \
  | cardano-tx serialize --base16
command explanation
cardano-tx empty 1097911063 Specify Network Magic for Testnet.
cardano-tx add-input 1 8a4419... The input for the transaction to be send will come from this particular transaction id that is already in_ledger for the source wallet.
cardano-tx add-output 1000000 2cWKMJemoBaj... We will be sending 1 ADA to this destination address.
cardano-tx add-output 8830231 37btjrVy... We need to send the remaining change back to the source wallet 10000000 - 1000000 - 172673 = 8827327
cardano-tx lock Lock the transaction, so it can be signed.
cardano-tx sign-with $XPRV Sign transaction.
cardano-tx serialize --base16 Serialize it, so it can be submitted via cardano-wallet.

Submitting transaction

Now we can send the transaction over using cardano-wallet transaction submit:

cardano-wallet transaction submit 82839f8200d81858248258206fdbeda74dad6d75999b895573a13f78ab7629d4c93454b322dd97678425665a01ff9f8282d818582883581c8011d974e8746a3b83cf145cde56b0053ad3fbe6dc62c5ab97f42309a102451a4170cb17001afb04e5681a000f42408282d818584983581c78e321d18854e2a570408086fafcc10021f003e751c3598a17535813a201581e581c60040b5457954023e04c2f1d890342bb2b05b96535df583e8b9bc34202451a4170cb17001a1899b5fe1a0086b1bfffa0818200d8185885825840fe6b954427a3f5dc659cfb6c93a3dc882db4664844267b5e1dba6b71957b3e073f20b66f616fba86a983fae9541dcb8b77c28676cc32f80ac28445d8b58ce34c5840ec3fcc115c706d12daa16a146427fbfd1bbd1d670fae8b35e2ab58b2e5e1c0aea8a243abcef590354ad30af4fbab0e019a5fbe156ad8d85816afdc7aef2b150f
Ok.
{
    "id": "e26e56411e10282700313e66dbe04b2f3fc201378417d2d135bca0f34df52e5c"
}

Icarus wallet

top ⤴️

Constructing transaction

The wallet

In the example we will be using an Icarus sequential wallet identified by following mnemonic sentence:

theme book settle across rhythm year riot primary day sudden nest develop purpose check stove

The balance of the wallet is 10 ADA (10 000 000 Lovelace). All funds are placed on the first address of the wallet, which we can derive using cardano-address utility:

Let's assume that our mnemonic phrase is saved in a file: icarus_phrase.prv.

cat icarus_phrase.prv \
| cardano-address key from-recovery-phrase Icarus \
| cardano-address key child 44H/1815H/0H/0/0 \
| cardano-address key public \
| cardano-address address bootstrap --network-tag testnet

2cWKMJemoBakRJh43zNuFrkjApyUVVjQn2eFkjfSdfye4JLzfK9zSngUSvTT1P4TNWB79

In this example, funds were sent to this address as the second output of a transaction with id 29657703d662e9c36845c2f61505e96e92128605ff9eb4b635ec659ed4998625. On a 0-based index, it makes it the output #1.

Signing key

The transaction needs to be signed with the key owning funds at the address.

We can use cardano-addresses CLI utility to get the key that we'll use for signing our transaction.

XPRV=`cat icarus_phrase.prv \
| cardano-address key from-recovery-phrase Icarus \
| cardano-address key child 44H/1815H/0H/0/0 --base16`

ℹ️ Since this was the first external address of our wallet, we know that the derivation path of the underlying private key is 44H/1815H/0H/0/0. The second address would be 44H/1815H/0H/0/1 and so forth...

⚠️ Change address ⚠️

Before we send transaction we need to specify the change, i.e. the how much of the remaining funds need to go back to our wallet.

This is extremely important! if we don't specify the change all remaining funds will be lost and we end up with balance 0 on our wallet after the transaction.

First, let's derive a change address using cardano-address:

echo "theme book settle across rhythm year riot primary day sudden nest develop purpose check stove" \
| cardano-address key from-recovery-phrase Icarus \
| cardano-address key child 44H/1815H/0H/0/1 \
| cardano-address key public \
| cardano-address address bootstrap --network-tag testnet

2cWKMJemoBajEoat816orhcEnFnkgdPgjKS3mwtg4Ccto8mu1odk6KJrfmtx9CkpUuXHp

The change is calculated as total_balance - amount_to_be_sent - fee.

In this example we will be sending 1 ADA (1000000 lovelace) to a destination address 2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1. As stated before the source wallet has total balance of 10 ADA (10 000 000 lovelaces), therefore the amount to be send back as a change should be:

10000000 - 1000000 - fee

We can check the fee using cardano-wallet API:


curl  -X POST http://localhost:8090/v2/byron-wallets/e7d4416c1c8e1c7a8fb7639c1145f5b3d4d8b1cf/payment-fees \
-H "Content-Type: application/json; charset=utf-8" \
-d '{
"payments": [
{
"address": "2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1",
"amount": { "quantity": 1000000, "unit": "lovelace" }
} ] }' | jq

......
Ok.
{
  "estimated_min": {
    "quantity": 169769,
    "unit": "lovelace"
  },
  "estimated_max": {
    "quantity": 169769,
    "unit": "lovelace"
  }
}

Ok, so finally the change should be:

10000000 - 1000000 - 169769 = 8830231

Constructing a transaction

Finally we can construct transaction using cardano-tx CLI.

cardano-tx empty 1097911063 \
  | cardano-tx add-input 1 29657703d662e9c36845c2f61505e96e92128605ff9eb4b635ec659ed4998625 \
  | cardano-tx add-output 1000000 2cWKMJemoBajGP6n7JUo52neaRtxHTCWF1R9q7uA8PvexKFKvMMGvEAF9fSMCdzJZUUj1 \
  | cardano-tx add-output 8830231 2cWKMJemoBajEoat816orhcEnFnkgdPgjKS3mwtg4Ccto8mu1odk6KJrfmtx9CkpUuXHp \
  | cardano-tx lock \
  | cardano-tx sign-with $XPRV \
  | cardano-tx serialize --base16
command explanation
cardano-tx empty 1097911063 Specify Network Magic for Testnet.
cardano-tx add-input 1 2965770... The input for the transaction to be send will come from this particular transaction id that is already in_ledger for the source wallet.
cardano-tx add-output 1000000 2cWKMJemoBaj... We will be sending 1 ADA to this destination address.
cardano-tx add-output 8830231 2cWKMJemoBaj... We need to send the remaining change back to the source wallet 10000000 - 1000000 - 169769 = 8830231
cardano-tx lock Lock the transaction, so it can be signed.
cardano-tx sign-with $XPRV Sign transaction.
cardano-tx serialize --base16 Serialize it, so it can be submitted via cardano-wallet.

Submitting transaction

Now we can send the transaction over using cardano-wallet transaction submit:

cardano-wallet transaction submit 82839f8200d818582482582029657703d662e9c36845c2f61505e96e92128605ff9eb4b635ec659ed499862501ff9f8282d818582883581c8011d974e8746a3b83cf145cde56b0053ad3fbe6dc62c5ab97f42309a102451a4170cb17001afb04e5681a000f42408282d818582883581c7e708fd52abd2263f453c0a4fdc4713a024f33fcd0acd25f058a01cda102451a4170cb17001a212741771a0086bd17ffa0818200d818588582584065eca1dee6ba9b2ef4c8515d430d800e9a2e4628062083da3f3c327eb0dccc2410723b6c2ab4b18feffe6931140d1d2ecfeb6a28ea6f0ec9fe6a5e9ac4838fd0584093f745130999ab80411b5899a39358ff3bebe470952d222b0f394bb169e1effef06587e92b38276542684583dc0eef6a9cee5acf8d1d491535dd841c5eaf5209
Ok.
{
    "id": "e26e56411e10282700313e66dbe04b2f3fc201378417d2d135bca0f34df52e5c"
}