Read and Write to the Transaction Note Field with Java
Up to 1kb of arbitrary data can be stored in any Transaction. This data can be stored and read from the transactions note field. If this data is encoded using the SDKs encode function you can decode the data from the note field using the decode function. 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 SDKs provide methods to properly encode and decode this data. The complete example on reading and writing a note can be found here.
Steps
- 1. Create an Account and Add Funds
- 2. Create a Person Object and Copy into Transaction
- 3. Utility Function to Verify Transaction and Print
- 4. Send Transaction with Note to Blockchain
- 5. Read Transaction from Blockchain and Recover Person 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 Java SDK to create an account and add funds.
Using goal:
$goal account new -d <your-data-directory> -w <your-wallet>
Using goal or the Java 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 Java SDK:
To create accounts using the Java 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 Person Object and Copy into Transaction
In this tutorial, we are just using a string for the note field. 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.
package com.algorand.javatest.firsttransaction;
import com.algorand.algosdk.account.Account;
import com.algorand.algosdk.transaction.SignedTransaction;
import com.algorand.algosdk.transaction.Transaction;
import com.algorand.algosdk.util.Encoder;
import com.algorand.algosdk.v2.client.common.AlgodClient;
import com.algorand.algosdk.v2.client.common.Response;
import com.algorand.algosdk.v2.client.model.NodeStatusResponse;
import com.algorand.algosdk.v2.client.model.PendingTransactionResponse;
import com.algorand.algosdk.v2.client.model.PostTransactionsResponse;
import org.json.JSONObject;
public class NoteField {
public AlgodClient client = null;
public AlgodClient client = null;
/**
* Initialize an algod client
*/
private AlgodClient connectToNetwork() {
final String ALGOD_API_ADDR = "localhost";
final Integer ALGOD_PORT = 4001;
final String ALGOD_API_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
AlgodClient client = new AlgodClient(ALGOD_API_ADDR, ALGOD_PORT, ALGOD_API_TOKEN);
return client;
}
/**
* note field example.
*/
public void gettingStartedNoteFieldExample() throws Exception {
if (client == null) {
this.client = connectToNetwork();
}
// Import your private key mnemonic and address
final String PASSPHRASE = "patrol target joy dial ethics flip usual fatigue bulb security prosper brand coast arch casino burger inch cricket scissors shoe evolve eternal calm absorb school";
com.algorand.algosdk.account.Account myAccount = new Account(PASSPHRASE);
System.out.println("My Address: " + myAccount.getAddress());
printBalance(myAccount);
// Construct the transaction
final String RECEIVER = "L5EUPCF4ROKNZMAE37R5FY2T5DF2M3NVYLPKSGWTUKVJRUGIW4RKVPNPD4";
// add some notes to the transaction
String note = "showing prefix and more";
Transaction txn = Transaction.PaymentTransactionBuilder()
.sender(myAccount.getAddress())
.noteUTF8(note)
.amount(100000)
.receiver(RECEIVER)
.lookupParams(client) // query algod for firstValid, fee, etc
.build();
}
public static void main(String args[]) throws Exception {
NoteField t = new NoteField();
t.gettingStartedNoteFieldExample();
}
}
3. Utility Function to Verify Transaction and Print
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
/**
* utility function to wait on a transaction to be confirmed
* the timeout parameter indicates how many rounds do you wish to check pending transactions for
*/
public PendingTransactionResponse waitForConfirmation(AlgodClient myclient, String txID, Integer timeout)
throws Exception {
if (myclient == null || txID == null || timeout < 0) {
throw new IllegalArgumentException("Bad arguments for waitForConfirmation.");
}
Response<NodeStatusResponse> resp = myclient.GetStatus().execute();
if (!resp.isSuccessful()) {
throw new Exception(resp.message());
}
NodeStatusResponse nodeStatusResponse = resp.body();
Long startRound = nodeStatusResponse.lastRound+1;
Long currentRound = startRound;
while (currentRound < (startRound + timeout)) {
// Check the pending transactions
Response<PendingTransactionResponse> resp2 = myclient.PendingTransactionInformation(txID).execute();
if (resp2.isSuccessful()) {
PendingTransactionResponse pendingInfo = resp2.body();
if (pendingInfo != null) {
if (pendingInfo.confirmedRound != null && pendingInfo.confirmedRound > 0) {
// Got the completed Transaction
return pendingInfo;
}
if (pendingInfo.poolError != null && pendingInfo.poolError.length() > 0) {
// If there was a pool error, then the transaction has been rejected!
throw new Exception("The transaction has been rejected with a pool error: " + pendingInfo.poolError);
}
}
}
Response<NodeStatusResponse> resp3 = myclient.WaitForBlock(currentRound).execute();
if (!resp3.isSuccessful()) {
throw new Exception(resp3.message());
}
currentRound++;
}
throw new Exception("Transaction not confirmed after " + timeout + " rounds!");
}
/**
* Print account balance.
*/
private void printBalance(Account myAccount) throws Exception {
Response<com.algorand.algosdk.v2.client.model.Account> resp = client.AccountInformation(myAccount.getAddress())
.execute();
if (!resp.isSuccessful()) {
throw new Exception(resp.message());
}
com.algorand.algosdk.v2.client.model.Account accountInfo = resp.body();
System.out.println("Account Balance: " + accountInfo.amount + " microAlgos");
}
4. Send Transaction with Note to Blockchain
At the end of the code in gettingStartedNoteFieldExample
, we can add a function call to broadcast the transaction to the network and also wait for the transaction to be confirmed.
// Sign the transaction
SignedTransaction signedTxn = myAccount.signTransaction(txn);
System.out.println("Signed transaction with txid: " + signedTxn.transactionID);
// Submit the transaction to the network
byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTxn);
Response<PostTransactionsResponse> resp = client.RawTransaction()
.rawtxn(encodedTxBytes)
.execute();
if (!resp.isSuccessful()) {
throw new Exception(resp.message());
}
String id = resp.body().txId;
// Wait for transaction confirmation
PendingTransactionResponse pTrx = waitForConfirmation(client, id, 4);
System.out.println("Transaction " + id + " confirmed in round " + pTrx.confirmedRound);
5. Read Transaction from Blockchain and Recover Person Object
Now that the transaction is confirmed, we can modify the code to read the transaction returned from the waitForConfirmation
method and decode the string from the note field. Add this to the bottom of the code in the gettingStartedNoteFieldExample
method.
// Read the transaction
JSONObject jsonObj = new JSONObject(pTrx.toString());
System.out.println("Transaction information (with notes): " + jsonObj.toString(2));
System.out.println("Decoded note: " + new String(pTrx.txn.tx.note));
printBalance(myAccount);
6. Confirm the Output
Your output should look similar to this:
My Address: NI2EDLP2KZYH6XYLCEZSI5SSO2TFBYY3ZQ5YQENYAGJFGXN4AFHPTR3LXU
Account Balance: 330437399microAlgos
Signed transaction with txid: ZIFJRHFHBPUMYDB7MGQ732A3NSH64NHP5LUPSZYGVA2EJTLQXNFQ
Transaction ZIFJRHFHBPUMYDB7MGQ732A3NSH64NHP5LUPSZYGVA2EJTLQXNFQ confirmed in round 11678898
Transaction information (with notes): {
"confirmed-round": 11678898,
"pool-error": "",
"txn": {
"sig": "+suTe/IiONQQ711Iz1Vv+AJQa6VM+Rya4W1yYwf6DDVepfBTUylIQ9t+vhLJrFwiowhBOTwXRsh3AT1tf8QDDA==",
"txn": {
"fv": 11678896,
"gen": "testnet-v1.0",
"gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
"note": "c2hvd2luZyBwcmVmaXggYW5kIG1vcmU=",
"rcv": "X0lHiLyLlNywBN/j0uNT6MumbbXC3qka06KqmNDItyI=",
"fee": 1000,
"snd": "ajRBrfpWcH9fCxEzJHZSdqZQ4xvMO4gRuAGSU128AU4=",
"amt": 100000,
"lv": 11679896,
"type": "pay"
}
},
"local-state-delta": [],
"global-state-delta": []
}
Decoded note: showing prefix and more
Account Balance: 330336399 microAlgos
7. Using Indexer to Query the Note Field
The following complete code can be used to query the note field with Indexer.
package com.algorand.javatest.indexer;
import com.algorand.algosdk.v2.client.common.IndexerClient;
import com.algorand.algosdk.v2.client.common.Response;
import com.algorand.algosdk.v2.client.model.Transaction;
import com.algorand.algosdk.v2.client.model.TransactionsResponse;
import org.json.JSONObject;
public class SearchTransactionsNote {
/**
* Initialize connection to indexer.
*/
private static IndexerClient connectToNetwork() {
final String INDEXER_API_ADDR = "localhost";
final int INDEXER_API_PORT = 8981;
return new IndexerClient(INDEXER_API_ADDR, INDEXER_API_PORT);
}
/**
* Indexer search transaction notes example.
*/
public static void main(String args[]) throws Exception {
IndexerClient indexerClientInstance = connectToNetwork();
Response<TransactionsResponse> resp = indexerClientInstance.searchForTransactions()
.notePrefix("showing prefix".getBytes())
.minRound(11551185L)
.execute();
if (!resp.isSuccessful()) {
throw new Exception(resp.message());
}
// pretty print json
JSONObject jsonObj = new JSONObject(resp.toString());
System.out.println("Transaction Info: " + jsonObj.toString(2));
int i = 0;
for (Transaction tx : resp.body().transactions) {
i++;
System.out.println("Transaction " + i);
System.out.println(" Note Info: " + new String(tx.note));
}
}
}
8. Completed Code
The complete example on reading and writing a note can be found here and searching on a note using indexer here.