Create Publication

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Tutorial Thumbnail
Beginner · 30 minutes

Read and Write to the Transaction Note Field with JavaScript

Up to 1kb of arbitrary data can be stored in any Transaction. This data can be stored and read from the transactions note field. This example walks through both the process of writing to and reading from the transaction note field.

Requirements

Background

When creating an application using Algorand, you can use the note field for arbitrary data storage. This data can be up to 1kb in length and the structure can be determined by the application. The complete example on reading and writing a note can be found here and searching on a note using indexer here.

Steps

1. Create an Account and Add Funds

Use goal or the JavaScript SDK to create an account and add funds.

Using goal:

$goal account new -d <your-data-directory> -w <your-wallet>

Using goal or the JavaScript SDK will return an address that we will need to recover in the tutorial. Export the account mnemonic using the following goal command.

 $goal account export -a <account-created-above>  -d <your-data-directory> -w <your-wallet>

Using the JavaScript SDK:

To create accounts using the JavaScript SDK see this tutorial to create a standalone account.

This will export the account mnemonic string of random words that you should copy for later use. Note that this is just a tutorial. Hard coding a mnemonic is not advised and proper key management should be used in production applications.

Use the dispenser to add funds to the account. The TestNet dispenser is here.


Learn More
- Add Funds using Dispenser

2. Create a JSON string and Copy into Transaction

In this tutorial, we are just using a JSON string to represent an object. This can be done using a simple string or structure. Before doing that we must connect to the server and recover the account we created and funded in the previous step. In the code below, specify your token, server and port. Additionally set the mnemonic to the string you recovered in step 1.

const algosdk = require('algosdk');

// const token = "<your-api-token>";
// const server = "<http://your-sever>";
// const port = 8080;

// sandbox
const token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const server = "http://localhost";
const port = 4001;


//Recover the account
var mnemonic = "<Your pass phrase>"
var recoveredAccount = algosdk.mnemonicToSecretKey(mnemonic);
console.log(recoveredAccount.addr);
//check to see if account is valid
var isValid = algosdk.isValidAddress(recoveredAccount.addr);
console.log("Is this a valid address: " + isValid);

//instantiate the algod wrapper
let algodClient = new algosdk.Algodv2(token, server, port);

//submit the transaction
(async () => {

    //Check your balance
    let accountInfo = await algodClient.accountInformation(recoveredAccount.addr).do();
    console.log("Account balance: %d microAlgos", accountInfo.amount);
    // Construct the transaction
    let params = await algodClient.getTransactionParams().do();
    // comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;
    // receiver defined as TestNet faucet address 
    const receiver = "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A";
    let names = '{"firstName":"John", "lastName":"Doe"}';
    const enc = new TextEncoder();
    const note = enc.encode(names);
    console.log(note);      
    let txn = algosdk.makePaymentTxnWithSuggestedParams(recoveredAccount.addr,
        receiver, 1000000, undefined, note, params);

    // Sign the transaction
    let signedTxn = txn.signTxn(recoveredAccount.sk);
    let txId = txn.txID().toString();
    console.log("Signed transaction with txID: %s", txId);

})().catch(e => {
    console.log(e);
});    

3. Utility Function to Verify Transaction

The signed transaction can now be sent to the network. Before doing that we need to add a utility function to the code that will verify the transaction has been written to the blockchain.

Add this waitForConfirmation function to the code.

// Function used to wait for a tx confirmation
const waitForConfirmation = async function (algodclient, txId, timeout) {
    // Wait until the transaction is confirmed or rejected, or until 'timeout'
    // number of rounds have passed.
    //     Args:
    // txId(str): the transaction to wait for
    // timeout(int): maximum number of rounds to wait
    // Returns:
    // pending transaction information, or throws an error if the transaction
    // is not confirmed or rejected in the next timeout rounds
    if (algodclient == null || txId == null || timeout < 0) {
        throw "Bad arguments.";
    }
    let status = (await algodclient.status().do());
    if (status == undefined) throw new Error("Unable to get node status");
    let startround = status["last-round"] + 1;   
    let currentround = startround;

    while (currentround < (startround + timeout)) {
        let pendingInfo = await algodclient.pendingTransactionInformation(txId).do(); if (pendingInfo != undefined) {
            if (pendingInfo["confirmed-round"] !== null && pendingInfo["confirmed-round"] > 0) {
                //Got the completed Transaction
                return pendingInfo;
            }
            else {
                if (pendingInfo["pool-error"] != null && pendingInfo["pool-error"].length > 0) {
                    // If there was a pool error, then the transaction has been rejected!
                    throw new Error("Transaction Rejected" + " pool error" + pendingInfo["pool-error"]);
                }
            }
        } 
        await algodClient.statusAfterBlock(currentround).do();        
        currentround++;
    }
    throw new Error("Pending tx not found in timeout rounds, timeout value = " + timeout);
};

4. Send Transaction with JSON string to Blockchain

At the end of the code, we can add a function call to broadcast the transaction to the network and also wait for the transaction to be confirmed.

    // Submit the transaction
    await algodClient.sendRawTransaction(signedTxn).do();

    // Wait for confirmation
    let confirmedTxn = await waitForConfirmation(algodClient, txId, 4);

5. Read Transaction from Blockchain and Recover JSON Object

Now that the transaction is confirmed, we can modify the code to read the transaction back from the blockchain and recover the person string from the note field. Add this to the bottom of the code in the async method.

    //Got the completed Transaction
    console.log("Transaction " + txId + " confirmed in round " + confirmedTxn["confirmed-round"]);
    let mytxinfo = JSON.stringify(confirmedTxn.txn.txn, undefined, 2);
    console.log("Transaction information: %o", mytxinfo);
    console.log("Note: %s", confirmedTxn.txn.txn.note);
    var string = new TextDecoder().decode(confirmedTxn.txn.txn.note);
    console.log(Uint8Array, string);
    const obj = JSON.parse(string);
    console.log(obj.firstName);

6. Confirm the Output

Your output should look similar to this:

7DCJZKC4JDUKM25W7TDJ5XRTWGUTH6DOG5WARVA47DOCXQOTB4GMLNVW7I
Is this a valid address: true
Account balance: 240897000 microAlgos
Uint8Array(38) [123, 34, 102, 105, 114, 115, 116, 78, 97, 109, 101, 34, 58, 34, 74, 111, 104, 110, 34, 44, 32, 34, 108, 97, 115, 116, 78, 97, 109, 101, 34, 58, 34, 68, 111, 101, 34, 125]

Signed transaction with txID: EJKR3PEMLWC56CYWRCTKKHJGURNPERDEV4IDIN7VRQMQQBNQ4GVA
Transaction EJKR3PEMLWC56CYWRCTKKHJGURNPERDEV4IDIN7VRQMQQBNQ4GVA confirmed in round 10970051
Transaction information: {
  "amt": 1000000,
  "fee": 1000,
  "fv": 10970049,
  "gen": "testnet-v1.0",
  "gh": {
    "0": 72,
    "1": 99,
    "2": 181,
...
  },
  "lv": 10971049,
  "note": {
    "0": 123,
    "1": 34,
    "2": 102,
...
  },
  "rcv": {
    "0": 48,
    "1": 253,
    "2": 204,
 ...
  },
  "snd": {
    "0": 248,
    "1": 196,
    "2": 156,
...
  },
  "type": "pay"
}

Note: Uint8Array(38)

ƒ Uint8Array() {"firstName":"John", "lastName":"Doe"}
John

7. Using Indexer to Query the Note Field

The following complete code can be used to query the note field with Indexer.

// SearchTransactionsNote.js
// requires [email protected] or higher 
// verify installed version
// npm list algosdk
const algosdk = require('algosdk');
const indexer_token = "";
const indexer_server = "http://localhost";
const indexer_port = 8980;

// Instantiate the indexer client wrapper
let indexerClient = new algosdk.Indexer(indexer_token, indexer_server, indexer_port);

(async () => {  
    let names = '{"firstName":"John"';
    const enc = new TextEncoder();
    const note = enc.encode(names);
    const s = Buffer.from(note).toString("base64");
    let transactionInfo = await indexerClient.searchForTransactions()
        .minRound(10894697)
        .notePrefix(s).do();
    console.log("Information for Transaction search: " + JSON.stringify(transactionInfo, undefined, 2));
    // create a buffer
    if (transactionInfo.transactions.length > 0)
    {
        console.log("First Match:"); 
        const buff = Buffer.from(transactionInfo.transactions[0].note, 'base64');
        // decode buffer as UTF-8
        const str = buff.toString('utf-8');
        // print normal string
        console.log(str);        
    }

})().catch(e => {
    console.log(e);
    console.trace();
});

Your output should look similar to:

First Match:
{"firstName":"John", "LastName":"Doe"}

8. Completed Code

The complete example on reading and writing a note can be found here and searching on a note using indexer here.