Payment Transactions

In this chapter we are creating and managing Algorand accounts in Python, including generating addresses, converting mnemonics, and executing secure payment transactions.

from algosdk.v2client.algod import AlgodClient
from algosdk.transaction import PaymentTxn, wait_for_confirmation
from algosdk import account
from algosdk import mnemonic
from algosdk.util import algos_to_microalgos    

Two Ways to Import an Account

Method 1 Create a New Account

private_key, address = account.generate_account()
print("Private Key: ", private_key)
print("Address: ", address)
#Output
#Private Key: 6KitD65Q7V6ZDB29EEx1YtoBeqy0PDt+78Ga4DchXItGwneOxj+2nn0hNp/4pW4w9r2cgXI4pVFSrtmZYLflSg== 
#Address: I3BHPDWGH63J47JBG2P7RJLOGD3L3HEBOI4KKUKSV3MZSYFX4VFDIDYSMU

If needed to import to a wallet, you can obtain the mnemonic from the private key

mnemonic_phrase = mnemonic.from_private_key(private_key)
print(mnemonic_phrase)
#Output: brown repeat amazing april survey fish gospel brown bless core deny plate admit burden pistol device shuffle sadness genius answer hurt analyst foot above annual <== Mnemonic

Method 2 Use mnemonic to obtain/redeem private key

mnemonic_phrase = 'brown repeat amazing april survey fish gospel brown bless core deny plate admit burden pistol device shuffle sadness genius answer hurt analyst foot above annual'
private_key = mnemonic.to_private_key(mnemonic_phrase)
print(private_key)
#Output: 6KitD65Q7V6ZDB29EEx1YtoBeqy0PDt+78Ga4DchXItGwneOxj+2nn0hNp/4pW4w9r2cgXI4pVFSrtmZYLflSg== <== Private Key

When developing in Python, there are two methods to obtain a private key and address to use for signing transactions you create. The private key is in base64 format and required for transaction signing.

Method 1: Create a brand new account and obtain the private key and address directly

In the algosdk's account module, there is a function called generate_account(), which you can import at the beginning of your code, and is defined here:

(function) def generate_account() -> tuple[str, Any]

Returns: (str, str): private key, account address

This returns the private key and account address as strings. Since this returns a tuple of two items, you would assign two variables to the output of the function, and then subsequently print them.

In testnet, you can now fund the account by inputting the address at https://bank.testnet.algorand.network/. All accounts require a minimum balance of 0.1 Algo to send transactions, and then 0.001 Algorand per transaction fee.

Should you need the mnemonic to import into a wallet application later, you can do so with the from_private_key() function in the mnemonic library as well; input your private key into the function and assign the output variable to something like "mnemonic_phrase". Function is defined below:

(function) def from_private_key(private_key: Any) -> str
#Returns the mnemonic for the private key.

Args: private_key (str): private key in base64

Returns: str: mnemonic

Method 2: Convert an Existing Mnemonic to a Private Key

Should you already have a mnemonic and require the private key in base64 format, you can create a variable called "mnemonic_phrase" and use the result of the to_private_key() function in the mnemonic library, which is defined below:

(function) def to_private_key(mnemonic: Any) -> str
#Returns the private key for the mnemonic.

Args: mnemonic (str): mnemonic of the private key

Returns: str: private key in base64

The mnemonic must be a string, meaning it is enclosed in apostrophes or double quotes ('' or ""), and there must be a space between words.

Now lets use the account we generated and funded using https://bank.testnet.algorand.network/, create our AlgodClient class to initiate a connection to the Algorand blockchain, generate a new account to interact with, and subsequently sign, and send some different kinds of payment transactions.

We will be trying a typical payment transaction with a note field, followed by a rekey transaction (rekey to a new account and back to ourselves from the new account), and an account closing transaction. All three are under the scope of a payment transaction.

from algosdk.v2client.algod import AlgodClient
from algosdk.account import generate_account
from algosdk.util import algos_to_microalgos
from algosdk.transaction import PaymentTxn, wait_for_confirmation

algod_token = ''
algod_server = 'https://testnet-api.algonode.cloud'
algod_client = AlgodClient(algod_token, algod_server)

address = 'I3BHPDWGH63J47JBG2P7RJLOGD3L3HEBOI4KKUKSV3MZSYFX4VFDIDYSMU'
private_key = '6KitD65Q7V6ZDB29EEx1YtoBeqy0PDt+78Ga4DchXItGwneOxj+2nn0hNp/4pW4w9r2cgXI4pVFSrtmZYLflSg=='

amount = algos_to_microalgos(1.001)
params = algod_client.suggested_params()

new_account_private_key, new_account_address = generate_account()

basic_payment_transaction = PaymentTxn(
    sender = address,
    receiver = new_account_address,
    sp = params,
    amt = amount,
    note = "Here's your one Algo!"
)

signed_basic_payment_transaction = basic_payment_transaction.sign(private_key)
transaction_id = algod_client.send_transaction(signed_basic_payment_transaction)
wait_for_confirmation(algod_client, transaction_id)
print(transaction_id)

Steps:

  1. Import our necessary modules
  2. Define our AlgodClient to initiate a connection to the chain
  3. Define our account's address and private key that we will be sending transactions from
  4. Define an amount of Algorand to send
  5. Obtain the params needed for all transactions
  6. Generate a new account for testing, so we have an address to send testnet Algorand to
  7. Define our payment transaction and its parameters
  8. Sign the transaction with our private key
  9. Assign the result of sending our signed transaction with the send_transaction() function from the AlgodClient class
  10. Use the wait_for_confirmation() function to ensure the transaction is successful
  11. Print the transaction ID for reference on an explorer like allo.info, Pera Wallet Explorer, BlockPack Explorer, or DappFlow Explorer.

We introduce a few new functions here:

  • the algos_to_microalgos() function from the algosdk.util module
  • the PaymentTxn class and wait_for_confirmation() function from the algosdk.transaction module
  • The send_transaction() function, which is a method from the AlgodClient class

In the algorand SDK's, when we want to reference an amount of algo, that amount needs to be in a format called "Microalgo"; Microalgo is essentially an amount of algo times 1,000,000. Meaning that 1 Algo would be 1,000,000 Microalgo, and the transaction fee of 0.001 Algo is 1,000 Microalgo.

Instead of manually calculating the microalgo amount each time, what we can do is use the algos_to_microalgos() function, and pass in the amount of algo as an argument, defined below:

(function) def algos_to_microalgos(algos: Any) -> Any
#Converts algos to microalgos.

Args: algos (int or decimal): how many algos

Returns: int: how many microalgos

The PaymentTransaction class comes with a lot of functionality, aside from simple payment transactions of an amount of Algo to more advanced features, including:

  • the ability to rekey your account to another (giving them full access, and losing your access)
  • closing your account (send all of your remaining Algo to them)

PaymentTransaction format:

class PaymentTxn (
    sender: str,
    sp: SuggestedParams,
    receiver: Any,
    amt: Any,
    close_remainder_to: Any | None = None,
    note: Any | None = None,
    lease: Any | None = None,
    rekey_to: Any | None = None
)
#Represents a payment transaction.

Args: sender (str): address of the sender sp (SuggestedParams): suggested params from algod receiver (str): address of the receiver amt (int): amount in microAlgos to be sent close_remainder_to (str, optional): if nonempty, account will be closed and remaining algos will be sent to this address note (bytes, optional): arbitrary optional bytes lease (byte[32], optional): specifies a lease, and no other transaction with the same sender and lease can be confirmed in this transaction's valid rounds rekey_to (str, optional): additionally rekey the sender to this address

After we define our Payment Transaction class parameters, we can then use the sign() method that is included within it. This sign function accepts our private key, and outputs a signed transaction object, which is needed to input to the send_transaction() function:

(method) def sign(private_key: Any) -> SignedTransaction
#Sign the transaction with a private key.

Args: private_key (str): the private key of the signing account

Returns: SignedTransaction: signed transaction with the signature

The wait_for_confirmation() function requires the AlgodClient class variable we created, as well as the transaction ID to wait for:

(function) def wait_for_confirmation (
    algod_client: AlgodClient,
    txid: str
)

Lastly, the send_transaction() function, which accepts signed transaction objects:

(method) def send_transaction (
        txn: GenericSignedTransaction
) -> Outputs transaction ID

Below are examples of rekey transactions and close amount to transactions, which are sent in succession (BUT NOT A GROUP TRANSACTION, WHICH WE WILL LEARN ABOUT LATER)

# Rekey Transaction
rekey_to_new_account_payment = PaymentTxn(
    sender = address,
    receiver = new_account_address,
    sp = params,
    amt = 0,
    rekey_to = new_account_address,
    note = "Take care of my account for me! I'll be back in a week"
)
signed_rekey_to_new_account_payment = rekey_to_new_account_payment.sign(private_key)
transaction_id = algod_client.send_transaction(signed_rekey_to_new_account_payment)
wait_for_confirmation(algod_client, transaction_id)
print(transaction_id)
# New account rekeys back to the original account, note that the sender is the original account but the new account uses their own private key, not the original accounts private key
rekey_back_to_old_account_from_new_account = PaymentTxn(
    sender = address,
    receiver = address,
    sp = params,
    rekey_to = address,
    amt = 0,
    note = "Sorry! I'm too busy trading this week. Maybe ask PorkChop.algo?"
)
signed_rekey_back_to_old_account_from_new_account = rekey_back_to_old_account_from_new_account.sign(new_account_private_key)
transaction_id = algod_client.send_transaction(signed_rekey_back_to_old_account_from_new_account)
wait_for_confirmation(algod_client, transaction_id)
print(transaction_id)
# Close remainder to transaction
close_account_to_new_account = PaymentTxn(
    sender = address,
    receiver = new_account_address,
    sp = params,
    amt = 0,
    close_remainder_to = new_account_address,
    note = 'Take care of my precious Algo!'
)
signed_close_account_to_new_account = close_account_to_new_account.sign(private_key)
transaction_id = algod_client.send_transaction(signed_close_account_to_new_account)
wait_for_confirmation(algod_client, transaction_id)
print(transaction_id)

Although the Payment Transaction has many possible inputs, the bare minimum is using the sender, sp, receiver, and amt field. Anything else is at your discretion!

DISCLAIMER: that when rekeying and closing out accounts, this process is irreversible! If you don't know the person, or feel unsure about doing so, you should never use these transactions outside of testing purposes without ultimate confidence. No platforms currently utilize rekey transactions for users, but do use them internally when generating smart contracts for contract to contract calls, which will come later in our learning process.


Quiz

Question 1

What function is used to create a new Algorand account in the algosdk library?





Question 2

How can you obtain the mnemonic phrase from a private key in the algosdk library?





Question 3

What is the correct way to initialize an AlgodClient instance to connect to the Algorand testnet?

algod_token = ''
algod_server = 'https://testnet-api.algonode.cloud'
algod_client = algod.AlgodClient(algod_token, algod_server)




Question 4

What function is used to convert an amount of Algo to MicroAlgo?





Question 5

Which function is used to send a signed transaction in the Algorand SDK?





Code Editor