Payment Transactions

    const algosdk = require('algosdk');
    
    // TWO WAYS TO IMPORT AN ACCOUNT
    
    // METHOD 1
    // CREATE A NEW ACCOUNT
    const account = algosdk.generateAccount();
    console.log('Private Key:', Buffer.from(account.sk).toString('base64')); // Output Private Key
    console.log('Address:', account.addr); // Output Address
    
    // IF NEEDED TO IMPORT TO A WALLET, YOU CAN OBTAIN THE MNEMONIC FROM THE PRIVATE KEY
    // REDEEM MNEMONIC FROM PRIVATE KEY
    const mnemonicPhrase = algosdk.secretKeyToMnemonic(account.sk);
    console.log('Mnemonic Phrase:', mnemonicPhrase); // Output Mnemonic Phrase
    
    // METHOD 2
    // USE MNEMONIC TO OBTAIN PRIVATE KEY
    // REDEEM PRIVATE KEY FROM MNEMONIC
    const mnemonic = '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';
    const recoveredAccount = algosdk.mnemonicToSecretKey(mnemonic);
    console.log('Recovered Private Key:', Buffer.from(recoveredAccount.sk).toString('base64')); // Output Private Key
    

When developing in JavaScript with the Algorand blockchain, there are two methods to obtain a private key and address for signing transactions you create. The private key is in base64 format and is 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 generateAccount(), which you can import at the beginning of your code, and is defined here:

    function generateAccount() 
    Generate an account.
    
    Returns:
    {addr: string, sk: Uint8Array}: An object containing the account address and secret key
    

This returns an object containing the private key (as a Uint8Array) and the account address as strings. You would assign the output of the function to a variable and then subsequently print them.

In the 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 secretKeyToMnemonic() function in the algosdk library as well; input your secret key into the function and assign the output variable to something like "mnemonicPhrase". Function is defined below:

    function secretKeyToMnemonic(secretKey: Uint8Array) -> string
    Return the mnemonic for the secret key.
    
    Args:
    secretKey (Uint8Array): The secret key as a Uint8Array
    
    Returns:
    string: mnemonic phrase
    

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 "mnemonicPhrase" and use the result of the mnemonicToSecretKey() function in the algosdk library, which is defined below:

    function mnemonicToSecretKey(mnemonic: string) -> {addr: string, sk: Uint8Array}
    Return the account object for the mnemonic.
    
    Args:
    mnemonic (string): mnemonic of the private key
    
    Returns:
    {addr: string, sk: Uint8Array}: An object containing the account address and secret key
    

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 let's 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.

    const algosdk = require('algosdk');
    
    // Algod client connection parameters
    const algodToken = '';
    const algodServer = 'https://testnet-api.algonode.cloud';
    const algodPort = '443';
    const algodClient = new algosdk.Algodv2(algodToken, algodServer, algodPort);
    
    // Sender's account details
    const address = 'I3BHPDWGH63J47JBG2P7RJLOGD3L3HEBOI4KKUKSV3MZSYFX4VFDIDYSMU';
    const private_key = '6KitD65Q7V6ZDB29EEx1YtoBeqy0PDt+78Ga4DchXItGwneOxj+2nn0hNp/4pW4w9r2cgXI4pVFSrtmZYLflSg==';
    
    // Convert 1.001 Algos to microalgos
    const amount = algosdk.algosToMicroalgos(1.001);
    
    // Get transaction parameters
    async function submitTransaction() {
        let params = await algodClient.getTransactionParams().do();
    
        // Generate a new account
        const { addr: newAccountAddress, sk: newAccountPrivateKey } = algosdk.generateAccount();
        console.log('New Account Address:', newAccountAddress);
    
        // Create a payment transaction
        let txn = algosdk.makePaymentTxnWithSuggestedParamsFromObject({
            from: address,
            to: newAccountAddress,
            amount: amount,
            note: new TextEncoder().encode("Here's your one Algo!"),
            suggestedParams: params
        });
    
        // Sign the transaction
        const signedTxn = txn.signTxn(Buffer.from(private_key, 'base64'));
    
        // Send the transaction
        try {
            let { txId } = await algodClient.sendRawTransaction(signedTxn).do();
            console.log('Transaction ID:', txId);
    
            // Wait for confirmation
            let confirmedTxn = await algosdk.waitForConfirmation(algodClient, txId, 4);
            console.log('Transaction confirmed in round', confirmedTxn['confirmed-round']);
        } catch (err) {
            console.log('Error submitting transaction:', err);
        }
    }
    
    submitTransaction();
    

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 sendTransaction() method from the AlgodClient class.
  10. Use the waitForConfirmation() 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 algosToMicroalgos() function from the algosdk.util module.
  • the makePaymentTxnWithSuggestedParams() function and waitForConfirmation() function from the algosdk module.
  • The sendRawTransaction() function, which is a method from the AlgodClient class.

In the Algorand SDK, 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 algosToMicroalgos() function, and pass in the amount of Algo as an argument.

The makePaymentTxnWithSuggestedParams() function 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:

    function makePaymentTxnWithSuggestedParams(sender, receiver, amount, closeRemainderTo, note, suggestedParams, rekeyTo) {
        // Returns a transaction object
    }
    

After we define our Payment Transaction parameters, we can then use the signTransaction() 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 sendRawTransaction() function.

The waitForConfirmation() function requires the AlgodClient variable we created, as well as the transaction ID to wait for.

Lastly, the sendRawTransaction() function, which accepts signed transaction objects and outputs the 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
    const rekeyToNewAccountPayment = algosdk.makePaymentTxnWithSuggestedParams(
        senderAddress,
        newAccountAddress,
        0,
        undefined,
        new TextEncoder().encode("Take care of my account for me! I'll be back in a week"),
        params,
        newAccountAddress
    );
    
    const signedRekeyToNewAccountPayment = rekeyToNewAccountPayment.signTxn(senderPrivateKey);
    const rekeyTransactionID = await algodClient.sendRawTransaction(signedRekeyToNewAccountPayment).do();
    await algosdk.waitForConfirmation(algodClient, rekeyTransactionID, 4);
    console.log(rekeyTransactionID);
    
    // New account rekeys back to the original account
    const rekeyBackToOldAccountFromNewAccount = algosdk.makePaymentTxnWithSuggestedParams(
        newAccountAddress,
        senderAddress,
        0,
        undefined,
        new TextEncoder().encode("Sorry! I'm too busy trading this week. Maybe ask PorkChop.algo?"),
        params,
        senderAddress
    );
    
    const signedRekeyBackToOldAccountFromNewAccount = rekeyBackToOldAccountFromNewAccount.signTxn(newAccountPrivateKey);
    const rekeyBackTransactionID = await algodClient.sendRawTransaction(signedRekeyBackToOldAccountFromNewAccount).do();
    await algosdk.waitForConfirmation(algodClient, rekeyBackTransactionID, 4);
    console.log(rekeyBackTransactionID);
    
    // Close Remainder to Transaction
    const closeAccountToNewAccount = algosdk.makePaymentTxnWithSuggestedParams(
        senderAddress,
        newAccountAddress,
        0,
        newAccountAddress,
        new TextEncoder().encode('Take care of my precious Algo!'),
        params
    );
    
    const signedCloseAccountToNewAccount = closeAccountToNewAccount.signTxn(senderPrivateKey);
    const closeAccountTransactionID = await algodClient.sendRawTransaction(signedCloseAccountToNewAccount).do();
    await algosdk.waitForConfirmation(algodClient, closeAccountTransactionID, 4);
    console.log(closeAccountTransactionID);
    

DISCLAIMER: 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 are the minimum balance and transaction fee requirements for Algorand accounts?





Question 4

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





Code Editor