Tutorials
No Results
Tutorial

Beginner · 1 hour

Working with ASA using Go

This tutorial demonstrates the steps involved in creating a basic Algorand Standard Asset (ASA). This tutorial can be completed on its own or can be done in conjunction with the follow-up tutorials: Asset Reconfigure, Opt-In, Asset Transfer, Asset Freeze, Asset Revoke, and Asset Destroy. Completing this tutorial will set you up for the subsequent four tutorials. Only two accounts are required to create an ASA in this tutorial, but we will need three accounts total for the subsequent four tutorials.

Completed code for this tutorial can be found here assetExample.go.

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 an 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.

Steps

Part 1: Create an Algorand Standard Asset

In this part we will create an Algorand Standard Asset using Go.

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

Step 1-1. Instantiate Algod Wrapper

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. loadAccounts() and PrettyPrint() functions will be described in the next step.

// Main function to demonstrate ASA examples
func main() {

    // Initialize an algodClient
    algodClient, err := algod.MakeClient(algodAddress, algodToken)
    if err != nil {
        return
    }
    // Get network-related transaction parameters and assign
    txParams, err := algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000
    // Get pre-defined set of keys for example
    sks, pks := loadAccounts()
    // Print asset info for newly created asset.
    prettyPrint(txParams)
    prettyPrint(sks)
    prettyPrint(pks)
}


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

Step 1-2. Recover, define and print Accounts, utility functions and structures

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

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

This tutorial will use TestNet accounts that have been pre-created the Create Accounts tutorial. Be sure to dispense Algos to these accounts before continuing, using the TestNet Dispenser.

package main

import (
    "context"
    "crypto/ed25519"
    json "encoding/json"
    "fmt"

    "github.com/algorand/go-algorand-sdk/client/v2/algod"
    "github.com/algorand/go-algorand-sdk/crypto"
    "github.com/algorand/go-algorand-sdk/mnemonic"
    "github.com/algorand/go-algorand-sdk/types"
)
import transaction "github.com/algorand/go-algorand-sdk/future"


// UPDATE THESE VALUES
// const algodAddress = "Your ADDRESS"
// const algodToken = "Your TOKEN"

// sandbox
const algodAddress = "http://localhost:4001"
const algodToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

// Accounts to be used through examples
func loadAccounts() (map[int][]byte, map[int]string) {
    // Shown for demonstration purposes. NEVER reveal secret mnemonics in practice.
    // Change these values to use the accounts created previously.

    // Paste in mnemonic phrases for all three accounts
    mnemonic1 := "PASTE your phrase for account 1"
    mnemonic2 := "PASTE your phrase for account 2"
    mnemonic3 := "PASTE your phrase for account 3"



    mnemonics := []string{mnemonic1, mnemonic2, mnemonic3}
    pks := map[int]string{1: "", 2: "", 3: ""}
    var sks = make(map[int][]byte)

    for i, m := range mnemonics {
        var err error
        sk, err := mnemonic.ToPrivateKey(m)
        sks[i+1] = sk
        if err != nil {
            fmt.Printf("Issue with account %d private key conversion.", i+1)
        }
        // derive public address from Secret Key.
        pk := sk.Public()
        var a types.Address
        cpk := pk.(ed25519.PublicKey)
        copy(a[:], cpk[:])
        pks[i+1] = a.String()
        fmt.Printf("Loaded Key %d: %s\n", i+1, pks[i+1])
    }
    return sks, pks
}

func waitForConfirmation(txID string, client *algod.Client) {
    status, err := client.Status().Do(context.Background())
    if err != nil {
        fmt.Printf("error getting algod status: %s\n", err)
        return
    }
    lastRound := status.LastRound
    for {
        pt, _, err := client.PendingTransactionInformation(txID).Do(context.Background())
        if err != nil {
            fmt.Printf("error getting pending transaction: %s\n", err)
            return
        }
        if pt.ConfirmedRound > 0 {
            fmt.Printf("Transaction "+txID+" confirmed in round %d\n", pt.ConfirmedRound)
            break
        }
        fmt.Printf("waiting for confirmation\n")
        lastRound++
        status, err = client.StatusAfterBlock(lastRound).Do(context.Background())
    }
}

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

// printAssetHolding utility to print asset holding for account
func printAssetHolding(assetID uint64, account string, client *algod.Client) {

    act, err := client.AccountInformation(account).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to get account information: %s\n", err)
        return
    }
    for _, assetholding := range act.Assets {
        if assetID == assetholding.AssetId {
            prettyPrint(assetholding)
            break
        }
    }
}

// printCreatedAsset utility to print created assert for account
func printCreatedAsset(assetID uint64, account string, client *algod.Client) {

    act, err := client.AccountInformation(account).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to get account information: %s\n", err)
        return
    }
    for _, asset := range act.CreatedAssets {
        if assetID == asset.Index {
            prettyPrint(asset)
            break
        }
    }
}


Learn More
- Algorand Standalone Account Creation Methods

Step 1-3. Create Asset Transaction

We need to define the Create Asset Transaction parameters. Paste this snippet at the end of the main function. Use the MakeAssetCreateTxn transaction method to create an Asset.

Uncomment these imports at the top of the code, if you have them commented out:

- b64 "encoding/base64"

- "github.com/algorand/go-algorand-sdk/transaction"

- "github.com/algorand/go-algorand-sdk/crypto"

    // CREATE ASSET

    // Construct the transaction
    // Set parameters for asset creation 
    creator := pks[1]
    assetName := "latinum"
    unitName := "latinum"
    assetURL := "https://path/to/my/asset/details"
    assetMetadataHash := "thisIsSomeLength32HashCommitment"
    defaultFrozen := false
    decimals := uint32(0)
    totalIssuance := uint64(1000)
    manager := pks[2]
    reserve := pks[2]
    freeze := pks[2]
    clawback := pks[2]
    note := []byte(nil)
    txn, err := transaction.MakeAssetCreateTxn(creator,
        note,
        txParams, totalIssuance, decimals,
        defaultFrozen, manager, reserve, freeze, clawback,
        unitName, assetName, assetURL, assetMetadataHash)

    if err != nil {
        fmt.Printf("Failed to make asset: %s\n", err)
        return
    }
    fmt.Printf("Asset created AssetName: %s\n", txn.AssetConfigTxnFields.AssetParams.AssetName)


Learn More
- Algorand Asset Parameters
- Common Transaction Fields

Step 1-4. Sign Transaction

Sign the transaction.

    // sign the transaction
    txid, stx, err := crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)

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

Broadcast the transaction to the blockchain.

    // Broadcast the transaction to the network
    sendResponse, err := algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Submitted transaction %s\n", sendResponse)
    // Wait for transaction to be confirmed
    waitForConfirmation(txid, algodClient)
    response, stxn, err := algodClient.PendingTransactionInformation(txid).Do(context.Background())
    fmt.Printf("%s\n", stxn)
    assetID := response.AssetIndex

    // print created asset and asset holding info for this asset
    fmt.Printf("Asset ID: %d\n", assetID)
    printCreatedAsset(assetID, pks[1], algodClient)
    printAssetHolding(assetID, pks[1], algodClient)

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

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

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

Asset ID: 13219193
{
    "index": 13219193,
    "params": {
        "clawback": "AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU",
        "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM",
        "decimals": 0,
        "freeze": "AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU",
        "manager": "AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU",
        "metadata-hash": "dGhpc0lzU29tZUxlbmd0aDMySGFzaENvbW1pdG1lbnQ=",
        "name": "latinum",
        "reserve": "AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU",
        "total": 1000,
        "unit-name": "latinum",
        "url": "https://path/to/my/asset/details"
    }
} 
{
    "amount": 1000,
    "asset-id": 13219193,
    "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM"
} 

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


Learn More
- Algorand Block Explorers

Step 1-7. Complete Example

Here is the completed code to Create an Asset.

package main

import (
    "context"
    "crypto/ed25519"
//  "encoding/base64"
    json "encoding/json"
    "fmt"

    "github.com/algorand/go-algorand-sdk/client/v2/algod"
    "github.com/algorand/go-algorand-sdk/crypto"
    "github.com/algorand/go-algorand-sdk/mnemonic"
    "github.com/algorand/go-algorand-sdk/types"
)
import transaction "github.com/algorand/go-algorand-sdk/future"

// UPDATE THESE VALUES
// const algodAddress = "Your ADDRESS"
// const algodToken = "Your TOKEN"

// sandbox
const algodAddress = "http://localhost:4001"
const algodToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

// Accounts to be used through examples
func loadAccounts() (map[int][]byte, map[int]string) {
    // Shown for demonstration purposes. NEVER reveal secret mnemonics in practice.
    // Change these values to use the accounts created previously.

    // Paste in mnemonic phrases for all three accounts
    mnemonic1 := "PASTE your phrase for account 1"
    mnemonic2 := "PASTE your phrase for account 2"
    mnemonic3 := "PASTE your phrase for account 3"


    mnemonics := []string{mnemonic1, mnemonic2, mnemonic3}
    pks := map[int]string{1: "", 2: "", 3: ""}
    var sks = make(map[int][]byte)

    for i, m := range mnemonics {
        var err error
        sk, err := mnemonic.ToPrivateKey(m)
        sks[i+1] = sk
        if err != nil {
            fmt.Printf("Issue with account %d private key conversion.", i+1)
        }
        // derive public address from Secret Key.
        pk := sk.Public()
        var a types.Address
        cpk := pk.(ed25519.PublicKey)
        copy(a[:], cpk[:])
        pks[i+1] = a.String()
        fmt.Printf("Loaded Key %d: %s\n", i+1, pks[i+1])
    }
    return sks, pks
}

func waitForConfirmation(txID string, client *algod.Client) {
    status, err := client.Status().Do(context.Background())
    if err != nil {
        fmt.Printf("error getting algod status: %s\n", err)
        return
    }
    lastRound := status.LastRound
    for {
        pt, _, err := client.PendingTransactionInformation(txID).Do(context.Background())
        if err != nil {
            fmt.Printf("error getting pending transaction: %s\n", err)
            return
        }
        if pt.ConfirmedRound > 0 {
            fmt.Printf("Transaction "+txID+" confirmed in round %d\n", pt.ConfirmedRound)
            break
        }
        fmt.Printf("waiting for confirmation\n")
        lastRound++
        status, err = client.StatusAfterBlock(lastRound).Do(context.Background())
    }
}

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

// printAssetHolding utility to print asset holding for account
func printAssetHolding(assetID uint64, account string, client *algod.Client) {

    act, err := client.AccountInformation(account).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to get account information: %s\n", err)
        return
    }
    for _, assetholding := range act.Assets {
        if assetID == assetholding.AssetId {
            prettyPrint(assetholding)
            break
        }
    }
}

// printCreatedAsset utility to print created assert for account
func printCreatedAsset(assetID uint64, account string, client *algod.Client) {

    act, err := client.AccountInformation(account).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to get account information: %s\n", err)
        return
    }
    for _, asset := range act.CreatedAssets {
        if assetID == asset.Index {
            prettyPrint(asset)
            break
        }
    }
}

// Main function to demonstrate ASA examples
func main() {

    // Initialize an algodClient
    algodClient, err := algod.MakeClient(algodAddress, algodToken)
    if err != nil {
        return
    }

    // Get network-related transaction parameters and assign
    txParams, err := algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000

    // Get pre-defined set of keys for example
    sks, pks := loadAccounts()

    // Print asset info for newly created asset.
    prettyPrint(txParams)
    prettyPrint(sks)
    prettyPrint(pks)


    // CREATE ASSET

    // Construct the transaction
    // Set parameters for asset creation 
    creator := pks[1]
    assetName := "latinum"
    unitName := "latinum"
    assetURL := "https://path/to/my/asset/details"
    assetMetadataHash := "thisIsSomeLength32HashCommitment"
    defaultFrozen := false
    decimals := uint32(0)
    totalIssuance := uint64(1000)
    manager := pks[2]
    reserve := pks[2]
    freeze := pks[2]
    clawback := pks[2]
    note := []byte(nil)
    txn, err := transaction.MakeAssetCreateTxn(creator,
        note,
        txParams, totalIssuance, decimals,
        defaultFrozen, manager, reserve, freeze, clawback,
        unitName, assetName, assetURL, assetMetadataHash)

    if err != nil {
        fmt.Printf("Failed to make asset: %s\n", err)
        return
    }
    fmt.Printf("Asset created AssetName: %s\n", txn.AssetConfigTxnFields.AssetParams.AssetName)
    // sign the transaction
    txid, stx, err := crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err := algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Submitted transaction %s\n", sendResponse)
    // Wait for transaction to be confirmed
    waitForConfirmation(txid, algodClient)
        response, stxn, err := algodClient.PendingTransactionInformation(txid).Do(context.Background())
    fmt.Printf("%s\n", stxn)
    assetID := response.AssetIndex

    // print created asset and asset holding info for this asset
    fmt.Printf("Asset ID: %d\n", assetID)
    printCreatedAsset(assetID, pks[1], algodClient)
    printAssetHolding(assetID, pks[1], algodClient)
}

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

Part 2: Change Asset Manager

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

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 strictEmptyAddressChecking argument can help with this behavior: when set to its default true, makeAssetConfigTxn 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 the Create Asset part. Note that MakeAssetConfigTxn transaction needs to be authorized by the existing manager of the asset. The remaining roles are all re-assigned to acct2.

    // CHANGE MANAGER
    assetID := uint64(your assetID)
    // Change Asset Manager from Account 2 to Account 1 
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000

    manager := pks[1]
    oldmanager := pks[2]
    reserve := pks[2]
    freeze := pks[2]
    clawback := pks[2]
    note := []byte(nil)

    strictEmptyAddressChecking := true
    txn, err := transaction.MakeAssetConfigTxn(oldmanager, note, txParams, assetID, manager, reserve, freeze, clawback, strictEmptyAddressChecking)
    if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }


Learn More
- Asset Configuration

Step 2-2. Sign Transaction

Sign the Transaction by the current manager, Account 2.

    txid, stx, err := crypto.SignTransaction(sks[2], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)

Step 2-3. Send Transaction to the network

Broadcast the transaction to the blockchain .

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

    // Wait for transaction to be confirmed
    waitForConfirmation(txid, algodClient)
    // print created assetinfo for this asset
    fmt.Printf("Asset ID: %d\n", assetID)
}

Step 2-4. Print Asset Information

Print Asset information for Asset creator account information

    printCreatedAsset(assetID, pks[1], algodClient)

Step 2-5. Check the transaction on the block explorer

Account 1 should now have the manager role. Once you’ve completed these steps your output should look something like this:

Loaded Key 1: THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM
Loaded Key 2: AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU
Loaded Key 3: 3ZQ3SHCYIKSGK7MTZ7PE7S6EDOFWLKDQ6RYYVMT7OHNQ4UJ774LE52AQCU

Transaction 2I4Z6HOY7ZU2VV2JHZVIYFXMIPE6TU2W4X7DDQEDYRHDX3I2DYAQ confirmed in round 10709099
Asset ID: 13219193
{
    "index": 13219193,
    "params": {
        "clawback": "AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU",
        "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM",
        "decimals": 0,
        "freeze": "AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU",
        "manager": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM",
        "metadata-hash": "dGhpc0lzU29tZUxlbmd0aDMySGFzaENvbW1pdG1lbnQ=",
        "name": "latinum",
        "reserve": "AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU",
        "total": 1000,
        "unit-name": "latinum",
        "url": "https://path/to/my/asset/details"
    }
}

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


Learn More
- Algorand Block Explorers

Step 2-6. Complete Example

Here is the completed code to change an Asset. Paste this code at the end of function main().

    // CHANGE MANAGER
    // Change Asset Manager from Account 2 to Account 1
        assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000

    manager = pks[1]
    oldmanager := pks[2]
    strictEmptyAddressChecking := true
    txn, err = transaction.MakeAssetConfigTxn(oldmanager, note, txParams, assetID, manager, reserve, freeze, clawback, strictEmptyAddressChecking)
    if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }

    txid, stx, err = crypto.SignTransaction(sks[2], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", txid)

    // Wait for transaction to be confirmed
    waitForConfirmation(txid,algodClient )
    // print created assetinfo for this asset
    fmt.Printf("Asset ID: %d\n", assetID)
    printCreatedAsset(assetID, pks[1], algodClient)
}

// resource https://developer.algorand.org/docs/features/asa/

Part 3: Opt-In

This part demonstrates the steps involved in “opting” in to receive an Algorand Standard Asset (ASA) To receive an Algorand asset, you must explicitly “opt-in” to receive the asset using the method MakeAssetAcceptanceTxn. This effectively sends a 0 amount of the asset to yourself (to the account wanting to receive the asset).


Learn More
- Minimum Account Balance Requirement

Background

Holding an asset increases the size of your balance record. When you hold an asset, your balance record has an entry in its Assets map. map AssetIndex => AssetHolding . The balance record is the row in the database associated with an Algorand account. So having an asset increases the minimum balance requirement for your account by 0.1 ALGO’s. Having someone else increase your minimum balance would be a massive security compromise, so an account must elect to increase the size of their own balance record.

Step 3-1. Create Opt-In Transaction

An asset can be reference 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 the Asset Creation Part. Opt-In to receiving an asset and update the fee. Account 3 is opting in.

    // paste in your assetID
    assetID := uint64(your assetID)
    // OPT-IN
    // Account 3 opts in to receive latinum
    // Use previously set transaction parameters and update sending address to account 3
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000

    txn, err = transaction.MakeAssetAcceptanceTxn(pks[3], note, txParams, assetID)
    if err != nil {
        fmt.Printf("Failed to send transaction MakeAssetAcceptanceTxn: %s\n", err)
        return
    }

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.

    txid, stx, err = crypto.SignTransaction(sks[3], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }

    fmt.Printf("Transaction ID: %s\n", txid)

Step 3-3. Send the Transaction to the network

Send the transaction to the network and print off account information.

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

    // Wait for transaction to be confirmed
    waitForConfirmation(txid, algodClient)

    // print created assetholding for this asset and Account 3, showing 0 balance
    fmt.Printf("Asset ID: %d\n", assetID)
    fmt.Printf("Account 3: %s\n", pks[3])
    printAssetHolding(assetID, pks[3], algodClient)

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

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

Transaction E6BVMGFL6BWY4WGV3XBCFBQYF574WBO622ORGNNEEDU5U2PQENOQ confirmed in round 10709506
Asset ID: 13219193
Account 3: 3ZQ3SHCYIKSGK7MTZ7PE7S6EDOFWLKDQ6RYYVMT7OHNQ4UJ774LE52AQCU
{
    "amount": 0,
    "asset-id": 13219193,
    "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM"
} 

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


Learn More
- Algorand Block Explorers

Step 3-5. Complete Example

This example assumes that the asset has already been created in Part 1. If the asset has not been created, it will throw an error.

    // OPT-IN

    // Account 3 opts in to receive latinum
    // Use previously set transaction parameters and update sending address to account 3
    assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000

    txn, err = transaction.MakeAssetAcceptanceTxn(pks[3], note, txParams, assetID)
    if err != nil {
        fmt.Printf("Failed to send transaction MakeAssetAcceptanceTxn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[3], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }

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

    // Wait for transaction to be confirmed
    waitForConfirmation(txid, algodClient)

    // print created assetholding for this asset and Account 3, showing 0 balance
    fmt.Printf("Asset ID: %d\n", assetID)
    fmt.Printf("Account 3: %s\n", pks[3])
    printAssetHolding(assetID, pks[3], algodClient)

// resource https://developer.algorand.org/docs/features/asa/

Part 4: Transfer

This Part 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.

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 part, assets can be transferred.

steps

Step 4-1. Create Asset Transfer Transaction

This code has Account 1 sending 10 assets to Account 3. Set assetID, amount, sender and receiver. Using this method to transfer assets: MakeAssetTransferTxn.

    // TRANSFER ASSET

    // Send  10 latinum from Account 1 to Account 3
    assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000

    sender := pks[1]
    recipient := pks[3]
    amount := uint64(10)
    closeRemainderTo := ""
    txn, err = transaction.MakeAssetTransferTxn(sender, recipient, amount, note, txParams, closeRemainderTo, 
        assetID)
    if err != nil {
        fmt.Printf("Failed to send transaction MakeAssetTransfer Txn: %s\n", err)
        return
    }


Learn More
- Transferring an Asset

Step 4-2. Sign Transfer Transaction

The transaction must be signed by Account 1, the sender account.

    txid, stx, err = crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)

Step 4-3. Send Transfer Transaction and Print Account Information

Submit the transaction and list the account amount for acct3.
You should see that it now has 10 of the new asset.

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

    // Wait for transaction to be confirmed
    waitForConfirmation(txid,algodClient)

    // print created assetholding for this asset and Account 3 and Account 1
    // You should see amount of 10 in Account 3, and 990 in Account 1
    fmt.Printf("Asset ID: %d\n", assetID)
    fmt.Printf("Account 3: %s\n", pks[3])
    printAssetHolding(assetID, pks[3], algodClient)
    fmt.Printf("Account 1: %s\n", pks[1])
    printAssetHolding(assetID, pks[1], algodClient)

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

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

Transaction RSZLCLZ6Z6UWQZDE3S45SLNP3UN7DZVWZXKMT5PBBLHIZ3CD77CA confirmed in round 10709793
Asset ID: 13219193
Account 3: 3ZQ3SHCYIKSGK7MTZ7PE7S6EDOFWLKDQ6RYYVMT7OHNQ4UJ774LE52AQCU
{
    "amount": 10,
    "asset-id": 13219193,
    "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM"
} 
Account 1: THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM
{
    "amount": 990,
    "asset-id": 13219193,
    "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM"
} 

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


Learn More
- Algorand Block Explorers

Step 4-5. Complete Example

This example assumes that the receiver 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.

    // TRANSFER ASSET
    // Transfer an Asset
    // Send  10 latinum from Account 1 to Account 3
    assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000
    note := []byte(nil)
    sender := pks[1]
    recipient := pks[3]
    amount := uint64(10)
    closeRemainderTo := ""

    txn, err := transaction.MakeAssetTransferTxn(sender, recipient, amount, note, txParams, closeRemainderTo, 
        assetID)
    if err != nil {
        fmt.Printf("Failed to send transaction MakeAssetTransfer Txn: %s\n", err)
        return
    }
    txid, stx, err := crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err := algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Submitted transaction %s\n", sendResponse)
    fmt.Printf("Transaction ID raw: %s\n", txid)

    // Wait for transaction to be confirmed
    waitForConfirmation(txid,algodClient)

    // print created assetholding for this asset and Account 3 and Account 1
    // You should see amount of 10 in Account 3, and 990 in Account 1
    fmt.Printf("Asset ID: %d\n", assetID)
    fmt.Printf("Account 3: %s\n", pks[3])
    printAssetHolding(assetID, pks[3], algodClient)
    fmt.Printf("Account 1: %s\n", pks[1])
    printAssetHolding(assetID, pks[1], algodClient)

// resource https://developer.algorand.org/docs/features/asa/

Part 5: Freeze

This part 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.

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 Asset Freeze Transaction

We need to define a freezeTarget as well as a freezeState . This asset was made “freezable” when we did create the asset part. Setting an address to the freeze parameter in the MakeAssetCreateTxn method ,in Part 1, makes the asset “freezable.” Setting the freeze address parameter to “”, would make the asset unfreezable and that characteristic cannot be changed retroactively.

The sender should be freeze account and should be designated as the freeze management role, this would be account 2 using MakeAssetFreezeTxn()

    // FREEZE ASSET
    // The freeze address (Account 2) Freeze's asset for Account 3.
    assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000
    newFreezeSetting := true
    target := pks[3]
    txn, err = transaction.MakeAssetFreezeTxn(freeze, note, txParams, assetID, target, newFreezeSetting)
    if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }


Learn More
- Asset Freeze Transaction

Step 5-2. Sign Freeze Transaction

The transaction must be signed by the freeze account.

    txid, stx, err = crypto.SignTransaction(sks[2], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)

Step 5-3. Send Freeze Transaction and Print Account Information

Send the transaction to the network. After Sent, Account 3 should have all of its asset holdings frozen and should not be able to trigger a spend transaction of the asset that was frozen.

    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", txid)
    // Wait for transaction to be confirmed
    waitForConfirmation(txid,algodClient)
    // You should now see is-frozen value of true
    fmt.Printf("Asset ID: %d\n", assetID)
    fmt.Printf("Account 3: %s\n", pks[3])
    printAssetHolding(assetID, pks[3], algodClient)

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

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

Transaction ZTNLBMAUDWGKFLAZLP4KPX4HDUCBMFWNPVCCXR3ORP5UPKSWWN3Q confirmed in round 10709877
Asset ID: 13219193
Account 3: 3ZQ3SHCYIKSGK7MTZ7PE7S6EDOFWLKDQ6RYYVMT7OHNQ4UJ774LE52AQCU
{
    "amount": 10,
    "asset-id": 13219193,
    "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM",
    "is-frozen": true
} 

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


Learn More
- Algorand Block Explorers

Step 5-5. Complete Example

This example assumes that the freezeTarget account has the asset in it’s balance record.

    // FREEZE ASSET
    // The freeze address (Account 2) Freeze's asset for Account 3.
    // assetID := uint64(332920)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000
    newFreezeSetting := true
    target := pks[3]
    txn, err = transaction.MakeAssetFreezeTxn(freeze, note, txParams, assetID, target, newFreezeSetting)
    if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[2], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", txid)
    // Wait for transaction to be confirmed
    waitForConfirmation(txid,algodClient)
    // You should now see is-frozen value of true
    fmt.Printf("Asset ID: %d\n", assetID)
    fmt.Printf("Account 3: %s\n", pks[3])
    printAssetHolding(assetID, pks[3], algodClient)
// resource https://developer.algorand.org/docs/features/asa/

Part 6: Revoke

This part demonstrates the steps involved in revoking an asset. Asset revocation is also referred to as clawback functionality. Revoking an asset allows an asset’s revocation manager to transfer assets on behalf of another user. It will only work when issued by the asset’s revocation manager.

When an asset was created in Part 1, the parameter in the MakeAssetCreateTxn function 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

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.

Step 6-1. Define the Revoke Transaction Parameters

In this step, we need to define a revocation target. In this case, we are revoking assets from Account 3, who’s assets were previously frozen ion the prior Part. We are not closing out the asset, so the assetCloseTo parameter is not used. MakeAssetRevocationTxn is the function used to clawback. The clawback address (Account 2) revokes 10 assets from Account 3 and places it back with Account 1.

    // REVOKE ASSET
    // Revoke an Asset
    // The clawback address (Account 2) revokes 10 latinum from Account 3 (target)
    // and places it back with Account 1 (creator).
    assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000
    target = pks[3]
    txn, err = transaction.MakeAssetRevocationTxn(clawback, target, amount, creator, note,
        txParams, assetID)
    if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }  

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.

    txid, stx, err := crypto.SignTransaction(sks[2], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)

Step 6-3. Send Revoke Asset Transaction and Print Account Information

Broadcast the transaction to the blockchain.

    // Broadcast the transaction to the network
    sendResponse, err := algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Submitted transaction %s\n", sendResponse)  
    fmt.Printf("Transaction ID raw: %s\n", txid)
    // Wait for transaction to be confirmed
    waitForConfirmation( txid, algodClient)
    // print created assetholding for this asset and Account 3 and Account 1
    // You should see amount of 0 in Account 3, and 1000 in Account 1
    fmt.Printf("Asset ID: %d\n", assetID)
    fmt.Printf("recipient")
    fmt.Printf("Account 3: %s\n", pks[3])
    printAssetHolding(assetID, pks[3], algodClient)
    fmt.Printf("target")
    fmt.Printf("Account 1: %s\n", pks[1])
    printAssetHolding(assetID, pks[1], algodClient)

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

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

Transaction OOFND7CNV47K77KESJHEUGKQZBDHDJJD6HYZQKT3LU37GBL4GVWQ confirmed in round 10709985
Asset ID: 13219193
recipientAccount 3: 3ZQ3SHCYIKSGK7MTZ7PE7S6EDOFWLKDQ6RYYVMT7OHNQ4UJ774LE52AQCU
{
    "amount": 0,
    "asset-id": 13219193,
    "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM",
    "is-frozen": true
} 
targetAccount 1: THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM
{
    "amount": 1000,
    "asset-id": 13219193,
    "creator": "THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM"
} 


Learn More
- Algorand Block Explorers

Step 6-5. Complete Example

Here is the completed code to revoke an asset using the clawback address.

    // REVOKE ASSET
    // Revoke an Asset
    // The clawback address (Account 2) revokes 10 latinum from Account 3 (target)
    // and places it back with Account 1 (creator).
    assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000
    target = pks[3]
    txn, err = transaction.MakeAssetRevocationTxn(clawback, target, amount, creator, note,
        txParams, assetID)
    if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[2], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", txid)
    // Wait for transaction to be confirmed
    waitForConfirmation( txid, algodClient)
    // print created assetholding for this asset and Account 3 and Account 1
    // You should see amount of 0 in Account 3, and 1000 in Account 1
    fmt.Printf("Asset ID: %d\n", assetID)
    fmt.Printf("recipient")
    fmt.Printf("Account 3: %s\n", pks[3])
    printAssetHolding(assetID, pks[3], algodClient)
    fmt.Printf("target")
    fmt.Printf("Account 1: %s\n", pks[1])
    printAssetHolding(assetID, pks[1], algodClient)
// resource https://developer.algorand.org/docs/features/asa/

Part 7: Destroy

This part 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 its balance record) all units of the asset. To trigger a destroy asset transaction, the original creator of the asset must be in possession (must have in its balance record) all units of the asset.


Learn More

Destroy an Asset

Step 7-1. Create Destroy Asset Transaction

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. With all assets back in the creator’s account, the manager (Account 1) destroys the asset.

    // DESTROY ASSET
    // Destroy the asset
    // Make sure all funds are back in the creator's account. Then use the
    // Manager account (Account 1) to destroy the asset.

    assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000

    txn, err = transaction.MakeAssetDestroyTxn(manager, note, txParams, assetID)
    if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }

Step 7-2. Sign Destroy Asset Transaction

The transaction must be signed by the manager.

    txid, stx, err = crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)

Step 7-3. Send Destroy Asset Transaction and Print Account Information

Broadcast the transaction to the blockchain.

    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", txid)
    // Wait for transaction to be confirmed
    waitForConfirmation(txid,algodClient)
    fmt.Printf("Asset ID: %d\n", assetID)   
    fmt.Printf("Account 3 must do a transaction for an amount of 0, \n" )
    fmt.Printf("with a closeRemainderTo to the creator account, to clear it from its accountholdings. \n")
    fmt.Printf("For Account 1, nothing should print after this as the asset is destroyed on the creator account \n")

    // print created asset and asset holding info for this asset (should not print anything)

    printCreatedAsset(assetID, pks[1], algodClient)
    printAssetHolding(assetID, pks[1], algodClient)

Step 7-4. Check the transaction on the block explorer

Your output should look similar to this:

Transaction 3WONXA2O7Z4XOVHFMYY353G3XCEYFHKN4QHKY377MMGGXFVHW5UA confirmed in round 10710164
Asset ID: 13219193
Account 3 must do a transaction for an amount of 0, 
with a closeRemainderTo to the creator account, to clear it from its accountholdings. 
For Account 1, nothing should print after this as the asset is destroyed on the creator account 

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-5. Complete Example

Here is the completed code to destroy an asset.

    // DESTROY ASSET
    // Destroy the asset
    // Make sure all funds are back in the creator's account. Then use the
    // Manager account (Account 1) to destroy the asset.

    assetID := uint64(your assetID)
    // Get network-related transaction parameters and assign
    txParams, err = algodClient.SuggestedParams().Do(context.Background())
    if err != nil {
        fmt.Printf("Error getting suggested tx params: %s\n", err)
        return
    }
    // comment out the next two (2) lines to use suggested fees
    txParams.FlatFee = true
    txParams.Fee = 1000

    txn, err = transaction.MakeAssetDestroyTxn(manager, note, txParams, assetID)
    if err != nil {
        fmt.Printf("Failed to send txn: %s\n", err)
        return
    }
    txid, stx, err = crypto.SignTransaction(sks[1], txn)
    if err != nil {
        fmt.Printf("Failed to sign transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID: %s\n", txid)
    // Broadcast the transaction to the network
    sendResponse, err = algodClient.SendRawTransaction(stx).Do(context.Background())
    if err != nil {
        fmt.Printf("failed to send transaction: %s\n", err)
        return
    }
    fmt.Printf("Transaction ID raw: %s\n", txid)
    // Wait for transaction to be confirmed
    waitForConfirmation(txid,algodClient)
    fmt.Printf("Asset ID: %d\n", assetID)   
    fmt.Printf("Account 3 must do a transaction for an amount of 0, \n" )
    fmt.Printf("with a closeRemainderTo to the creator account, to clear it from its accountholdings. \n")
    fmt.Printf("For Account 1, nothing should print after this as the asset is destroyed on the creator account \n")

    // print created asset and asset holding info for this asset (should not print anything)

    printCreatedAsset(assetID, pks[1], algodClient)
    printAssetHolding(assetID, pks[1], algodClient)

// resource https://developer.algorand.org/docs/features/asa/

Completed code for this tutorial can be found here assetExample.go.

algorand standard assets

asset freeze

assets

optin

asset opt-in

asset creation

asset transfer

asa

create assets

asset revoke

asset destroy

change asset

destroy

revoke

December 02, 2020