ASA SDK Usage

Below you will find examples of creating, configuring, transferring, and destroying assets in each of the available SDKs. The example code is separated into snippets categorized by these core functions, but is laid out in order and should be viewed as a single script for each respective language.

For information on installing Go, Java, JavaScript and Python SDKs see Using the SDKs and REST APIs.

  1. Setup: Imports, Global Variables, Helper Functions, etc. (Go, Java, JavaScript, Python)
  2. Create a New Asset (Go, Java, JavaScript, Python)
  3. Configure Asset Manager (Go, Java, JavaScript, Python)
  4. Opt-in to Receive Asset (Go, Java, JavaScript, Python)
  5. Transfer an Asset (Go, Java, JavaScript, Python)
  6. Freeze an Asset (Go, Java, JavaScript, Python)
  7. Revoke an Asset (Go, Java, JavaScript, Python)
  8. Destroy an Asset (Go, Java, JavaScript, Python)

 

A few things to note before getting started...

  • Be sure to update your algod address and token for all examples.
  • There are three private mnemonics shown for demonstration purposes. In practice, you should never reveal these. We recommend changing these to your own mnemonics even when running these examples so you will be in control of asset inflows/outflows. Use the BetaNet dispenser to fund your accounts on BetaNet.
  • When changing the configuration of an asset, you must specify all previous managers even if they will not change. Otherwise they will default to empty. We plan to update this so you only need to specify changed fields in the future.

ASA in Go

Setup: Imports, Global Variables, Helper Functions, etc.

Define algod constants and transaction headers. Define several helper functions that will be used throughout the Go examples.

package main

import (
	"fmt"
	json "encoding/json"
	b64 "encoding/base64"
	"github.com/algorand/go-algorand-sdk/transaction"
	"github.com/algorand/go-algorand-sdk/client/algod"
	"github.com/algorand/go-algorand-sdk/mnemonic"
	"github.com/algorand/go-algorand-sdk/crypto"
)
// UPDATE THESE VALUES
const algodAddress = "ADDRESS"
const algodToken = "TOKEN"

var txHeaders = append([]*algod.Header{}, &algod.Header{"Content-Type", "application/json"})

// Accounts to be used through examples
func loadAccounts() (map[int][]byte, map[int]string){
	// Shown for demonstration purposes. NEVER reveal secret mnemonics in practice.
	// Change these values if you want to use different accounts.
	var pks = map[int]string {
		1: "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM",
		2: "AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU",
		3: "3ZQ3SHCYIKSGK7MTZ7PE7S6EDOFWLKDQ6RYYVMT7OHNQ4UJ774LE52AQCU",
	}

	mnemonic1 := "portion never forward pill lunch organ biology weird catch curve isolate plug innocent skin grunt bounce clown mercy hole eagle soul chunk type absorb trim"
	mnemonic2 := "place blouse sad pigeon wing warrior wild script problem team blouse camp soldier breeze twist mother vanish public glass code arrow execute convince ability there"
	mnemonic3 := "image travel claw climb bottom spot path roast century also task cherry address curious save item clean theme amateur loyal apart hybrid steak about blanket"
	mnemonics := []string{mnemonic1, mnemonic2, mnemonic3}
	var sks = make(map[int][]byte) 
	for i, m := range mnemonics {
		var err error
		sks[i+1], err = mnemonic.ToPrivateKey(m)
		if err != nil {
			fmt.Printf("Issue with account %d private key conversion.", i+1)
		} else {
			fmt.Printf("Loaded Key %d: %s\n", i+1, pks[i+1])
		}
	}
	return sks, pks
}

// Function that waits for a given txId to be confirmed by the network
func waitForConfirmation(algodClient algod.Client, txId string) {
	for {
		b3, err := algodClient.PendingTransactionInformation(txId, txHeaders...)
		if err != nil {
			fmt.Printf("waiting for confirmation... (pool error, if any): %s\n", err)
			continue
		}
		if b3.ConfirmedRound > 0 {
			fmt.Printf("Transaction "+b3.TxID+" confirmed in round %d\n", b3.ConfirmedRound)
			break
		}
	}
}

// Pretty prints Go structs
func PrettyPrint(data interface{}) {
    var p []byte
    //    var err := error
    p, err := json.MarshalIndent(data, "", "\t")
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("%s \n", p)
}

// Main function to demonstrate ASA examples
func main() {
	// Get pre-defined set of keys for example
	sks, pks := loadAccounts()
	
	// Initialize an algodClient
	algodClient, err := algod.MakeClient(algodAddress, algodToken)
	if err != nil {
		return
	}

	// Get network-related transaction parameters and assign
	txParams, err := algodClient.SuggestedParams()
	if err != nil {
		fmt.Printf("error getting suggested tx params: %s\n", err)
		return
    }	
    
    // Initialize transaction parameters for the following examples
	fee := txParams.Fee
	firstRound := txParams.LastRound
	lastRound := txParams.LastRound + 1000 
	genHash := b64.StdEncoding.EncodeToString(txParams.GenesisHash)
	genID := txParams.GenesisID 

Create a New ASA

Account 1 creates an ASA called latinum and sets the manager, reserve, freeze, and clawback addresses to itself.

	// Create an asset
	// Set parameters for asset creation transaction
	creator := pks[1]
	assetName := "latinum"
	unitName := "latinum"
	assetURL := "https://path/to/my/asset/details"
	assetMetadataHash := "thisIsSomeLength32HashCommitment"
	defaultFrozen := false
    decimals := uint32(0)
	totalIssuance := uint64(1000)
	manager := pks[1]
	reserve := pks[1]
	freeze := pks[1]
	clawback := pks[1]
	note := []byte(nil)
	txn, err := transaction.MakeAssetCreateTxn(creator, fee, firstRound, lastRound, note,
	genID, genHash, totalIssuance, decimals, defaultFrozen, manager, reserve, freeze, clawback,
	unitName, assetName, assetURL, assetMetadataHash)
	if err != nil {
		fmt.Printf("Failed to make asset: %s\n", err)
		return
		}
	fmt.Printf("Asset created AssetName: %s\n", txn.AssetConfigTxnFields.AssetParams.AssetName)
	
	txid, stx, err := crypto.SignTransaction(sks[1], txn)
	if err != nil {
		fmt.Printf("Failed to sign transaction: %s\n", err)
		return
		}
	fmt.Printf("Transaction ID: %s\n", txid)
		// Broadcast the transaction to the network
	sendResponse, err := algodClient.SendRawTransaction(stx)
	if err != nil {
		fmt.Printf("failed to send transaction: %s\n", err)
		return
		}
	
	// Wait for transaction to be confirmed
	waitForConfirmation(algodClient, sendResponse.TxID)
		
	// Retrieve asset ID by grabbing the max asset ID
	// from the creator account's holdings. 
	act, err := algodClient.AccountInformation(pks[1], txHeaders...)
	if err != nil {
		fmt.Printf("failed to get account information: %s\n", err)
		return
	}
	assetId := uint64(0)
	for i, _ := range act.AssetParams {
		if i > assetId {
			assetId = i
		}
	}
	fmt.Printf("Asset ID from AssetParams: %d\n", assetId)
	
	// Retrieve asset info.
	assetInfo, err := algodClient.AssetInformation(assetId, txHeaders...)

	// Print asset info for newly created asset.
	PrettyPrint(assetInfo)

Configure Asset Manager

Account 1, the current asset manager, changes the manager of the asset to Account 2.

	// Change Asset Manager from Account 1 to Account 2
	manager = pks[2]
	old_manager := pks[1]

	txn, err = transaction.MakeAssetConfigTxn(old_manager, fee, firstRound, lastRound, note, genID, genHash, assetId, manager, reserve, freeze, clawback)
	if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
	}
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx)
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", sendResponse.TxID)

	// Wait for transaction to be confirmed
	waitForConfirmation(algodClient, sendResponse.TxID)
	// Retrieve asset info.
	assetInfo, err = algodClient.AssetInformation(assetId, txHeaders...)
	// Print asset info showing updated manager address.
	PrettyPrint(assetInfo)


Opt-in to Receive Asset

Account 3 opts-in to receive the newly created asset. We use the MakeAssetAcceptanceTxn wrapper which generates an asset transfer transaction to and from Account 3 for 0 latinum.

	// Account 3 opts in to receive latinum
    // Use previously set transaction parameters and update sending address to account 3
	txn, err = transaction.MakeAssetAcceptanceTxn(pks[3], fee, firstRound, lastRound, note, genID, genHash, assetId)
	if err != nil {
        fmt.Printf("Failed to send transaction MakeAssetAcceptanceTxn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[3], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
	}

    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx)
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", sendResponse.TxID)

	// Wait for transaction to be confirmed
	waitForConfirmation(algodClient, sendResponse.TxID)

	act, err = algodClient.AccountInformation(pks[3], txHeaders...)
    if err != nil {
        fmt.Printf("failed to get account information: %s\n", err)
        return
	}
	PrettyPrint(act.Assets[assetId])

Transfer an Asset

Account 1 transfers 10 latinum to Account 3.

	// Send  10 latinum from Account 1 to Account 3

	sender := pks[1]
	recipient := pks[3]
	amount := uint64(10)
	closeRemainderTo := ""
	txn, err = transaction.MakeAssetTransferTxn(sender, recipient, closeRemainderTo, amount, fee, firstRound, lastRound, note,
        genID, genHash, assetId)
	if err != nil {
        fmt.Printf("Failed to send transaction MakeAssetTransfer Txn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
	}
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx)
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", sendResponse.TxID)

	// Wait for transaction to be confirmed
	waitForConfirmation(algodClient, sendResponse.TxID)

	act, err = algodClient.AccountInformation(pks[3], txHeaders...)
    if err != nil {
        fmt.Printf("failed to get account information: %s\n", err)
        return
	}
	PrettyPrint(act.Assets[assetId])

Freeze an Asset

The freeze account (Account 1) freezes Account 3's holding of the asset.

	// Freeze asset for Account 3.
	newFreezeSetting := true
	target := pks[3]
	txn, err = transaction.MakeAssetFreezeTxn(freeze, fee, firstRound, lastRound, note, genID, genHash, assetId, target, newFreezeSetting)
	if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
	}
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx)
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
	fmt.Printf("Transaction ID raw: %s\n", sendResponse.TxID)
	// Wait for transaction to be confirmed
	waitForConfirmation(algodClient, sendResponse.TxID)

	act, err = algodClient.AccountInformation(pks[3], txHeaders...)
    if err != nil {
        fmt.Printf("failed to get account information: %s\n", err)
        return
	}
	PrettyPrint(act.Assets[assetId])

Revoke an Asset

The clawback address (Account 1) revokes all 10 latinum from Account 3.

	// Revoke an asset
	// The clawback account (Account 1) revokes 10 latinum from Account 3.

	target = pks[3]
	txn, err = transaction.MakeAssetRevocationTxn(clawback, target, creator, amount, fee, firstRound, lastRound, note,
	genID, genHash, assetId)
	if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
	}
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx)
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
	fmt.Printf("Transaction ID raw: %s\n", sendResponse.TxID)
	// Wait for transaction to be confirmed
	waitForConfirmation(algodClient, sendResponse.TxID)

	act, err = algodClient.AccountInformation(pks[3], txHeaders...)
    if err != nil {
        fmt.Printf("failed to get account information: %s\n", err)
        return
	}
	PrettyPrint(act.Assets[assetId])



Destroy an Asset

Since all the funds are back in the creator's account, the Manager (now, Account 2) destroys the Asset. The final curly bracket closes out the main function.


	// Destroy the asset
	// Make sure all funds are back in the creator's account. Then use the
	// Manager account to destroy the asset.
	txn, err = transaction.MakeAssetDestroyTxn(manager, fee, firstRound, lastRound, note, genID, genHash, assetId)
	if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[2], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
	}
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx)
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
	fmt.Printf("Transaction ID raw: %s\n", sendResponse.TxID)
	// Wait for transaction to be confirmed
	waitForConfirmation(algodClient, sendResponse.TxID)
	// Retrieve asset info. This should now throw an error.
	assetInfo, err = algodClient.AssetInformation(assetId, txHeaders...)
	if err != nil {
		fmt.Printf("%s\n", err)
	}
}


ASA in Java

Setup: Imports, Global Variables, Helper Functions, etc.

Include all necessary imports. Define various utility functions to be used throughout examples and hardcode example accounts.

package com.algorand.algosdk.asset;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import com.algorand.algosdk.account.Account;
import com.algorand.algosdk.algod.client.AlgodClient;
import com.algorand.algosdk.algod.client.ApiException;
import com.algorand.algosdk.algod.client.api.AlgodApi;
import com.algorand.algosdk.algod.client.auth.ApiKeyAuth;
import com.algorand.algosdk.algod.client.model.AssetParams;
import com.algorand.algosdk.algod.client.model.TransactionID;
import com.algorand.algosdk.algod.client.model.TransactionParams;
import com.algorand.algosdk.crypto.Address;
import com.algorand.algosdk.crypto.Digest;
import com.algorand.algosdk.transaction.SignedTransaction;
import com.algorand.algosdk.transaction.Transaction;
import com.algorand.algosdk.util.Encoder;

/**
 * Show Creating, modifying, sending and listing assets 
 */
public class AssetExample 
{   
    // Inline class to handle changing block parameters
    // Throughout the example
    static class ChangingBlockParms
    {
        public BigInteger fee; 
        public BigInteger firstRound;  
        public BigInteger lastRound; 
        public String genID;
        public Digest genHash;
        public ChangingBlockParms() {
            this.fee = BigInteger.valueOf(0);
            this.firstRound = BigInteger.valueOf(0);
            this.lastRound = BigInteger.valueOf(0);
            this.genID = "";
            this.genHash = null;
        }
    };
    // Utility function to wait on a transaction to be confirmed    
    private static void waitForTransactionToComplete(AlgodApi algodApiInstance, String txID ) throws Exception{
        while(true) {
            try {
                //Check the pending tranactions
                com.algorand.algosdk.algod.client.model.Transaction b3 = algodApiInstance.pendingTransactionInformation(txID);
                if (b3.getRound() != null && b3.getRound().longValue() > 0) {
                    //Got the completed Transaction
                    System.out.println("Transaction " + b3.getTx() + " confirmed in round " + b3.getRound().longValue());
                    break;
                } 
            } catch (Exception e) {
                throw( e );
            }
        }

    }

    // Utility function to update changing block parameters 
    public static ChangingBlockParms getChangingParms(AlgodApi algodApiInstance) throws Exception{
        ChangingBlockParms cp = new AssetExample.ChangingBlockParms(); 
        try {
            TransactionParams params = algodApiInstance.transactionParams();
            cp.fee = params.getFee();
            cp.firstRound = params.getLastRound();
            cp.lastRound = cp.firstRound.add(BigInteger.valueOf(1000));
            cp.genID = params.getGenesisID();
            cp.genHash = new Digest(params.getGenesishashb64());

        } catch (ApiException e) {
           throw( e );
        }
        return( cp );
    }

    // Utility function for sending a raw signed transaction to the network
    public static TransactionID submitTransaction(AlgodApi algodApiInstance, SignedTransaction signedTx ) throws Exception{
        try {
            // Msgpack encode the signed transaction
            byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTx);
            TransactionID id = algodApiInstance.rawTransaction(encodedTxBytes);
            return( id );
        } catch (ApiException e) {
            throw( e );
        }
    }


    public static void main(String args[]) throws Exception {
        final String ALGOD_API_ADDR = "NODEADDRESS";
        final String ALGOD_API_TOKEN = "NODETOKEN";

        AlgodClient client = (AlgodClient) new AlgodClient().setBasePath(ALGOD_API_ADDR);
        ApiKeyAuth api_key = (ApiKeyAuth) client.getAuthentication("api_key");
        api_key.setApiKey(ALGOD_API_TOKEN);
        AlgodApi algodApiInstance = new AlgodApi(client);

        // Shown for demonstration purposes. NEVER reveal secret mnemonics in practice.
        // These three accounts are for testing purposes
        final String account1_mnemonic = "portion never forward pill lunch organ biology"  
                           + " weird catch curve isolate plug innocent skin grunt" 
                           + " bounce clown mercy hole eagle soul chunk type absorb trim";
        final String account2_mnemonic = "place blouse sad pigeon wing warrior wild script"
                            + " problem team blouse camp soldier breeze twist mother"
                            + " vanish public glass code arrow execute convince ability"
                            + " there";
        final String account3_mnemonic = "image travel claw climb bottom spot path roast "
                            + "century also task cherry address curious save item "
                            + "clean theme amateur loyal apart hybrid steak about blanket";

        Account acct1  = new Account(account1_mnemonic); 
        Account acct2  = new Account(account2_mnemonic);
        Account acct3  = new Account(account3_mnemonic);                           
        // get last round and suggested tx fee
        // We use these to get the latest round and tx fees
        // These parameters will be required before every 
        // Transaction
        // We will account for changing transaction parameters
        // before every transaction in this example
        ChangingBlockParms cp = null;
        try {
            cp = getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }

Create a New Asset

Account 1 creates a new asset called latinum and sets Account 2 as the manager, reserve, freeze, and clawback address.

      // The following parameters are asset specific
        // and will be re-used throughout the example. 

        // Create the Asset
        // Total number of this asset available for circulation
        BigInteger assetTotal = BigInteger.valueOf(10000);
        // Whether user accounts will need to be unfrozen before transacting
        boolean defaultFrozen = false;
        // Decimals specifies the number of digits to display after the decimal
        // place when displaying this asset. A value of 0 represents an asset
        // that is not divisible, a value of 1 represents an asset divisible
        // into tenths, and so on. This value must be between 0 and 19
        Integer assetDecimals = 0;
        // Used to display asset units to user
        String unitName = "LATINUM";
        // Friendly name of the asset
        String  assetName = "latinum";
        // Optional string pointing to a URL relating to the asset 
        String url = "http://this.test.com";
        // Optional hash commitment of some sort relating to the asset. 32 character length.
        String assetMetadataHash = "16efaa3924a6fd9d3a4824799a4ac65d";
        // The following parameters are the only ones
        // that can be changed, and they have to be changed
        // by the current manager
        // Specified address can change reserve, freeze, clawback, and manager
        Address manager  = acct2.getAddress();
        // Specified address is considered the asset reserve
        // (it has no special privileges, this is only informational)
        Address reserve = acct2.getAddress();
        // Specified address can freeze or unfreeze user asset holdings
        Address freeze = acct2.getAddress();
        // Specified address can revoke user asset holdings and send 
        // them to other addresses
        Address clawback = acct2.getAddress();

        Transaction tx = Transaction.createAssetCreateTransaction(acct1.getAddress(), 
        BigInteger.valueOf( 1000 ), cp.firstRound, cp.lastRound, null, cp.genID, 
        cp.genHash, assetTotal, assetDecimals, defaultFrozen, unitName, assetName, url, 
        assetMetadataHash.getBytes(), manager, reserve, freeze, clawback);
        // Update the fee as per what the BlockChain is suggesting
        Account.setFeeByFeePerByte(tx, cp.fee);

        // Sign the Transaction
        SignedTransaction signedTx = acct1.signTransaction(tx);
        // send the transaction to the network and
        // wait for the transaction to be confirmed
        try{
            TransactionID id = submitTransaction( algodApiInstance, signedTx);
            System.out.println( "Transaction ID: " + id );
            waitForTransactionToComplete( algodApiInstance, signedTx.transactionID);
            // Now that the transaction is confirmed we can get the assetID
            com.algorand.algosdk.algod.client.model.Transaction ptx = algodApiInstance.pendingTransactionInformation(id.getTxId());
            assetID = ptx.getTxresults().getCreatedasset();
        } catch (Exception e){
            e.printStackTrace();
            return;
        }
 
        System.out.println( "AssetID = " +  assetID);

Configure Asset Manager

The current manager (Account 2) sets the new manager to Account 1.

        // Change Asset Configuration:
        // Next we will change the asset configuration
        // First we update standard Transaction parameters
        // To account for changes in the state of the blockchain
        try {
            cp = getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }
        // Note that configuration changes must be done by
        // The manager account, which is currently acct2
        // Note in this transaction we are re-using the asset
        // creation parameters and only changing the manager
        // and transaction parameters like first and last round
        tx = Transaction.createAssetConfigureTransaction(acct2.getAddress(), BigInteger.valueOf( 1000 ),
            cp.firstRound, cp.lastRound, null, cp.genID, cp.genHash, assetID, acct1.getAddress(), reserve, freeze, clawback, false);
        // Update the fee as per what the BlockChain is suggesting
        Account.setFeeByFeePerByte(tx, cp.fee);
        // The transaction must be signed by the current manager account
        // We are reusing the signedTx variable from the first transaction in the example    
        signedTx = acct2.signTransaction(tx);
       // send the transaction to the network and
        // wait for the transaction to be confirmed
        try{
            TransactionID id = submitTransaction( algodApiInstance, signedTx);
            System.out.println( "Transaction ID: " + id );
            waitForTransactionToComplete( algodApiInstance, signedTx.transactionID);
        } catch (Exception e){
            e.printStackTrace();
            return;
        }  

        // Next we will list the newly created asset
        // Get the asset information for the newly changed asset
        AssetParams assetInfo = algodApiInstance.assetInformation(assetID);
        //The manager should now be the same as the creator
        System.out.println(assetInfo);


Opt-in to Receive Asset

Account 3 opts-in to receive latinum by issuing a 0 amount transfer of the asset to itself.

        // Opt in to Receiving the Asset
        // Opting in to transact with the new asset
        // All accounts that want recieve the new asset
        // Have to opt in. To do this they send an asset transfer
        // of the new asset to themseleves with an ammount of 0
        // In this example we are setting up the 3rd recovered account to 
        // receive the new asset        
        // First we update standard Transaction parameters
        // To account for changes in the state of the blockchain
        try {
            cp = getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }
        tx = Transaction.createAssetAcceptTransaction(acct3.getAddress(),  BigInteger.valueOf( 1000 ), cp.firstRound, 
        cp.lastRound, null, cp.genID, cp.genHash, assetID);
        // Update the fee based on the network suggested fee
        Account.setFeeByFeePerByte(tx, cp.fee);
        // The transaction must be signed by the current manager account
        // We are reusing the signedTx variable from the first transaction in the example    
        signedTx = acct3.signTransaction(tx);
        // send the transaction to the network and
        // wait for the transaction to be confirmed
        try{
            TransactionID id = submitTransaction( algodApiInstance, signedTx);
            System.out.println( "Transaction ID: " + id );
            waitForTransactionToComplete( algodApiInstance, signedTx.transactionID);
            // We can now list the account information for acct3 
            // and see that it can accept the new asseet
            act = algodApiInstance.accountInformation(acct3.getAddress().toString());
            System.out.println( act );
        } catch (Exception e){
            e.printStackTrace();
            return;
        }  

Transfer an Asset

Account 1 sends 10 latinum to Account 3.

        // Transfer the Asset:
        // Now that account3 can recieve the new asset 
        // we can tranfer assets in from the creator
        // to account3
        // First we update standard Transaction parameters
        // To account for changes in the state of the blockchain
        try {
            cp = getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }
        // Next we set asset xfer specific parameters
        // We set the assetCloseTo to null so we do not close the asset out
        Address assetCloseTo = new Address();
        BigInteger assetAmount = BigInteger.valueOf(10);
        tx = Transaction.createAssetTransferTransaction(acct1.getAddress(), 
            acct3.getAddress(), assetCloseTo, assetAmount, BigInteger.valueOf( 1000 ), 
            cp.firstRound, cp.lastRound, null, cp.genID, cp.genHash, assetID);        
        // Update the fee based on the network suggested fee
        Account.setFeeByFeePerByte(tx, cp.fee);
        // The transaction must be signed by the sender account
        // We are reusing the signedTx variable from the first transaction in the example    
        signedTx = acct1.signTransaction(tx);
        // send the transaction to the network and
        // wait for the transaction to be confirmed
        try{
            TransactionID id = submitTransaction( algodApiInstance, signedTx);
            System.out.println( "Transaction ID: " + id );
            waitForTransactionToComplete( algodApiInstance, signedTx.transactionID);
            // We can now list the account information for acct3 
            // and see that it now has 5 of the new asset
            act = algodApiInstance.accountInformation(acct3.getAddress().toString());
            System.out.println( act.getHolding(assetID).getAmount() );
        } catch (Exception e){
            e.printStackTrace();
            return;
        }


Freeze an Asset

The freeze address (Account 2) freezes Account 3's latinum holdings.

        // Freeze the Asset:
        // The asset was created and configured to allow freezing an account
        // If the freeze address is blank, it will no longer be possible to do this.
        // In this example we will now freeze account3 from transacting with the 
        // The newly created asset. 
        // Thre freeze transaction is sent from the freeze acount
        // Which in this example is account2 
        // First we update standard Transaction parameters
        // To account for changes in the state of the blockchain
        try {
            cp = getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }
        // Next we set asset xfer specific parameters
        boolean freezeState = true;
        // The sender should be freeze account acct2
        // Theaccount to freeze should be set to acct3
        tx = Transaction.createAssetFreezeTransaction(acct2.getAddress(), 
            acct3.getAddress(), freezeState, BigInteger.valueOf( 1000 ), cp.firstRound, 
            cp.lastRound, null, cp.genHash, assetID);
        // Update the fee based on the network suggested fee
        Account.setFeeByFeePerByte(tx, cp.fee);
        // The transaction must be signed by the freeze account acct2
        // We are reusing the signedTx variable from the first transaction in the example    
        signedTx = acct2.signTransaction(tx);
        // send the transaction to the network and
        // wait for the transaction to be confirmed
        try{
            TransactionID id = submitTransaction( algodApiInstance, signedTx);
            System.out.println( "Transaction ID: " + id );
            waitForTransactionToComplete( algodApiInstance, signedTx.transactionID);
            // We can now list the account information for acct3 
            // and see that it now frozen 
            // Note--currently no getter method for frozen state
            act = algodApiInstance.accountInformation(acct3.getAddress().toString());
            System.out.println( act.getHolding(assetID).toString() );
        } catch (Exception e){
            e.printStackTrace();
            return;
        }


Revoke an Asset

The clawback address (Account 2) revokes 10 latinum from Account 3 and places them back with Account 1.

        // Revoke the asset:
        // The asset was also created with the ability for it to be revoked by 
        // clawbackaddress. If the asset was created or configured by the manager
        // not allow this by setting the clawbackaddress to a blank address  
        // then this would not be possible.
        // We will now clawback the 10 assets in account3. Account2
        // is the clawbackaccount and must sign the transaction
        // The sender will be be the clawback adress.
        // the recipient will also be be the creator acct1 in this case  
        // First we update standard Transaction parameters
        // To account for changes in the state of the blockchain
        try {
            cp = getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }
        // Next we set asset xfer specific parameters
        assetAmount = BigInteger.valueOf( 10 );
        tx = Transaction.createAssetRevokeTransaction(acct2.getAddress(), 
        acct3.getAddress(), acct1.getAddress(), assetAmount, BigInteger.valueOf( 1000 ), cp.firstRound, 
        cp.lastRound, null, cp.genID, cp.genHash, assetID);
        // Update the fee based on the network suggested fee
        Account.setFeeByFeePerByte(tx, cp.fee);
        // The transaction must be signed by the clawback account
        // We are reusing the signedTx variable from the first transaction in the example    
        signedTx = acct2.signTransaction(tx);
        // send the transaction to the network and
        // wait for the transaction to be confirmed
        try{
            TransactionID id = submitTransaction( algodApiInstance, signedTx);
            System.out.println( "Transaction ID: " + id );
            waitForTransactionToComplete( algodApiInstance, signedTx.transactionID);
            // We can now list the account information for acct3 
            // and see that it now has 0 of the new asset
            act = algodApiInstance.accountInformation(acct3.getAddress().toString());
            System.out.println( act.getHolding(assetID).getAmount() );
        } catch (Exception e){
            e.printStackTrace();
            return;
        }  


Destroy an Asset

With all the assets back in the creator's account, the manager (Account 1) destroys the asset.

        // Destroy the Asset:
        // All of the created assets should now be back in the creators
        // Account so we can delete the asset.
        // If this is not the case the asset deletion will fail
        // The address for the from field must be the creator
        // First we update standard Transaction parameters
        // To account for changes in the state of the blockchain
        try {
            cp = getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }
        // Next we set asset xfer specific parameters
        // The manager must sign and submit the transaction
        // This is currently set to acct1
        tx = Transaction.createAssetDestroyTransaction(acct1.getAddress(), 
            BigInteger.valueOf( 1000 ), cp.firstRound, cp.lastRound, null, cp.genHash, assetID);
        // Update the fee based on the network suggested fee
        Account.setFeeByFeePerByte(tx, cp.fee);
        // The transaction must be signed by the manager account
        // We are reusing the signedTx variable from the first transaction in the example    
        signedTx = acct1.signTransaction(tx);
        // send the transaction to the network and
        // wait for the transaction to be confirmed
        try{
            TransactionID id = submitTransaction( algodApiInstance, signedTx);
            System.out.println( "Transaction ID: " + id );
            waitForTransactionToComplete( algodApiInstance, signedTx.transactionID);
            // We can now list the account information for acct1 
            // and see that the asset is no longer there
            act = algodApiInstance.accountInformation(acct1.getAddress().toString());
            System.out.println( "Does AssetID: " + assetID + " exist? " + 
                act.getThisassettotal().containsKey(assetID) );
        } catch (Exception e){
            e.printStackTrace();
            return;
        }  
    }
}

ASA in JavaScript

Setup: Imports, Global Variables, Helper Functions, etc.

Initialize network-related transaction parameter variables, define functions to refresh network-parameters, initialize example accounts and instantiate an instance of algod.

//const algosdk = require('algosdk');
//Retrieve the token, server and port values for your installation in the algod.net
//and algod.token files within the data directory
// UPDATE THESE VALUES
const token = "TOKEN";
const server = "SERVER";
const port = PORT;


// Structure for changing blockchain params
var cp = {
    fee: 0, 
    firstRound: 0,  
    lastRound: 0, 
    genID: "",
    genHash: ""    
}
// Utility function to update params from blockchain
var getChangingParms = async function( algodclient ) {
    let params = await algodclient.getTransactionParams();
    cp.firstRound = params.lastRound;
    cp.lastRound = cp.firstRound + parseInt(1000);
    let sfee = await algodclient.suggestedFee();
    cp.fee = sfee.fee;
    cp.genID = params.genesisID;
    cp.genHash = params.genesishashb64;
}

// Function used to wait for a tx confirmation
var waitForConfirmation = async function(algodclient, txId) {
    while (true) {
        b3 = await algodclient.pendingTransactionInformation(txId);
        if (b3.round != null && b3.round > 0) {
            //Got the completed Transaction
            console.log("Transaction " + b3.tx + " confirmed in round " + b3.round);
            break;
        }
    }
};

// Recover accounts used in example
var account1_mnemonic = "portion never forward pill lunch organ biology" +
    " weird catch curve isolate plug innocent skin grunt" +
    " bounce clown mercy hole eagle soul chunk type absorb trim";
var account2_mnemonic = "place blouse sad pigeon wing warrior wild script" +
    " problem team blouse camp soldier breeze twist mother" +
    " vanish public glass code arrow execute convince ability" +
    " there";
var account3_mnemonic = "image travel claw climb bottom spot path roast" +
    " century also task cherry address curious save item" +
    " clean theme amateur loyal apart hybrid steak about blanket"

var recoveredAccount1 = algosdk.mnemonicToSecretKey(account1_mnemonic);
var recoveredAccount2 = algosdk.mnemonicToSecretKey(account2_mnemonic);
var recoveredAccount3 = algosdk.mnemonicToSecretKey(account3_mnemonic);
console.log(recoveredAccount1.addr);
console.log(recoveredAccount2.addr);
console.log(recoveredAccount3.addr);

// Instantiate the algod wrapper
let algodclient = new algosdk.Algod(token, server, port);


Create a New Asset

Account 1 creates an asset called latinum and sets Account 2 as the manager, reserve, freeze, and clawback address.

(async() => {
    // Asset Creation:
    // The first transaciton is to create a new asset
    // Get last round and suggested tx fee
    // We use these to get the latest round and tx fees
    // These parameters will be required before every 
    // Transaction
    // We will account for changing transaction parameters
    // before every transaction in this example
    await getChangingParms(algodclient);
    let note = undefined; // arbitrary data to be stored in the transaction; here, none is stored

    //Asset creation specific parameters
    // The following parameters are asset specific
    // Throughout the example these will be re-used. 
    // We will also change the manager later in the example
    let addr = recoveredAccount1.addr; 
    // Whether user accounts will need to be unfrozen before transacting    
    let defaultFrozen = false;
    // integer number of decimals for asset unit calculation
    let decimals = 0;
    // total number of this asset available for circulation   
    let totalIssuance = 1000; 
    // Used to display asset units to user    
    let unitName = "LATINUM"; 
    // Friendly name of the asset    
    let assetName = "latinum"; 
    // Optional string pointing to a URL relating to the asset
    let assetURL = "http://someurl"; 
    // Optional hash commitment of some sort relating to the asset. 32 character length.
    let assetMetadataHash = "16efaa3924a6fd9d3a4824799a4ac65d"; 
    // The following parameters are the only ones
    // that can be changed, and they have to be changed
    // by the current manager
    // Specified address can change reserve, freeze, clawback, and manager
    let manager = recoveredAccount2.addr; 
    // Specified address is considered the asset reserve
    // (it has no special privileges, this is only informational)
    let reserve = recoveredAccount2.addr;
    // Specified address can freeze or unfreeze user asset holdings 
    let freeze = recoveredAccount2.addr; 
    // Specified address can revoke user asset holdings and send 
    // them to other addresses    
    let clawback = recoveredAccount2.addr; 

    // signing and sending "txn" allows "addr" to create an asset
    let txn = algosdk.makeAssetCreateTxn(addr, cp.fee, cp.firstRound, cp.lastRound, note,
        cp.genHash, cp.genID, totalIssuance, decimals, defaultFrozen, manager, reserve, freeze, 
        clawback, unitName, assetName, assetURL, assetMetadataHash);

    let rawSignedTxn = txn.signTxn(recoveredAccount1.sk)
    let tx = (await algodclient.sendRawTransaction(rawSignedTxn));
    console.log("Transaction : " + tx.txId);
    let assetID = null;
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, tx.txId);
    // Get the new asset's information from the creator account
    let ptx = await algodclient.pendingTransactionInformation(tx.txId);
    assetID = ptx.txresults.createdasset;

Configure Asset Manager

The current manager (Account 2) issues an asset configuration transaction that assigns Account 1 as the new manager.

    // Change Asset Configuration:
    // Change the manager using an asset configuration transaction

    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    await getChangingParms(algodclient);

    // Asset configuration specific parameters
    // all other values are the same so we leave 
    // Them set.
    // specified address can change reserve, freeze, clawback, and manager
    manager = recoveredAccount1.addr;

    // Note that the change has to come from the existing manager
    let ctxn = algosdk.makeAssetConfigTxn(recoveredAccount2.addr, cp.fee, 
        cp.firstRound, cp.lastRound, note, cp.genHash, cp.genID,
        assetID, manager, reserve, freeze, clawback);

    // This transaction must be signed by the current manager
    rawSignedTxn = ctxn.signTxn(recoveredAccount2.sk)
    let ctx = (await algodclient.sendRawTransaction(rawSignedTxn));
    console.log("Transaction : " + ctx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, ctx.txId);

    //Get the asset information for the newly changed asset
    let assetInfo = await algodclient.assetInformation(assetID);
    //The manager should now be the same as the creator
    console.log(assetInfo);



Opt-in to Receive Asset

Account 3 opts-in to receive the new asset by sending a 0 amount transfer of the asset to itself.

    // Opting in to an Asset:
    // Opting in to transact with the new asset
    // Allow accounts that want recieve the new asset
    // Have to opt in. To do this they send an asset transfer
    // of the new asset to themseleves 
    // In this example we are setting up the 3rd recovered account to 
    // receive the new asset
    let sender = recoveredAccount3.addr;
    let recipient = sender;
    // We set revocationTarget to undefined as 
    // This is not a clawback operation
    let revocationTarget = undefined;
    // CloseReaminerTo is set to undefined as
    // we are not closing out an asset
    let closeRemainderTo = undefined;
    // We are sending 0 assets
    amount = 0;

    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    await getChangingParms(algodclient);

    // signing and sending "txn" allows sender to begin accepting asset specified by creator and index
    let opttxn = algosdk.makeAssetTransferTxn(sender, recipient, closeRemainderTo, revocationTarget,
        cp.fee, amount, cp.firstRound, cp.lastRound, note, cp.genHash, cp.genID, assetID);

    // Must be signed by the account wishing to opt in to the asset    
    rawSignedTxn = opttxn.signTxn(recoveredAccount3.sk);
    let opttx = (await algodclient.sendRawTransaction(rawSignedTxn));
    console.log("Transaction : " + opttx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, opttx.txId);

    //You should now see the new asset listed in the account information
    act = await algodclient.accountInformation(recoveredAccount3.addr);
    console.log("Account Information for: " + JSON.stringify(act.assets));


Transfer an Asset

Account 1 sends 10 latinum to Account 3.

    // Transfer New Asset:
    // Now that account3 can recieve the new tokens 
    // we can tranfer tokens in from the creator
    // to account3
    sender = recoveredAccount1.addr;
    recipient = recoveredAccount3.addr;
    revocationTarget = undefined;
    closeRemainderTo = undefined;
    //Amount of the asset to transfer
    amount = 10;

    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    await getChangingParms(algodclient);

    // signing and sending "txn" will send "amount" assets from "sender" to "recipient"
    let xtxn = algosdk.makeAssetTransferTxn(sender, recipient, closeRemainderTo, revocationTarget,
        cp.fee, amount, cp.firstRound, cp.lastRound, note, cp.genHash, cp.genID, assetID);
    // Must be signed by the account sending the asset  
    rawSignedTxn = xtxn.signTxn(recoveredAccount1.sk)
    let xtx = (await algodclient.sendRawTransaction(rawSignedTxn));
    console.log("Transaction : " + xtx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, xtx.txId);

    // You should now see the 10 assets listed in the account information
    act = await algodclient.accountInformation(recoveredAccount3.addr);
    console.log("Account Information for: " + JSON.stringify(act.assets));


Freeze an Asset

The freeze address (Account 2) freezes Account 3's latinum holdings.

    // The asset was created and configured to allow freezing an account
    // If the freeze address is set "", it will no longer be possible to do this.
    // In this example we will now freeze account3 from transacting with the 
    // The newly created asset. 
    // Thre freeze transaction is sent from the freeze acount
    // Which in this example is account2 
    from = recoveredAccount2.addr;
    freezeTarget = recoveredAccount3.addr;
    freezeState = true;

    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    await getChangingParms(algodclient);


    // The freeze transaction needs to be signed by the freeze account
    let ftxn = algosdk.makeAssetFreezeTxn(from, cp.fee, cp.firstRound, cp.lastRound, note, cp.genHash, cp.genID,
        assetID, freezeTarget, freezeState)

    // Must be signed by the freeze account   
    rawSignedTxn = ftxn.signTxn(recoveredAccount2.sk)
    let ftx = (await algodclient.sendRawTransaction(rawSignedTxn));
    console.log("Transaction : " + ftx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, ftx.txId);

    // You should now see the asset is frozen listed in the account information
    act = await algodclient.accountInformation(recoveredAccount3.addr);
    console.log("Account Information for: " + JSON.stringify(act.assets));


Revoke an Asset

The clawback address (Account 2) revokes 10 latinum from Account 3 and places it back with Account 1.

    // Revoke an Asset:
    // The asset was also created with the ability for it to be revoked by 
    // the clawbackaddress. If the asset was created or configured by the manager
    // to not allow this by setting the clawbackaddress to "" then this would 
    // not be possible.
    // We will now clawback the 10 assets in account3. account2
    // is the clawbackaccount and must sign the transaction
    // The sender will be be the clawback adress.
    // the recipient will also be be the creator in this case
    // that is account3
    sender = recoveredAccount2.addr;
    recipient = recoveredAccount1.addr;
    revocationTarget = recoveredAccount3.addr;
    closeRemainderTo = undefined;
    amount = 10;
   
    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    await getChangingParms(algodclient);

    // signing and sending "txn" will send "amount" assets from "revocationTarget" to "recipient",
    // if and only if sender == clawback manager for this asset
    let rtxn = algosdk.makeAssetTransferTxn(sender, recipient, closeRemainderTo, revocationTarget,
        cp.fee, amount, cp.firstRound, cp.lastRound, note, cp.genHash, cp.genID, assetID);
    // Must be signed by the account that is the clawback address    
    rawSignedTxn = rtxn.signTxn(recoveredAccount2.sk)
    let rtx = (await algodclient.sendRawTransaction(rawSignedTxn));
    console.log("Transaction : " + rtx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, rtx.txId);

    // You should now see 0 assets listed in the account information
    // for the third account
    console.log("Asset ID: " + assetID);
    act = await algodclient.accountInformation(recoveredAccount3.addr);
    console.log("Account Information for: " + JSON.stringify(act.assets));



Destroy an Asset

With all assets back in the creator's account, the manaager (Account 1) destroys the asset.

    // Destroy and Asset:
        // All of the created assets should now be back in the creators
        // Account so we can delete the asset.
        // If this is not the case the asset deletion will fail
     
        // First update changing transaction parameters
        // We will account for changing transaction parameters
        // before every transaction in this example
        await getChangingParms(algodclient);
    
    
        // The address for the from field must be the manager account
        // Which is currently the creator addr1
        addr = recoveredAccount1.addr;
    
        // if all assets are held by the asset creator,
        // the asset creator can sign and issue "txn" to remove the asset from the ledger. 
        let dtxn = algosdk.makeAssetDestroyTxn(addr, cp.fee, cp.firstRound, cp.lastRound, note, cp.genHash, cp.genID, assetID);
        // The transaction must be signed by the manager which 
        // is currently set to account1
        rawSignedTxn = dtxn.signTxn(recoveredAccount1.sk)
        let dtx = (await algodclient.sendRawTransaction(rawSignedTxn));
        console.log("Transaction : " + dtx.txId);
        // wait for transaction to be confirmed
        await waitForConfirmation(algodclient, dtx.txId);
    
        // The account3 and account1 should no longer contain the asset as it has been destroyed
        console.log("Asset ID: " + assetID);
        act = await algodclient.accountInformation(recoveredAccount3.addr);
        console.log("Account Information for: " + JSON.stringify(act.assets));
    
    
    })().catch(e => {
        console.log(e);
        console.trace();
    });


ASA in Python

Setup: Imports, Global Variables, Helper Functions, etc.

Define example accounts, instantiate algod, and define utility functions.

import json
from algosdk import account, algod, mnemonic, transaction

# Shown for demonstration purposes. NEVER reveal secret mnemonics in practice. 
# Change these values if you want to use different accounts.
mnemonic1 = "portion never forward pill lunch organ biology weird catch curve isolate plug innocent skin grunt bounce clown mercy hole eagle soul chunk type absorb trim"
mnemonic2 = "place blouse sad pigeon wing warrior wild script problem team blouse camp soldier breeze twist mother vanish public glass code arrow execute convince ability there"
mnemonic3 = "image travel claw climb bottom spot path roast century also task cherry address curious save item clean theme amateur loyal apart hybrid steak about blanket"


# For ease of reference, add account public and private keys to 
# an accounts dict.
accounts = {}
counter = 1
for m in [mnemonic1, mnemonic2, mnemonic3]:
    accounts[counter] = {}
    accounts[counter]['pk'] = mnemonic.to_public_key(m)
    accounts[counter]['sk'] = mnemonic.to_private_key(m)
    counter += 1

# Specify your node address and token. This must be updated.
algod_address = "" # ADD ADDRESS
algod_token = "" # ADD TOKEN

# Initialize an algod client
algod_client = algod.AlgodClient(algod_token, algod_address)

# Get network params for transactions.
params = algod_client.suggested_params()
first = params.get("lastRound")
last = first + 1000
gen = params.get("genesisID")
gh = params.get("genesishashb64")
min_fee = params.get("minFee")

# Utility function to wait for a transaction to be confirmed by network
def wait_for_tx_confirmation(txid):
    """Wait until the transaction's round info is confirmed, i.e. no longer 0"""
    pendinginfo = algod_client.pending_transaction_info(txid)
    while pendinginfo['round'] == 0:
        pendinginfo = algod_client.pending_transaction_info(txid)

Create a New Asset

Account 1 creates a new asset called "latinum" with 1000 units total, assigning itself as the manager, reserve address, freezer address, and clawback address.

# Configure fields for creating the asset.
data = {
    "sender": accounts[1]['pk'],
    "fee": min_fee,
    "first": first,
    "last": last,
    "gh": gh,
    "total": 1000,
    "default_frozen": False,
    "unit_name": "LATINUM",
    "asset_name": "latinum",
    "manager": accounts[1]['pk'],
    "reserve": accounts[1]['pk'],
    "freeze": accounts[1]['pk'],
    "clawback": accounts[1]['pk'],
    "url": "https://path/to/my/asset/details",
    "flat_fee": True
    "decimals": 0
}

# Construct Asset Creation transaction
txn = transaction.AssetConfigTxn(**data)

# Sign with secret key of creator
stxn = txn.sign(accounts[1]['sk'])

# Send the transaction to the network and retrieve the txid.
txid = algod_client.send_transaction(stxn)
print(txid)

# Retrieve the asset ID of the newly created asset by first
# ensuring that the creation transaction was confirmed,
# then pulling account info of the creator and grabbing the 
# asset with the max asset ID. 

# Wait for the transaction to be confirmed
wait_for_tx_confirmation(txid)

try:
    # Pull account info for the creator
    account_info = algod_client.account_info(accounts[1]['pk'])
    # Get max asset ID
    asset_id = max(map(lambda x: int(x), account_info.get('thisassettotal').keys()))
    print("Asset ID: {}".format(asset_id))
    print(json.dumps(account_info['thisassettotal'][str(asset_id)], indent=4))
except Exception as e:
    print(e)

Configure Asset Manager

Change the manager address of the asset from Account 1 to Account 2.

# Update manager address.
# Keep reserve, freeze, and clawback address same as before, i.e. account 1
data = {
    "sender": accounts[1]['pk'],
    "fee": min_fee,
    "first": first,
    "last": last,
    "gh": gh,
    "index": asset_id,
    "manager": accounts[2]['pk'],
    "reserve": accounts[1]['pk'],
    "freeze": accounts[1]['pk'],
    "clawback": accounts[1]['pk'],
    "flat_fee": True
}
txn = transaction.AssetConfigTxn(**data)
stxn = txn.sign(accounts[1]['sk'])
txid = algod_client.send_transaction(stxn)
print(txid)

# Wait for the transaction to be confirmed
wait_for_tx_confirmation()

# Check asset info to view change in management.
asset_info = algod_client.asset_info(asset_id)
print(json.dumps(asset_info, indent=4))


Opt-in to Receive Asset

Account 2 opts in to receiving latinum by sendiinig a 0 amount transaction of the asset to itself.

# Check if asset_id is in account 2's asset holdings prior to opt-in
account_info = algod_client.account_info(accounts[2]['pk'])
holding = None
if 'assets' in account_info:
    holding = account_info['assets'].get(str(asset_id))

if not holding:
    # Get latest network parameters
    data = {
        "sender": accounts[2]['pk'],
        "fee": min_fee,
        "first": first,
        "last": last,
        "gh": gh,
        "receiver": accounts[2]["pk"],
        "amt": 0,
        "index": asset_id,
        "flat_fee": True
    }

    # Use the AssetTransferTxn class to transfer assets
    txn = transaction.AssetTransferTxn(**data)
    stxn = txn.sign(accounts[2]['sk'])
    txid = algod_client.send_transaction(stxn)
    print(txid)
    # Wait for the transaction to be confirmed
    wait_for_tx_confirmation(txid)
    # Now check the asset holding for that account. 
    # This should now show a holding with a balance of 0.
    account_info = algod_client.account_info(accounts[2]['pk'])
    print(json.dumps(account_info['assets'][str(asset_id)], indent=4))

Transfer an Asset

Account 1 sends 10 latinum to Account 2.

data = {
    "sender": accounts[1]['pk'],
    "fee": min_fee,
    "first": first,
    "last": last,
    "gh": gh,
    "receiver": accounts[2]["pk"],
    "amt": 10,
    "index": asset_id,
    "flat_fee": True
}

txn = transaction.AssetTransferTxn(**data)
stxn = txn.sign(accounts[1]['sk'])
txid = algod_client.send_transaction(stxn)
print(txid)
# Wait for the transaction to be confirmed
wait_for_tx_confirmation(txid)
# The balance should now be 10.
account_info = algod_client.account_info(accounts[2]['pk'])
print(json.dumps(account_info['assets'][str(asset_id)], indent=4))

Freeze an Asset

The freeze address (Account 1) freezes Account 2's latinum holdings.

data = {
    "sender": accounts[1]['pk'],
    "fee": min_fee,
    "first": first,
    "last": last,
    "gh": gh,
    "receiver": accounts[2]["pk"],
    "amt": 10,
    "index": asset_id,
    "flat_fee": True
}

txn = transaction.AssetTransferTxn(**data)
stxn = txn.sign(accounts[1]['sk'])
txid = algod_client.send_transaction(stxn)
print(txid)
# Wait for the transaction to be confirmed
wait_for_tx_confirmation(txid)
# The balance should now be 10.
account_info = algod_client.account_info(accounts[2]['pk'])
print(json.dumps(account_info['assets'][str(asset_id)], indent=4))

Revoke an Asset

The clawback account (Account 1) revokes 10 latinum from Account 2 and puts it into its own account.

data = {
    "sender": accounts[1]['pk'],
    "fee": min_fee,
    "first": first,
    "last": last,
    "gh": gh,
    "receiver": accounts[1]["pk"],
    "amt": 10,
    "index": asset_id,
    "revocation_target": accounts[2]['pk'],
    "flat_fee": True
}

txn = transaction.AssetTransferTxn(**data)
stxn = txn.sign(accounts[1]['sk'])
txid = algod_client.send_transaction(stxn)
print(txid)
# Wait for the transaction to be confirmed
wait_for_tx_confirmation(txid)
# The balance of account 2 should now be 0.
account_info = algod_client.account_info(accounts[2]['pk'])
print(json.dumps(account_info['assets'][str(asset_id)], indent=4))
# The balance of account 1 should increase by 10 to 1000.
account_info = algod_client.account_info(accounts[1]['pk'])
print(json.dumps(account_info['assets'][str(asset_id)], indent=4))

Destroy an Asset

The manager (Account 2) destroys the asset by sending an asset configuration transacton with only the asset ID specified. This works because the sending account is the manager and all of the asset is returned to the creatoor.

data = {
    "sender": accounts[2]['pk'],
    "fee": min_fee,
    "first": first,
    "last": last,
    "gh": gh,
    "index": asset_id,
    "flat_fee": True
}

# Construct Asset Creation transaction
txn = transaction.AssetConfigTxn(**data)

# Sign with secret key of creator
stxn = txn.sign(accounts[2]['sk'])

# Send the transaction to the network and retrieve the txid.
txid = algod_client.send_transaction(stxn)
print(txid)

# Wait for the transaction to be confirmed
wait_for_tx_confirmation(txid)

# This should raise an exception since the asset was deleted.
try:
    asset_info = algod_client.asset_info(asset_id)
except Exception as e:
    print(e)