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:
- Import our necessary modules
- Define our
AlgodClient
to initiate a connection to the chain - Define our account's address and private key that we will be sending transactions from
- Define an amount of Algorand to send
- Obtain the params needed for all transactions
- Generate a new account for testing, so we have an address to send testnet Algorand to
- Define our payment transaction and its parameters
- Sign the transaction with our private key
- Assign the result of sending our signed transaction with the
send_transaction()
function from theAlgodClient
class - Use the
wait_for_confirmation()
function to ensure the transaction is successful - 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 thealgosdk.util
module - the
PaymentTxn
class andwait_for_confirmation()
function from thealgosdk.transaction
module - The
send_transaction()
function, which is a method from theAlgodClient
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.