This post will take you through the must-know concepts for creating a basic pay transaction on Algorand, i.e. when you want to send some number of Algos from one account to another account on the Algorand blockchain.

First we will review some basic concepts with respect to Algorand transactions. Then we will go through five examples that will apply those concepts in common scenarios. I will use the Python SDK to demonstrate these concepts, but the functionality is also available in Javascript, Java, and Go.

The Must-Knows

Transaction Validity Round Range — Every transaction on Algorand is bounded by a first and last valid round, which must be specified on creation. The transaction will be rejected by the nodes if the current round is outside of this range. The round range (i.e. last valid - first valid) cannot exceed 1000 rounds.

Transaction Fee — Every transaction requires a minimum fee of 1,000 microalgos. 1 algo equals 1,000,000 microalgos. When blocks are full, using the suggested fee (returned as a fee-per-byte ratio) will make it more likely for your transaction to be accepted into a block given current network traffic. We will save the details of how the suggested fee is calculated for another time.

In the SDKs, you can specify a fee as a fee-per-byte and the SDK will do the work of estimating the fee for the full transaction. If you just want to use the minimum fee, set your fee to 1000 and set the flat_fee argument to True.

Minimum Balance — Every account registered on the Algorand ledger must have a minimum balance of 100,000 microalgos. Therefore, a transaction that sends funds to a new account (i.e. an account with a 0 balance) must send a minimum of 100,000 microalgos for the transaction to be valid. Similarly, any sending account must make sure that the amount of algos that they send will keep their remaining balance greater than or equal to 100,000 microalgos. If you want to completely empty funds from the sending account, you can specify a close-to address, which will receive any remaining funds and the sending account will be removed from the ledger.

TxIDs and Duplicate Transactions — Every transaction has an associated transaction ID which is generated by hashing the content of the transaction. You can only send a transaction (with a particular transaction ID) to the network once.

Genesis Hash and ID — Every transaction must specify the hash of the genesis block for the network the transaction is intended for. This ensures that the transaction can only be valid for that network. The network ID is optional but provides a human-readable network identifier for the transaction.

Transaction Signatures— Signing a transaction adds a signature to the transaction using the sending account’s private key.

Transactions sent from a multisignature account, must be aware of the composition of the multisignature account in order to be sign-able. This can be accomplished in two ways 1) If you are signing with a kmd wallet and the kmd wallet has the multisignature account in it, it will add the required msig object upon signature from any one of the composed accounts. 2) You can make the transaction sign-able by specifying the template msig values within the transaction itself. I recommend option 2 for cases where you plan to sign a transaction in the future and on a separate device so you can be sure that the required information is already there.

Pay Scenarios

I will use the Purestake API (which provides the Algorand API as a service, so you do not need to run your own node) and I will include the node address and token in the code so you can easily copy/paste and run this code yourself.

As a prerequisite, make sure you have the Algorand Python SDK installed:

pip3 install py-algorand-sdk

The full example code can be found here. I will link to relevant parts of the code throughout this post.

In all of these examples, I will be Alice and will have the secret key associated with her account. Make sure to change her address if you want to be able to sign the transactions in these examples.

There are a few transaction parameters that I can only define relative to the current conditions of the network. I initialize those variables globally. Specifically, I initialize an instance of an algod client (using Purestake’s API), and I call the suggested_params function to pull the genesis hash (and the optional genesis ID) of the network, as well as the latest network round (so we can accurately set my transaction’s first valid round).

I also globally set my addresses for Alice, Bob, and Charlie.

Now we can create transactions per each of the scenarios below. The “pay” transaction constructor class for Python can be referenced here.

Scenario 1: Alice wants to send 100 Algos to Bob with a flat fee of 1000 microalgos.

This is our most basic scenario, which can be found in the corresponding function scenario1 . Here, we set the amount to 100,000,000 microalgos which equals 100 algos. We set the fee to 1000 microalgos and set the keyword argument flat_fee to True . We fill in our other positional arguments that were defined globally, which are our first_valid round, our last_valid round, the sender address (i.e. alice ), the receiver address (i.e. bob ) and the genesis_hash . We place our genesis_id as one of our optional keyword arguments.

Code Input:

Transaction Output:

A human-readable view of the transaction file produced by `goal clerk inspect`.

Scenario 2: Alice wants to send 100 Algos to Bob with a note and the suggested fee.

Here we will take Scenario 1, add a note to it, and use the suggested fee instead of a flat fee. Notes can be any arbitrary byte data up to 1 KB in size. The SDK will estimate the size of the transaction in bytes and multiply that by the suggested fee parameter provided. If the fee is less than the minimum 1000 microalgos, then it will use 1000. See updated code below.

Code Input:

We now use the suggested fee as input and remove the flat_fee = True keyword argument, since the default is False.

Transaction Output:

Using the suggested fee set our overall fee above the minimum. In this case, the suggested fee per byte ratio was 1 to 1 and the large size of our note increased the fee accordingly. For smaller transactions, this likely would have still been the minimum 1000 microalgos given the network conditions when I ran this script.

Scenario 3: Alice wants to transfer all of her Algos to Bob.

Since all accounts on the Algorand ledger must have a minimum balance, we need to explicitly “close out” an account by using the close_remainder_to keyword argument. In this example, our receiving and close-to address are the same, so we do not need to be too precise on the amount since all the funds will be sent to the same address. So we use 1 microalgo in the code below. If the receiving and close-to address were different, you would want to precisely set the amount value.

Code Input:

Given that our close to and receiving addresses are both bob, the amount is trivially set at 1 microalgo.

Transaction Output:

We confirm that the receiving and close to address again are the same.

Scenario 4: Alice wants to send 100 Algos to Bob in 24 hours from now.

In this scenario, we need to estimate a future round based on the average time it takes for a block to be produced. Since this is only 24 hours from now and we have a max validity range of 1000, we can usually estimate accurately enough such that we will hit our UTC time target. In the code below, we assume an average block time of 4.5 seconds to recalculate the first_valid and last_valid rounds.

Code Input:

Transaction Output:

Note the 19,200 round increase (~24 hours in the future assuming blocks are produced every 4.5 seconds) for both the first and last valid round relative to the previous transactions.

Scenario 5: Alice and Bob want to send 100 Algos to Charlie from a joint multisignature account.

In this scenario, we will produce two transactions that will demonstrate the two ways you can create a multisignature transaction as described in the must-knows section above. The first transaction will look the same as Scenario 1, except with a different sending address (the multisig address). The second transaction will include an msig object. Again, both transactions are valid, but in the former case, you need to make sure your wallet that signs the transaction has the multisignature account information imported for it to know how to sign it. This is why I recommend the latter case when possible as it gives you more diverse signing possibilities.

First we need to create a multisignature address using Alice and Bob’s two addresses. We will create a 2/2 multisignature account, meaning any transaction from that account will require a signature from both Alice and Bob. Here is the portion of the code that creates the multisig object and generates (and prints) the address.

We swap out the sending address with the new multisig_address and change the receiver address to charlie .

Update the sending and receiving addresses.

Then we create a payment transaction just like the previous examples. This completes the first type of transaction without the multisig information.

Transaction Output (no multisig):

This type of transaction will require that the signing device “knows” the multisignature account information so that it can add the information when it signs.

For the second type of transaction, we will add the multisig object to the transaction itself. We first instantiate a MultisigTransaction object and feed it our PaymentTxn and our Multisig object.

tx represents the first transaction and tx_with_multisig represents the second.

Transaction Output (with multisig):

Running this code

Run these examples yourself by cloning this repo and simply running:

python3 pay-scenarios.py

It is helpful to have goal installed too so you can see the human-readable version of the transactions you create. Play around with the different arguments to see how the format and values of the transaction output change. Update the addresses and try signing the transactions with the SDKs and with goal .

Signing Transactions

You can sign these transactions using functions in any of the SDKs or using goal if you leverage the write_to_file functions. Here is a quick video demonstrating how the output of a transaction changes when signing. I will use goal to inspect and sign the transactions.

Demo of signing pay transactions using the goal CLI.

Conclusion

This post took you through some common scenarios for the Algorand pay transaction. It leveraged the Python SDK to create the transactions and goal to inspect and sign them.

As a developer, you will find that many of the must-know concepts introduced above carry through to the other transaction types on Algorand. Therefore, learning the basics of the Algorand pay transaction through your preferred SDK is a great way to familiarize yourself with the foundational paradigms of the Algorand network and will aid you in creating more complex scenarios down the road.