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
- Algorand JavaScript SDK
- Algorand Sandbox Installation or set up a node as described here.
- Three funded accounts and the corresponding addresses, and private keys or mnemonic phrases. See Create Accounts Tutorial
Optional:
- API testing tool such as Postman
- Use VS Code with JavaScript
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.
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
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);