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 · 1 hour

Working with ASA Using JavaScript

This tutorial demonstrates the steps involved in creating a basic Algorand Standard Asset (ASA) using JavaScript SDK. This tutorial includes Asset Reconfigure, Opt-In, Asset Transfer, Asset Freeze, Asset Revoke, and Asset Destroy.

Completed code for this tutorial can be found here AssetExample.js.

Requirements

Optional:

Background

Algorand Standard Assets (ASA) allow developers to create fungible and non-fungible assets with a few method calls. ASA’s are highly customizable with parameters that allow developers to define total issuance of an asset, name of asset, units of an asset, as well as access controls privileges over an asset.

There are a few items to be aware of before getting started with assets as documented in the ASA feature guide overview on the developer portal.

Also, see more information on decimals and fees.

Steps

Part 1: Create an Algorand Standard Asset

This part demonstrates the steps involved in creating an asset.

Step 1-1. Instantiate Algod Wrapper and Recover Accounts

In this step, we are passing in the token, server, and port values from a local node or local sandbox instance. You can also connect to remote node using a third-party service.

Hardcoding and recovering accounts in this way is not advised, for security purposes, but is sufficient for this tutorial.

This tutorial will use TestNet accounts that have been pre-created from the Create Accounts tutorial.

Note: If you have not already done so, use this tutorial to Create Accounts

Be sure to dispense Algos to these accounts before continuing, using the TestNet Dispenser.

const algosdk = require('algosdk');

// UPDATE THESE VALUES
// Sandbox
const token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const server = "http://localhost";
const port = 4001;

var account1_mnemonic = "PASTE your phrase for account 1";
var account2_mnemonic = "PASTE your phrase for account 2";
var account3_mnemonic = "PASTE your phrase for account 3";

var recoveredAccount1 = algosdk.mnemonicToSecretKey(account1_mnemonic);
var recoveredAccount2 = algosdk.mnemonicToSecretKey(account2_mnemonic);
var recoveredAccount3 = algosdk.mnemonicToSecretKey(account3_mnemonic);
console.log("Account One: " + recoveredAccount1.addr);
console.log("Account Two: " + recoveredAccount2.addr);
console.log("Account Three: " + recoveredAccount3.addr);

// Instantiate the algod wrapper

let algodclient = new algosdk.Algodv2(token, server, port);


Learn More
- Connect to an Algorand Node
- Algorand Node Setup

Learn More
- Algorand Standalone Account Creation Methods

Step 1-2. Print Accounts, utility functions and structures

Add these functions.

// Function used to wait for a tx confirmation
const waitForConfirmation = async function (algodclient, txId) {
    let response = await algodclient.status().do();
    let lastround = response["last-round"];
    while (true) {
        const pendingInfo = await algodclient.pendingTransactionInformation(txId).do();
        if (pendingInfo["confirmed-round"] !== null && pendingInfo["confirmed-round"] > 0) {
            //Got the completed Transaction
            console.log("Transaction " + txId + " confirmed in round " + pendingInfo["confirmed-round"]);
            break;
        }
        lastround++;
        await algodclient.statusAfterBlock(lastround).do();
    }
};


// Function used to print created asset for account and assetid
const printCreatedAsset = async function (algodclient, account, assetid) {
    // note: if you have an indexer instance available it is easier to just use this
    //     let accountInfo = await indexerClient.searchAccounts()
    //    .assetID(assetIndex).do();
    // and in the loop below use this to extract the asset for a particular account
    // accountInfo['accounts'][idx][account]);
    let accountInfo = await algodclient.accountInformation(account).do();
    for (idx = 0; idx < accountInfo['created-assets'].length; idx++) {
        let scrutinizedAsset = accountInfo['created-assets'][idx];
        if (scrutinizedAsset['index'] == assetid) {
            console.log("AssetID = " + scrutinizedAsset['index']);
            let myparms = JSON.stringify(scrutinizedAsset['params'], undefined, 2);
            console.log("parms = " + myparms);
            break;
        }
    }
};

// Function used to print asset holding for account and assetid
const printAssetHolding = async function (algodclient, account, assetid) {
    // note: if you have an indexer instance available it is easier to just use this
    //     let accountInfo = await indexerClient.searchAccounts()
    //    .assetID(assetIndex).do();
    // and in the loop below use this to extract the asset for a particular account
    // accountInfo['accounts'][idx][account]);
    let accountInfo = await algodclient.accountInformation(account).do();
    for (idx = 0; idx < accountInfo['assets'].length; idx++) {
        let scrutinizedAsset = accountInfo['assets'][idx];
        if (scrutinizedAsset['asset-id'] == assetid) {
            let myassetholding = JSON.stringify(scrutinizedAsset, undefined, 2);
            console.log("assetholdinginfo = " + myassetholding);
            break;
        }
    }
};

Step 1-3. Create Transaction

We need to define the Create Asset Transaction parameters and Create the Asset.

  • Configure fields for creating the asset.

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

Copy off the AssetID, from this step as it will be used in subsequent Parts.

(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
    let params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;
    console.log(params);
    let note = algosdk.encodeObj("showing prefix");
    // 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.makeAssetCreateTxnWithSuggestedParams(addr, note,
         totalIssuance, decimals, defaultFrozen, manager, reserve, freeze,
        clawback, unitName, assetName, assetURL, assetMetadataHash, params);


Learn More
- Algorand Asset Parameters
- Common Transaction Fields

Step 1-4. Sign Transaction

Sign with the secret key of the creator, Account 1.

    let rawSignedTxn = txn.signTxn(recoveredAccount1.sk)

Step 1-5. Send Create ASA Transaction to the Blockchain and print the Tx ID

Send the transaction to the network and retrieve the txid. Copy off the Asset ID for use in subsequent parts.

    let tx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    console.log("Asset Creation Txn : " + 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;
    console.log("AssetID = " + assetID);
        // Get the new asset's information from the creator account
    let ptx = await algodclient.pendingTransactionInformation(tx.txId).do();
    assetID = ptx["asset-index"];
   // console.log("AssetID = " + assetID);

    await printCreatedAsset(algodclient, recoveredAccount1.addr, assetID);
    await printAssetHolding(algodclient, recoveredAccount1.addr, assetID);

Step 1-6. Check the transaction on a block explorer

Once you’ve completed these steps your output should look like this:

    Transaction: BQMRGV6VZLXXPI4OEEJB6OGFRVJQEKWFJULNPMGVF25H7WE6EUQA
    Transaction BQMRGV6VZLXXPI4OEEJB6OGFRVJQEKWFJULNPMGVF25H7WE6EUQA confirmed in round 3961853
    AssetID = 2653785
    parms = {
         "clawback": "AK6Q33PDO4RJZQPHEMODC6PUE5AR2UD4FBU6TNEJOU4UR4KC6XL5PWW5K4",
         "creator": "ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ",
         "decimals": 0,
         "default-frozen": false,
         "freeze": "AK6Q33PDO4RJZQPHEMODC6PUE5AR2UD4FBU6TNEJOU4UR4KC6XL5PWW5K4",
         "manager": "AK6Q33PDO4RJZQPHEMODC6PUE5AR2UD4FBU6TNEJOU4UR4KC6XL5PWW5K4",
         "metadata-hash": "MTZlZmFhMzkyNGE2ZmQ5ZDNhNDgyNDc5OWE0YWM2NWQ=",
         "name": "latinum",
         "reserve": "AK6Q33PDO4RJZQPHEMODC6PUE5AR2UD4FBU6TNEJOU4UR4KC6XL5PWW5K4",
         "total": 1000,
         "unit-name": "LATINUM",
         "url": "http://someurl"
     }

     assetholdinginfo = {
         "amount": 1000,
         "asset-id": 2653785,
         "creator": "ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ",
         "is-frozen": false
     }

You can check the asset creation transaction on a block explorer for reference.

Note: Copy off the AssetID, as it will be used in subsequent tutorials.


Learn More
- Algorand Block Explorers

Step 1-7. Complete Example

Here is the completed code to Create an Asset.

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;

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

// Function used to wait for a tx confirmation
const waitForConfirmation = async function (algodclient, txId) {
    let response = await algodclient.status().do();
    let lastround = response["last-round"];
    while (true) {
        const pendingInfo = await algodclient.pendingTransactionInformation(txId).do();
        if (pendingInfo["confirmed-round"] !== null && pendingInfo["confirmed-round"] > 0) {
            //Got the completed Transaction
            console.log("Transaction " + txId + " confirmed in round " + pendingInfo["confirmed-round"]);
            break;
        }
        lastround++;
        await algodclient.statusAfterBlock(lastround).do();
    }
};


// Function used to print created asset for account and assetid
const printCreatedAsset = async function (algodclient, account, assetid) {
    // note: if you have an indexer instance available it is easier to just use this
    //     let accountInfo = await indexerClient.searchAccounts()
    //    .assetID(assetIndex).do();
    // and in the loop below use this to extract the asset for a particular account
    // accountInfo['accounts'][idx][account]);
    let accountInfo = await algodclient.accountInformation(account).do();
    for (idx = 0; idx < accountInfo['created-assets'].length; idx++) {
        let scrutinizedAsset = accountInfo['created-assets'][idx];
        if (scrutinizedAsset['index'] == assetid) {
            console.log("AssetID = " + scrutinizedAsset['index']);
            let myparms = JSON.stringify(scrutinizedAsset['params'], undefined, 2);
            console.log("parms = " + myparms);
            break;
        }
    }
};
// Function used to print asset holding for account and assetid
const printAssetHolding = async function (algodclient, account, assetid) {
    // note: if you have an indexer instance available it is easier to just use this
    //     let accountInfo = await indexerClient.searchAccounts()
    //    .assetID(assetIndex).do();
    // and in the loop below use this to extract the asset for a particular account
    // accountInfo['accounts'][idx][account]);
    let accountInfo = await algodclient.accountInformation(account).do();
    for (idx = 0; idx < accountInfo['assets'].length; idx++) {
        let scrutinizedAsset = accountInfo['assets'][idx];
        if (scrutinizedAsset['asset-id'] == assetid) {
            let myassetholding = JSON.stringify(scrutinizedAsset, undefined, 2);
            console.log("assetholdinginfo = " + myassetholding);
            break;
        }
    }
};

// Recover accounts
// paste in mnemonic phrases here for each account

var account1_mnemonic = "PASTE your phrase for account 1";
var account2_mnemonic = "PASTE your phrase for account 2";
var account3_mnemonic = "PASTE your phrase for account 3"

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.Algodv2(token, server, port);

// Debug Console should look similar to this

// THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM  
// AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU   
// 3ZQ3SHCYIKSGK7MTZ7PE7S6EDOFWLKDQ6RYYVMT7OHNQ4UJ774LE52AQCU   


(async () => {
    // Asset Creation:
    // The first transaction 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
    let params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;
    console.log(params);

    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.makeAssetCreateTxnWithSuggestedParams(addr, note,
        totalIssuance, decimals, defaultFrozen, manager, reserve, freeze,
        clawback, unitName, assetName, assetURL, assetMetadataHash, params);

    let rawSignedTxn = txn.signTxn(recoveredAccount1.sk)
    let tx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    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).do();
    assetID = ptx["asset-index"];
    // console.log("AssetID = " + assetID);

    await printCreatedAsset(algodclient, recoveredAccount1.addr, assetID);
    await printAssetHolding(algodclient, recoveredAccount1.addr, assetID);
})().catch(e => {
    console.log(e);
    console.trace();
});

Note: Copy off the AssetID, as it will be used in subsequent parts.

Part 2: Change Manager Role

This part demonstrates the steps involved in changing / reconfiguring an asset, specifically the asset manager role.

Requirements

  • The AssetID of an Asset created in Part 1.

Background

ASA’s are highly customizable. Of the many characteristics, an asset has certain privileges associated with it including manger, freeze, clawback and reserve functionality. The manager of an asset is the only Algorand account that can destroy an asset and is the only account that can reconfigure the other admin roles of an asset. All other parameters are locked for the life of the asset.

Asset reconfiguration allows the address specified as manager to change any of the special addresses for the asset, such as the reserve address. To keep an address the same, it must be re-specified in each new configuration transaction. Supplying an empty address is the same as turning the associated feature off for this asset. Once a special address is set to the empty address, it can never change again. For example, if an asset configuration transaction specifying clawback=”” were issued, the associated asset could never be revoked from asset holders, and clawback=”” would be true for all time. The strict_empty_address_check argument can help with this behavior: when set to its default true, AssetConfigTxn will throw an error if any undefined management addresses are passed.

Step 2-1. Create Asset Configuration Transaction

The parameter that needs to be defined for an asset configuration transaction is the manager variable which is set to the new account that will become the new manager of the asset. Paste in your assetID from Part 1. The current manager (Account 2) issues an asset configuration transaction that assigns Account 1 as the new manager. Keep reserve, freeze, and clawback address the same as before, i.e. Account 2. Add this code at the end of the async method.

    // use assetID from Part 1
    let assetID = (your assetID);
    // 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

    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;
    // 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.makeAssetConfigTxnWithSuggestedParams(recoveredAccount2.addr, note, 
        assetID, manager, reserve, freeze, clawback, params);


Learn More

Step 2-2. Sign Transaction

Sign the transaction.

    rawSignedTxn = ctxn.signTxn(recoveredAccount2.sk)

Step 2-3. Send Transaction to the Network

Broadcast the transaction to the blockchain

    let ctx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    console.log("Transaction : " + ctx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, ctx.txId);

Step 2-4. Print Asset Information

Print the asset information.

    // Get the asset information for the newly changed asset
    // use indexer or utiltiy function for Account info

    // The manager should now be the same as the creator
    await printCreatedAsset(algodclient, recoveredAccount1.addr, assetID);

Step 2-5. Check the Transaction on a Block Explorer

Once you’ve completed these steps your output should look something like this:

     Transaction: BXDODE2RUC77WVJL6HOQBACVAS6QPXOBSE55ZZTLJUTNLBXZNENA
     Transaction BXDODE2RUC77WVJL6HOQBACVAS6QPXOBSE55ZZTLJUTNLBXZNENA confirmed in round 3961855
     AssetID = 2653785
     parms = {
         "clawback": "AK6Q33PDO4RJZQPHEMODC6PUE5AR2UD4FBU6TNEJOU4UR4KC6XL5PWW5K4",
         "creator": "ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ",
         "decimals": 0,
         "default-frozen": false,
         "freeze": "AK6Q33PDO4RJZQPHEMODC6PUE5AR2UD4FBU6TNEJOU4UR4KC6XL5PWW5K4",
         "manager": "ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ",
         "metadata-hash": "MTZlZmFhMzkyNGE2ZmQ5ZDNhNDgyNDc5OWE0YWM2NWQ=",
         "name": "latinum",
         "reserve": "AK6Q33PDO4RJZQPHEMODC6PUE5AR2UD4FBU6TNEJOU4UR4KC6XL5PWW5K4",
         "total": 1000,
         "unit-name": "LATINUM",
         "url": "http://someurl"
     }

You can check the asset config transaction on a block explorer for reference.


Learn More

Step 2-6. Complete Example

    // use assetID from Part 1
    let assetID = (your assetID);
    // 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

    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;
    // 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.makeAssetConfigTxnWithSuggestedParams(recoveredAccount2.addr, note, 
        assetID, manager, reserve, freeze, clawback, params);

    // This transaction must be signed by the current manager
    rawSignedTxn = ctxn.signTxn(recoveredAccount2.sk)
    let ctx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    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
    // use indexer or utiltiy function for Account info

    // The manager should now be the same as the creator
    await printCreatedAsset(algodclient, recoveredAccount1.addr, assetID);

Next Tutorial: Asset Opt-In Tutorial

Part 3: Opt-in

Overview

This part demonstrates the steps involved in “opting” in to receive an ASA. To receive an Algorand asset, you must explicitly “opt-in” to receive the asset by sending a 0 amount of the asset to yourself (to the account wanting to receive the asset).

Background

On Algorand, each asset holding for an account is stored in a separate row in the account’s balance record, increasing the overall disk space required for that account. To protect against DDoS attacks, the minimum balance of an account increases by 0.1 Algos for each unique asset it holds. Having someone else increase your minimum balance would be a massive security compromise, so an account must elect to hold an asset (and increase the size of their own balance record).


Learn More
- Minimum Account Balance Requirement

Requirements

  • The AssetID of an Asset created in Part 1.
    Add this code at the end of the async method from part 2.

Step 3-1. Opt-In Transaction

An asset can be referenced by its assetName or its assetID which is generated when you create the asset. Given that the assetName is not unique, it is always recommended to reference an asset by its assetID, which is a unique value.

This value was derived in Part 1.

Notice that in this next block of code, the sender and recipient are the same account.

    // paste in the asset id from the create asset tutorial
    let assetID = (your assetid);
    // 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

    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;

    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;


    // signing and sending "txn" allows sender to begin accepting asset specified by creator and index
    let opttxn = algosdk.makeAssetTransferTxnWithSuggestedParams(sender, recipient, closeRemainderTo, revocationTarget,
         amount, note, assetID, params);

Step 3-2. Sign Transaction

The transaction must be signed by the account wishing to opt-in to the asset, in this case- Account 3.

  rawSignedTxn = opttxn.signTxn(recoveredAccount3.sk);

Step 3-3. Send the Transaction to the network

Broadcast the transaction to the blockchain.

    let opttx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    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
    console.log("Account 3 = " + recoveredAccount3.addr);

Step 3-4. Print the Account Information

Print the account information for account 3.

    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);

Step 3-5. Check the transaction on a block explorer

Once you’ve completed these steps you’re output should look something like this:

    Transaction: U7D44D6JJJZLNN2X7BACDMACHPPSUWROTCOYAJZEBMPHPSKWY36Q
    Transaction U7D44D6JJJZLNN2X7BACDMACHPPSUWROTCOYAJZEBMPHPSKWY36Q confirmed in round 3961857
    Account 3 = IWR4CLLCN2TIVX2QPVVKVR5ER5OZGMWAV5QB2UIPYMPKBPLJZX4C37C4AA
    assetholdinginfo = {
        "amount": 0,
        "asset-id": 2653785,
        "creator": "ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ",
        "is-frozen": false
    }

You can check the opt-in transaction on a block explorer for reference.


Learn More
- Algorand Block Explorers

Step 3-6. Complete Example

This example assumes that the asset has already been created. If the asset has not been created, it will throw an error. In this example, account 3 is opting in.

    let assetID = (your assetid);
    // 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

    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;

    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;


    // signing and sending "txn" allows sender to begin accepting asset specified by creator and index
    let opttxn = algosdk.makeAssetTransferTxnWithSuggestedParams(sender, recipient, closeRemainderTo, revocationTarget,
         amount, note, assetID, params);

    // Must be signed by the account wishing to opt in to the asset    
    rawSignedTxn = opttxn.signTxn(recoveredAccount3.sk);
    let opttx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    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
    console.log("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);

Part 4: Transfer

This tutorial demonstrates the steps involved in transferring an asset from one account to another. Transfers are authorized by the account that holds the asset to be transferred. Asset transfers are analogous to standard payment transactions but for Algorand Standard Assets.

Background

Transferring an asset allows users to transact with assets, after they have issued asset acceptance transactions. The optional closeRemainderTo argument can be used to stop transacting with a particular asset. Now that the opt-in has been done on a potential receiving account in the previous asset opt-in step, assets can be transferred.

Step 4-1. Create Asset Transfer Transaction

This code has Account 1 sending 10 assets to Account 3. Set assetID, Asset Amount, sender and recipient. We set revocationTarget to undefined as this is not a revocation operation. closeReaminderTo is set to undefined as we are not closing out an asset. Use the same method that was used in the previous part.

    // replace with your  assetid
    let assetID = (your AssetID);  
    // Transfer New Asset:
    // Now that account3 can recieve the new tokens 
    // we can tranfer tokens in from the creator
    // to account3
    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example

    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;

    sender = recoveredAccount1.addr;
    recipient = recoveredAccount3.addr;
    revocationTarget = undefined;
    closeRemainderTo = undefined;
    //Amount of the asset to transfer
    amount = 10;

    // signing and sending "txn" will send "amount" assets from "sender" to "recipient"
    let xtxn = algosdk.makeAssetTransferTxnWithSuggestedParams(sender, recipient, closeRemainderTo, revocationTarget,
         amount,  note, assetID, params);


Learn More
- Transferring an Asset
-

Step 4-2. Sign Transfer Transaction

The transaction must be signed by the sender account.

    // Must be signed by the account sending the asset  
    rawSignedTxn = xtxn.signTxn(recoveredAccount1.sk)

Step 4-3. Send Transfer Transaction

Submit the transaction and list the account amount for acct3.

    let xtx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    console.log("Transaction : " + xtx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, xtx.txId);

Step 4-4. Print Account Information

You should see that Account 3 now has 10 of the new asset.

    // You should now see the 10 assets listed in the account information
    console.log("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);

Step 4-5. Check the transaction on a block explorer

Once you’ve completed these steps you’re output should look something like this:

    your console/terminal out put should look similar to this:
    Transaction: BMQQXKLOYE5663UZOIMLBVFRXE73XH2UXPBPPGJR7XOMM5E3UO7A
    Transaction BMQQXKLOYE5663UZOIMLBVFRXE73XH2UXPBPPGJR7XOMM5E3UO7A confirmed in round 3961861
    Account 3 = IWR4CLLCN2TIVX2QPVVKVR5ER5OZGMWAV5QB2UIPYMPKBPLJZX4C37C4AA
    AssetExample.js: 343
    assetholdinginfo = {
        "amount": 10,
        "asset-id": 2653785,
        "creator": "ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ",
        "is-frozen": false
    }
       Transaction: LM2QOJP6FKLX2XIKCDLTM37IEE2IGQ5IMVOEYOCFHKOAUMU5G6ZQ
       Transaction LM2QOJP6FKLX2XIKCDLTM37IEE2IGQ5IMVOEYOCFHKOAUMU5G6ZQ confirmed in round 4273946
       Account Information for: { "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM", "amount": 10, "frozen": false }

You can check the transfer transaction on a block explorer for reference.


Learn More
- Algorand Block Explorers

Step 4-6. Complete Example

This example assumes that the recipient account has already opted in to receiving the asset. If the account has not already opted in for this asset, it will throw an error.

    let assetID = (your assetID);   
    // Transfer New Asset:
    // Now that account3 can recieve the new tokens 
    // we can tranfer tokens in from the creator
    // to account3
    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example

    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;

    sender = recoveredAccount1.addr;
    recipient = recoveredAccount3.addr;
    revocationTarget = undefined;
    closeRemainderTo = undefined;
    //Amount of the asset to transfer
    amount = 10;

    // signing and sending "txn" will send "amount" assets from "sender" to "recipient"
    let xtxn = algosdk.makeAssetTransferTxnWithSuggestedParams(sender, recipient, closeRemainderTo, revocationTarget,
         amount,  note, assetID, params);
    // Must be signed by the account sending the asset  
    rawSignedTxn = xtxn.signTxn(recoveredAccount1.sk)
    let xtx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    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
    console.log("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);

Next Tutorial: Asset Freeze Tutorial

Part 5: Freeze

Overview

This tutorial demonstrates how to freeze an asset holding of a particular account. Freezing an asset means that the asset can no longer be sent to or from that account.

An example use case for this functionality is if you suspect fraudulent activity related to your asset, you can issue a freeze transaction against the offending account’s asset holding while you take the time to investigate. If the account checks out okay, you can issue a follow-up transaction to unfreeze the account so they can resume trade.

Note that the sender of a freeze or unfreeze transaction must be the Freeze Manager, which is specified in the asset’s on-chain configuration. Read more about Asset Freeze Transactions in the docs.

Background

Algorand Standard Assets are built on layer-1 and benefit from the same speed, ease of use, and security as the Algorand blockchain’s native token. Read all about Algorand Standard Assets in the docs.

One of the characteristics of an ASA is the ability to make it freezable, or not. Making an asset freezable means that an account that has an asset in its balance record can be made frozen for that particular asset and will not be able to make asset transfer transactions with that asset. The corresponding account that would trigger a freeze transaction is called the freeze address. If the asset is created without a freeze address, then the asset is forever “un-freezable.”

Note: A frozen account can always close out to the asset creator.

Step 5-1. Create the Freeze Transaction

In this first step, we need to define a freezeTarget as well as a freezeState . This asset was made “freezable” when we first created the asset. Setting an address to the freeze parameter in the method call makes the asset “freezable.” Setting the freeze address parameter to “”, would make the asset unfreezable and that characteristic cannot be changed retroactively. In this example we will freeze Account3 from transacting with an asset. The freeze transaction is sent from the freeze account, Which in this example is Account 2.

    // Freeze Asset:
    // replace with your  assetid
    let assetID = (your asset id);

    // 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. 
    // The freeze transaction is sent from the freeze acount
    // Which in this example is account2 

    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
   // await getChangingParms(algodclient);
    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;

    from = recoveredAccount2.addr;
    freezeTarget = recoveredAccount3.addr;
    freezeState = true;

    // The freeze transaction needs to be signed by the freeze account
    let ftxn = algosdk.makeAssetFreezeTxnWithSuggestedParams(from, note,
        assetID, freezeTarget, freezeState, params)


Learn More
- Asset Freeze Transaction

Step 5-2. Sign Freeze Transaction

The freeze transaction needs to be signed by the freeze account (Account 2).

    // Must be signed by the freeze account   
    rawSignedTxn = ftxn.signTxn(recoveredAccount2.sk)

Step 5-3. Send Freeze Transaction

Broadcast the freeze transaction to the blockchain.

    let ftx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    console.log("Transaction : " + ftx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, ftx.txId);

Step 5-4. Print Account Information

You should now see the asset is frozen listed in the account information.

    // You should now see the asset is frozen listed in the account information
    console.log("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);

Step 5-5. Check the Transaction on a Block Explorer

Once you’ve completed these steps you’re output should look something like this:

    Transaction: VI33O4TAM2JZ4AMNJFYBDH4NATHPGBVAFTALLUKRI2WZ772EKA4A
    Transaction VI33O4TAM2JZ4AMNJFYBDH4NATHPGBVAFTALLUKRI2WZ772EKA4A confirmed in round 3961865
    Account 3 = IWR4CLLCN2TIVX2QPVVKVR5ER5OZGMWAV5QB2UIPYMPKBPLJZX4C37C4AA
    AssetExample.js: 385
    assetholdinginfo = {
        "amount": 10,
        "asset-id": 2653785,
        "creator": "ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ",
        "is-frozen": true
    }

You can check the freeze transaction on a block explorer for reference.


Learn More
- Algorand Block Explorers

Step 5-6. Complete Example

This example assumes that the freezeTarget account has an asset transferred to it in the previous Part.

    let assetID = (your AssetID);
    // Freeze Asset

    // 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. 
    // The freeze transaction is sent from the freeze acount
    // Which in this example is account2 

    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
   // await getChangingParms(algodclient);
    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;

    from = recoveredAccount2.addr;
    freezeTarget = recoveredAccount3.addr;
    freezeState = true;

    // The freeze transaction needs to be signed by the freeze account
    let ftxn = algosdk.makeAssetFreezeTxnWithSuggestedParams(from, note,
        assetID, freezeTarget, freezeState, params)

    // Must be signed by the freeze account   
    rawSignedTxn = ftxn.signTxn(recoveredAccount2.sk)
    let ftx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    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
    console.log("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);

Part 6: Revoke

Overview

This tutorial demonstrates the steps involved in revoking an asset. Asset revocation is the same as clawback functionality. When an asset is created, the parameter in the method call that allows an asset to be “revocable” is called clawback address. If that parameter is set to “”, the asset is “un-revocable” and cannot be retroactively changed to being “revocable”.


Learn More
- Asset Parameters

Background

ASA’s are highly customizable. One of the characteristics of an ASA is the ability to make the asset revocable or not. This functionality is called clawback in the code. The corresponding account that would trigger a revoke transaction is called the clawback address.

Note: A creator account can clawback from a frozen account.

Steps

Step 6-1. Revoke Transaction

In this step, we need to use a revocation target that we previously set to undefined the previous times we’ve used the Asset Transfer method. In this case, we are revoking assets from Account 3, whose assets were previously frozen in the last Part. We are not closing out the asset, so the closeRemainderTo parameter is set to undefined.

The asset was created in Part 1 with the ability for it to be revoked by the clawback address. If the asset was created or configured by the manager to not allow this by setting the clawback address to “” then this would not be possible. We will now clawback the 10 assets from account 3. Account 2 is the clawback account (sender) and must sign the transaction. The recipient will be the clawback to address. The recipient will also be the creator, in this case, which is account 1. The clawback address (Account 2) revokes 10 assets from Account 3 and places it back with Account 1.

    // paste in the asset id 
    let assetID = (your assetID);
   // 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
    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;   

    sender = recoveredAccount2.addr;
    recipient = recoveredAccount1.addr;
    revocationTarget = recoveredAccount3.addr;
    closeRemainderTo = undefined;
    amount = 10;
    // 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.makeAssetTransferTxnWithSuggestedParams(sender, recipient, closeRemainderTo, revocationTarget,
       amount, note, assetID, params);

Step 6-2. Sign Revoke Asset Transaction

Account 2 is the clawback address, meaning that this is the account that approves and sends the transaction and also pays the 1000 microAlgo fee.

   // Must be signed by the account that is the clawback address 
    rawSignedTxn = rtxn.signTxn(recoveredAccount2.sk)

Step 6-3. Send Revoke Asset Transaction

Broadcast the transaction to the blockchain.

    rawSignedTxn = rtxn.signTxn(recoveredAccount2.sk)
    let rtx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    console.log("Transaction : " + rtx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, rtx.txId);

Step 6-4. Print Account Information

You should now see 0 assets listed in the account information for the third account.

   // You should now see 0 assets listed in the account information
    // for the third account
    console.log("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);

Step 6-5. Check the transaction on a block explorer

Once you’ve completed these steps you’re output should look something like this:

    Transaction: I6U5NCHZ6DLHLBXECPSSRLAN3JYYUXOZLN4HCE4BPLRG7G7LGRMA
    Transaction I6U5NCHZ6DLHLBXECPSSRLAN3JYYUXOZLN4HCE4BPLRG7G7LGRMA confirmed in round 3961873
    Account 3 = IWR4CLLCN2TIVX2QPVVKVR5ER5OZGMWAV5QB2UIPYMPKBPLJZX4C37C4AA
    AssetExample.js: 433
    assetholdinginfo = {
        "amount": 0,
        "asset-id": 2653785,
        "creator": "ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ",
        "is-frozen": true
    }


Learn More
- Algorand Block Explorers

Step 6-6. Complete Example

Here is the complete example code to revoke an asset.

    let assetID = (your assetID);
    // 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
    // First update changing transaction parameters
    // We will account for changing transaction parameters
    // before every transaction in this example
    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;   

    sender = recoveredAccount2.addr;
    recipient = recoveredAccount1.addr;
    revocationTarget = recoveredAccount3.addr;
    closeRemainderTo = undefined;
    amount = 10;
    // 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.makeAssetTransferTxnWithSuggestedParams(sender, recipient, closeRemainderTo, revocationTarget,
       amount, note, assetID, params);
    // Must be signed by the account that is the clawback address    
    rawSignedTxn = rtxn.signTxn(recoveredAccount2.sk)
    let rtx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    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("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);

Part 7: Destroy

Overview

This tutorial demonstrates the steps involved in destroying an asset. In order to trigger a destroy asset transaction, the original creator of the asset must be in possession (must have in it’s balance record) all units of the asset.


Learn More

Destroy an Asset

Background

In order to trigger a destroy asset transaction, the original creator of the asset must be in possession (must have in it’s balance record) all units of the asset.

Steps

Step 7-1. Destroy Asset

The only parameter that needs to be defined when conducting an asset destroy operation is the sender address, which needs to be the manager address of 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 manager account, which is currently the creator of Account 1 (addr). With all assets back in the creator’s account, the manager (Account 1) destroys the asset.

    // paste in the asset id 
    let assetID = (your assetID);
    // 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

    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;

    // The address for the from field must be the manager account
    // Which is currently the creator addr1
    addr = recoveredAccount1.addr;
    note = undefined;
    // 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.makeAssetDestroyTxnWithSuggestedParams(addr, note, assetID, params);

Step 7-2. Sign Destroy Asset Transaction

The transaction must be signed by the manager which is currently set to Account 1.

    // The transaction must be signed by the manager which 
    // is currently set to account1
    rawSignedTxn = dtxn.signTxn(recoveredAccount1.sk)

Step 7-3. Send Destroy Asset Transaction

Broadcast the transaction to the blockchain.

    let dtx = (await algodclient.sendRawTransaction(rawSignedTxn).do());
    console.log("Transaction : " + dtx.txId);
    // wait for transaction to be confirmed
    await waitForConfirmation(algodclient, dtx.txId);

Step 7-4. Print Account Information

Account 3 and Account 1 should no longer contain the asset as it has been destroyed.

    // The account3 and account1 should no longer contain the asset as it has been destroyed
    console.log("Asset ID: " + assetID);
    console.log("Account 1 = " + recoveredAccount1.addr);
    await printCreatedAsset(algodclient, recoveredAccount1.addr, assetID);
    await printAssetHolding(algodclient, recoveredAccount1.addr, assetID);
    console.log("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);  


    // Notice that although the asset was destroyed, the asset id and associated 
    // metadata still exists in account holdings for Account 3. 
    // When you destroy an asset, the global parameters associated with that asset
    // (manager addresses, name, etc.) are deleted from the creator's balance record (Account 1).
    // However, holdings are not deleted automatically -- users still need to close out of the deleted asset.
    // This is necessary for technical reasons because we currently can't have a single transaction touch potentially 
    // thousands of accounts (all the holdings that would need to be deleted).

Step 7-5. Check the Transaction on a Block Explorer

Check the transaction in the block explorer.

    Transaction: KMSVPDQMVGIASU2ZLKRBSWOQVXR3EOJ4Z4WKS5GTB7MKE4D57L6Q
    Transaction KMSVPDQMVGIASU2ZLKRBSWOQVXR3EOJ4Z4WKS5GTB7MKE4D57L6Q confirmed in round 3961877
    Asset ID: 2653785
    Account 1 = ATTR6RUEHHBHXKUHT4GUOYWNBVDV2GJ5FHUWCSFZLHD55EVKZWOWSM7ABQ
    Account 3 = IWR4CLLCN2TIVX2QPVVKVR5ER5OZGMWAV5QB2UIPYMPKBPLJZX4C37C4AA
    AssetExample.js: 480
    assetholdinginfo = {
        "amount": 0,
        "asset-id": 2653785,
        "creator": "",
        "is-frozen": true
    }

Notice that although the asset was destroyed, the asset id and associated metadata still exists in the account balance record. When you destroy an asset, the global parameters associated with that asset (manager addresses, name, etc.) are deleted from the creator’s balance record. However, holdings are not deleted automatically – users still need to close out of the deleted asset. This is necessary for technical reasons because we can’t have a single transaction touch potentially thousands of accounts (all the holdings that would need to be deleted).


Learn More
- Algorand Block Explorers

Step 7-6. Complete Example

    // paste in your asset id
    let assetID =(your assetID);
    // 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

    params = await algodclient.getTransactionParams().do();
    //comment out the next two lines to use suggested fee
    params.fee = 1000;
    params.flatFee = true;

    // The address for the from field must be the manager account
    // Which is currently the creator addr1
    addr = recoveredAccount1.addr;
    note = undefined;
    // 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.makeAssetDestroyTxnWithSuggestedParams(addr, note, assetID, params);
    // 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).do());
    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);
    console.log("Account 1 = " + recoveredAccount1.addr);
    await printCreatedAsset(algodclient, recoveredAccount1.addr, assetID);
    await printAssetHolding(algodclient, recoveredAccount1.addr, assetID);
    console.log("Account 3 = " + recoveredAccount3.addr);
    await printAssetHolding(algodclient, recoveredAccount3.addr, assetID);