Payment Transactions

package main

import (
    "fmt"
    "github.com/algorand/go-algorand-sdk/crypto"
    "github.com/algorand/go-algorand-sdk/mnemonic"
)

func main() {
    // METHOD 1: Create a New Account
    newAccount := crypto.GenerateAccount()
    fmt.Printf("Private Key: %s\n", newAccount.PrivateKey)
    fmt.Printf("Address: %s\n", newAccount.Address.String())

    // Convert Private Key to Mnemonic
    mnemo, err := mnemonic.FromPrivateKey(newAccount.PrivateKey)
    if err != nil {
        fmt.Printf("Error converting private key to mnemonic: %s\n", err)
        return
    }
    fmt.Println("Mnemonic:", mnemo)

    // METHOD 2: Use Mnemonic to Obtain Private Key
    // Assuming mnemonicPhrase is obtained securely and already exists
    mnemonicPhrase := "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"
    privKey, err := mnemonic.ToPrivateKey(mnemonicPhrase)
    if err != nil {
        fmt.Printf("Error retrieving private key from mnemonic: %s\n", err)
        return
    }
    fmt.Printf("Private Key from Mnemonic: %s\n", privKey)
}

When developing in Go with the Algorand SDK, there are two primary methods for obtaining a private key and address which are necessary for signing transactions. The private key is typically managed in byte slice format and is essential for the creation and signing of transactions.

Method 1: Create a New Account and Obtain the Private Key and Address Directly

In the Algorand Go SDK, you use the crypto.GenerateAccount() function to generate a new account. This function returns an account object that contains both the private key and the address, not in a tuple like Python, but directly accessible via the account object properties. Once you have this account, you can print the private key and the address. These details can then be used to sign transactions or manage Algorand assets.

After generating a new account, you can fund it by sending Algos to the generated address. This can be done through the Algorand dispenser for testnet, which is available at https://bank.testnet.algorand.network/. Remember that all accounts require a minimum balance of 0.1 Algo to remain active, and each transaction typically costs a fee of 0.001 Algo.

For wallet applications, if you need to derive the mnemonic from the private key, you can use the mnemonic.FromPrivateKey() function provided by the Go SDK. This function converts the private key into a human-readable mnemonic phrase.

Method 2: Convert an Existing Mnemonic to a Private Key

If you already possess a mnemonic and need to derive the corresponding private key, you can utilize the mnemonic.ToPrivateKey(mnemonic string) function. This function takes a string mnemonic and returns the corresponding private key. It is crucial to ensure that the mnemonic is correctly formatted, enclosed in quotes, and that it includes spaces between the words as expected in standard mnemonic phrases.

Using the Account for Transactions

Once you have an account with funds, you can use the AlgodClient to initiate a connection to the Algorand blockchain. This setup allows you to interact with the blockchain, send transactions, and check account details. You might engage in various transaction types such as sending Algos to another account, rekeying the account (which changes the authoritative private key for the account), or closing the account (transferring all remaining Algos to another account). Each of these actions can be encapsulated within a payment transaction using the Algorand Go SDK.

This text should provide a clear, non-code explanation of the key processes involved in using the Algorand Go SDK for creating and managing accounts, as well as performing transactions.

package main

import (
    "context"
    "fmt"
    "github.com/algorand/go-algorand-sdk/client/algod"
    "github.com/algorand/go-algorand-sdk/crypto"
    "github.com/algorand/go-algorand-sdk/future"
    "github.com/algorand/go-algorand-sdk/types"
)

func main() {
    // Define the Algod client
    algodToken := ""
    algodAddress := "https://testnet-api.algonode.cloud"
    headers := []*algod.Header{{Key: "X-API-Key", Value: algodToken}}
    algodClient, err := algod.MakeClientWithHeaders(algodAddress, algodToken, headers)
    if err != nil {
        fmt.Printf("Failed to make algod client: %s\n", err)
        return
    }

    // Existing account details
    address := "I3BHPDWGH63J47JBG2P7RJLOGD3L3HEBOI4KKUKSV3MZSYFX4VFDIDYSMU"
    privateKey := "6KitD65Q7V6ZDB29EEx1YtoBeqy0PDt+78Ga4DchXItGwneOxj+2nn0hNp/4pW4w9r2cgXI4pVFSrtmZYLflSg=="

    // Convert 1.001 Algo to microAlgos for transaction amount
    amount := uint64(1.001 * 1e6) // Algos are expressed in microAlgos in the SDK

    // Fetch the suggested transaction parameters
    txParams, err := algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }

    // Generate a new account
    newAccount := crypto.GenerateAccount()
    newAccountAddress := newAccount.Address.String()
    newAccountPrivateKey := newAccount.PrivateKey

    // Create a payment transaction
    note := []byte("Here's your one Algo!")
    tx, err := future.MakePaymentTxn(address, newAccountAddress, amount, note, "", txParams)
    if err != nil {
        fmt.Printf("Failed to make transaction: %s\n", err)
        return
    }

    // Sign the transaction
    signTx, err := crypto.SignTransaction(privateKey, tx)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }

    // Send the transaction
    sendResponse, err := algodClient.SendRawTransaction(signTx).Do(context.Background())
    if err != nil {
        fmt.Printf("Failed to send transaction: %s\n", err)
        return
    }

    // Wait for confirmation
    confirmedTxn, err := future.WaitForConfirmation(algodClient, sendResponse.TxID, 4, context.Background())
    if err != nil {
        fmt.Printf("Error waiting for confirmation: %s\n", err)
        return
    }

    // Print the transaction ID
    fmt.Printf("Transaction confirmed with ID: %s\n", confirmedTxn.Txn.Txn.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, https://testnet.explorer.perawallet.app/, https://www.blockpack.app/#/explorer/home, or https://app.dappflow.org/explorer/home

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 SDKs, 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.

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)

# Repeat the process for the Rekey Transaction and the close remainder to transaction

// 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 generate a new account in the Algorand Go SDK?





Question 2

Which function is used to convert a private key to a mnemonic in the Algorand Go SDK?





Question 3

How do you convert an amount in Algos to microAlgos in the Algorand Go SDK?

amount := 1.001
microAlgos := uint64(amount * 1e6)




Question 4

Which function is used to sign a transaction in the Algorand Go SDK?





Code Editor