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 Python

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

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

Using goal:

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

Using goal or the Python 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 Python SDK:

To create accounts using the Python 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 a person object. This can be done using a simple string. This can then be encoded into the transaction by using the encode method. 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.

import json
import base64
from algosdk.v2client import algod
from algosdk import mnemonic
from algosdk.future.transaction import PaymentTxn

def send_note():
    # Use sandbox or your address and token
    algod_address = "http://localhost:4001"
    algod_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    algod_client = algod.AlgodClient(algod_token, algod_address)

    passphrase = "<your-25-word-mnemonic>"
    private_key = mnemonic.to_private_key(passphrase)
    my_address = mnemonic.to_public_key(passphrase)
    print("My address: {}".format(my_address))
    params = algod_client.suggested_params()
    # comment out the next two (2) lines to use suggested fees
    params.flat_fee = True
    params.fee = 1000
    note = '{"firstName":"John", "LastName":"Doe"}'.encode()
    receiver = "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A"

    unsigned_txn = PaymentTxn(my_address, params, receiver, 1000000, None, note)

    # sign transaction
    signed_txn = unsigned_txn.sign(private_key)

send_note()

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 wait_for_confirmation function to the code

# utility for waiting on a transaction confirmation
def wait_for_confirmation(client, transaction_id, timeout):
    """
    Wait until the transaction is confirmed or rejected, or until 'timeout'
    number of rounds have passed.
    Args:
        transaction_id (str): the transaction to wait for
        timeout (int): maximum number of rounds to wait    
    Returns:
        dict: pending transaction information, or throws an error if the transaction
            is not confirmed or rejected in the next timeout rounds
    """
    start_round = client.status()["last-round"] + 1;
    current_round = start_round

    while current_round < start_round + timeout:
        try:
            pending_txn = client.pending_transaction_info(transaction_id)
        except Exception:
            return 
        if pending_txn.get("confirmed-round", 0) > 0:
            return pending_txn
        elif pending_txn["pool-error"]:  
            raise Exception(
                'pool error: {}'.format(pending_txn["pool-error"]))
        client.status_after_block(current_round)                     
        current_round += 1
    raise Exception(
        'pending tx not found in timeout rounds, timeout value = : {}'.format(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.

    # sign transaction
    signed_txn = unsigned_txn.sign(private_key)
    # send transaction
    txid = algod_client.send_transaction(signed_txn)
    print("Send transaction with txID: {}".format(txid))

    # wait for confirmation
    try:
        confirmed_txn = wait_for_confirmation(algod_client, txid, 4)  
    except Exception as err:
        print(err)
        return

5. Read Transaction from Blockchain and Recover JSON string

Now that the transaction is confirmed, we can modify the code to read the transaction returned from the wait_for_confirmation method and recover the person string from the note field using the decode method. Add this to the bottom of the code in the send_note method.

    print("txID: {}".format(txid), " confirmed in round: {}".format(
        confirmed_txn.get("confirmed-round", 0)))       
    print("Transaction information: {}".format(
        json.dumps(confirmed_txn, indent=2)))
    print("Decoded note: {}".format(base64.b64decode(
        confirmed_txn["txn"]["txn"]["note"]).decode()))        
    person_dict = json.loads(base64.b64decode(
        confirmed_txn["txn"]["txn"]["note"]).decode())
    print("First Name = {}".format(person_dict['firstName']))

6. Confirm the Output

Your output should look similar to this:

My address: 7DCJZKC4JDUKM25W7TDJ5XRTWGUTH6DOG5WARVA47DOCXQOTB4GMLNVW7I
Signed transaction with txID: IHWWKMQOA4PTM4XAM4VVQBDI2ZRV3DUHEHNC2WLTNAET3RBJZJLQ
txID: IHWWKMQOA4PTM4XAM4VVQBDI2ZRV3DUHEHNC2WLTNAET3RBJZJLQ  confirmed in round: 10829545
Transaction information: {
  "confirmed-round": 10829545,
  "pool-error": "",
  "txn": {
    "sig": "tr2fP2jvEKqGFZYSB4ml8eK+B4COVXwBiWKbmsN6pYA77JXT7ngol2grzGh6y/z+2Uf5DmH2mNvtSfnNqX/tAg==",
    "txn": {
      "amt": 1000000,
      "fee": 1000,
      "fv": 10829543,
      "gen": "testnet-v1.0",
      "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=",
      "lv": 10830543,
      "note": "eyJmaXJzdE5hbWUiOiJKb2huIiwgIkxhc3ROYW1lIjoiRG9lIn0=",
      "rcv": "GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A",
      "snd": "7DCJZKC4JDUKM25W7TDJ5XRTWGUTH6DOG5WARVA47DOCXQOTB4GMLNVW7I",
      "type": "pay"
    }
  }
}
Decoded note: {"firstName":"John", "LastName":"Doe"}
First Name = John

7. Using Indexer to Query the Note Field

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

# search_transactions_note.py
import base64
import json
# requires Python SDK version 1.3 or higher
from algosdk.v2client import indexer

# instantiate indexer client
myindexer = indexer.IndexerClient(indexer_token="", indexer_address="http://localhost:8980")

import base64

note_prefix = '{"firstName":"John"'.encode()
response = myindexer.search_transactions(
    note_prefix=note_prefix, min_round=10968688)

print("note_prefix = " +
      json.dumps(response, indent=2, sort_keys=True))
# print first note that matches
if (len(response["transactions"]) > 0):
    print("Decoded note: {}".format(base64.b64decode(
        response["transactions"][0]["note"]).decode()))
    person_dict = json.loads(base64.b64decode(
        response["transactions"][0]["note"]).decode())
    print("First Name = {}".format(person_dict['firstName']))

8. Completed Code

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