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
- 2. Create a JSON string and Copy into Transaction
- 3. Utility Function to Verify Transaction
- 4. Send Transaction with JSON string to Blockchain
- 5. Read Transaction from Blockchain and Recover JSON Object
- 6. Confirm the Output
- 7. Using Indexer to Query the Note Field
- 8. Completed Code
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.