Tutorials
No Results
Tutorial Image

Beginner · 15 minutes or less

Opt-In to an Asset using Java

This tutorial 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 AssetAcceptTransactionBuilder(). This effectivly sends a 0 amount of the asset to yourself (to the account wanting to receive the asset).


Learn More
- Minimum Account Balance Requirement

Requirements

Recommended sequence, if you are new to ASA:

Optional:

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.

1. Define Opt-In Transaction Parameters

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 Create an Asset tutorial.

// copy assetID from Create an Asset tutorial 
// and replace valueOf
BigInteger assetID = BigInteger.valueOf(your asset ID); 

2. Create Opt-In Transaction

Opt-In to receiving an asset, and update the fee.

// Opt in to Receiving the Asset
try {
    cp = ex.getChangingParms(algodApiInstance);
} catch (ApiException e) {
    e.printStackTrace();
    return;
}
Transaction tx = Transaction.AssetAcceptTransactionBuilder().acceptingAccount(acct3.getAddress()).fee(0)
    .firstValid(cp.firstRound).lastValid(cp.lastRound).genesisHash(cp.genHash).assetIndex(assetID).build();
// Update the fee based on the network suggested fee
Account.setFeeByFeePerByte(tx, cp.fee);

3. Sign Transaction

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

// The transaction must be signed by the current manager account
SignedTransaction signedTx = acct3.signTransaction(tx);

4. Send the Transaction to the network

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

com.algorand.algosdk.algod.client.model.Account act;
// send the transaction to the network and
try {
    TransactionID id = ex.submitTransaction(signedTx);
    System.out.println("Transaction ID: " + id);
    ex.waitForConfirmation(signedTx.transactionID);
    // We can now list the account information for acct3
    // and see that it can accept the new asseet
    act = algodApiInstance.accountInformation(acct3.getAddress().toString());
    AssetHolding ah = act.getHolding(myassetID);
    System.out.println("Account 3 Asset Holding: " + ah.getAmount());
} catch (Exception e) {
    e.printStackTrace();
    return;
}

5. Check the transaction on a block explorer

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

Transaction ID: class TransactionID {
    txId: DLAZLYROZXCUWYCCEUVMH5IBXC26HHKWATI43GVHHJIEVQTQ5UYA
}
Transaction DLAZLYROZXCUWYCCEUVMH5IBXC26HHKWATI43GVHHJIEVQTQ5UYA confirmed in round 5827415
Account 3 Asset Holding: 0

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


Learn More
- Algorand Block Explorers

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.

package com.algorand.javatest;

import java.math.BigInteger;

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

/**
 * Show Creating, modifying, sending and listing assets
 */
public class TutorialAssetOptIn {

    public AlgodApi algodApiInstance = null;

    // utility function to connect to a node
    private AlgodApi connectToNetwork() {

        //sandbox
        final String ALGOD_API_ADDR = "http://localhost:4001";
        final String ALGOD_API_TOKEN = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

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

    // Inline class to handle changing block parameters
    // Throughout the example
    public class ChangingBlockParms {
        public BigInteger fee;
        public BigInteger firstRound;
        public BigInteger lastRound;
        public String genID;
        public Digest genHash;

        public ChangingBlockParms() {
            this.fee = BigInteger.valueOf(0);
            this.firstRound = BigInteger.valueOf(0);
            this.lastRound = BigInteger.valueOf(0);
            this.genID = "";
            this.genHash = null;
        }
    };

    // Utility function to wait on a transaction to be confirmed
    public void waitForConfirmation(String txID) throws Exception {
        if (algodApiInstance == null)
            connectToNetwork();
        long lastRound = algodApiInstance.getStatus().getLastRound().longValue();
        while (true) {
            try {
                // Check the pending tranactions
                com.algorand.algosdk.algod.client.model.Transaction pendingInfo = algodApiInstance
                    .pendingTransactionInformation(txID);
                if (pendingInfo.getRound() != null && pendingInfo.getRound().longValue() > 0) {
                    // Got the completed Transaction
                    System.out.println("Transaction " + pendingInfo.getTx() + " confirmed in round " +
                        pendingInfo.getRound().longValue());
                    break;
                }
                lastRound++;
                algodApiInstance.waitForBlock(BigInteger.valueOf(lastRound));
            } catch (Exception e) {
                throw (e);
            }
        }
    }

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

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

    public static void main(String args[]) throws Exception {

        TutorialAssetOptIn ex = new TutorialAssetOptIn();
        AlgodApi algodApiInstance = ex.connectToNetwork();

        // recover example accounts
        // final String account1_mnemonic = "<your-25-word-mnemonic>";             
        // final String account2_mnemonic = "<your-25-word-mnemonic>";             
        // final String account3_mnemonic = "<your-25-word-mnemonic>";  

        final String account1_mnemonic = "neutral blade diesel guard punch glide pepper cancel wise soul legend second capital load hover extra witness forward enlist flee pitch taxi impulse absent common";
        final String account2_mnemonic = "cute ask spread arena glide way feed else this case parade fly diamond cargo satoshi clever pear apple dream champion effort near flee absent gate";
        final String account3_mnemonic = "number define pet usual brave day traffic peasant style goddess wisdom cart mouse fork ecology jungle impose border dad please worth surprise sort abstract mechanic";

        Account acct1 = new Account(account1_mnemonic);
        Account acct2 = new Account(account2_mnemonic);
        Account acct3 = new Account(account3_mnemonic);
        System.out.println("Account1: " + acct1.getAddress());
        System.out.println("Account2: " + acct2.getAddress());
        System.out.println("Account3: " + acct3.getAddress());

        // get changing network parameters
        ChangingBlockParms cp = null;
        try {
            cp = ex.getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }

        // insert opt in here
        // copy assetID from Create an Asset tutorial 
        // for value, replacing 0
        BigInteger assetID = BigInteger.valueOf(0);

        // Opt in to Receiving the Asset
        try {
            cp = ex.getChangingParms(algodApiInstance);
        } catch (ApiException e) {
            e.printStackTrace();
            return;
        }
        Transaction tx = Transaction.AssetAcceptTransactionBuilder().acceptingAccount(acct3.getAddress()).fee(0)
            .firstValid(cp.firstRound).lastValid(cp.lastRound).genesisHash(cp.genHash).assetIndex(assetID).build();

        // Update the fee based on the network suggested fee
        Account.setFeeByFeePerByte(tx, cp.fee);
        // The transaction must be signed by the current manager account
        SignedTransaction signedTx = acct3.signTransaction(tx);
        com.algorand.algosdk.algod.client.model.Account act;
        // send the transaction to the network and
        try {
            TransactionID id = ex.submitTransaction(signedTx);
            System.out.println("Transaction ID: " + id);
            ex.waitForConfirmation(signedTx.transactionID);
            // We can now list the account information for acct3
            // and see that it can accept the new asseet
            act = algodApiInstance.accountInformation(acct3.getAddress().toString());
            AssetHolding ah = act.getHolding(assetID);
            System.out.println("Account 3 Asset Holding: " + ah.getAmount());
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }
}
// resource https://developer.algorand.org/docs/features/asa/
}

Next Tutorial: Transfer Asset using Java

java

asset transfer

asset opt-in

optin

asset

April 12, 2020