Java SDK

The Java SDK provides a set of packages to interact with the algod and kmd processes. In addition, it also provides a few more packages for doing stand-alone functions like creating and signing a transaction, creating an account, getting a backup mnemonic and restoring an account without having to connect to an Algorand node.

More documentation is available on github and within the JavaDocs.

Install the SDK

The Java SDK is available in Maven’s central repository and can be used in your Maven project by using the following dependency.


<dependency>
    <groupId>com.algorand</groupId>
    <artifactId>algosdk</artifactId>
    <version>1.0.1</version>
</dependency>

Package Overview

The com.algorand.algosdk.kmd.client and com.algorand.algosdk.algod.client packages contain classes that provide HTTP clients for the algod and kmd node processes. algod is the Algorand protocol daemon, responsible for reaching consensus with the network and participating in the Algorand protocol. You can use it to check the status of the blockchain, read a block, look at transactions, look at balances, or submit a signed transaction. kmd is the key management daemon. It is responsible for managing spending key material, signing transactions, and managing wallets.

The com.algorand.algosdk.account Package contains the primary functions for creating, restoring and backing up accounts and signing transactions without having to connect to an Algrorand node.

The com.algorand.algosdk.util package contains classes to handle setting up a crypto provider and the Encoder class, which can be used to serialize messages for the algod/kmd APIs and the network.

The com.algorand.algosdk.mnemonic Package contains the Mnemonic and Wordlist classes, which are used for generating the backup mnemonic with support for turning 32-byte keys into checksummed, human-readable mnemonics (and going from mnemonics back to keys).

The com.algorand.algosdk.transaction Package contains the Transaction and the SignedTransation classes, which are used to create transactions and signing them without having to be connected to an Algorand node.

The com.algorand.algosdk.crypto Package contains classes for creating the data structures used within the other Packages.

The com.algorand.algosdk.auction Package contains classes used to create and sign bids for Algorand auctions.

Start the algod and kmd Processes

When using the SDK, it can interact with the algod, kmd or both processes. The algod process starts automatically when nodes are started. The kmd process automatically starts when called using the goal command line tool but with a 60-second timeout. This means it will shut down when no activity is detected for 60 seconds. To make sure the kmd is available for use with the SDK you can use the goal kmd command to start the kmd process with an indefinite timeout. Typically this will be just entering the command:

goal kmd start -d <data-dir>

Once the kmd is started, it will create  kmd.net and kmd.token files within the data directory for kmd. The algod data directory also has the algod.net and algod.token files. These files contain the host and port number for the REST endpoints and API tokens that the SDK will need to communicate with their respective processes.

For more information on the algod and kmd processes see the Node Overview documentation.

Examples

The following sections cover some of the basic functionality that is provided within the SDK. Some examples will require connecting to an Algorand node to communicate with either the kmd or algod processes.

Creating an Account, Backing up and Restoring it

You do not need to connect to an Algorand node to create an Account. In a later example, you will see how to connect to a node and interact with the nodes kmd process to manage wallets. To create an account and get the backup phrase to restore the account you can use code similar to the following:


    public static void main(String args[]) throws Exception {
            //Create a random new account
            Account act = new Account();
            //Get the new account address
            Address addr = act.getAddress();
            
            //Get the backup phrase
            String backup = act.toMnemonic();

            System.out.println("Account Address: " + addr.toString());
            System.out.println("Backup Phrase: " + backup);

            //recover an account
            Account recoveredAccount = new Account(backup);
            System.out.println("Recovered Account Address: " + recoveredAccount.getAddress().toString());
    }

Retrieving Latest Block Information

You can get the latest block from a node by connecting to its algod process and calling the getStatus method to get the latest processed block and then use that as a parameter to the getBlock function:


    public static void main(String args[]) throws Exception {
        
        //Get the values for the following two settings in the
        //algod.net and algod.token files within the data directory 
        //of your node.
        final String ALGOD_API_ADDR = "http://yourhost:yourport";
        final String ALGOD_API_TOKEN = "yourtoken";

        //Create an instance of the algod API client
        AlgodClient client = (AlgodClient) new AlgodClient().setBasePath(ALGOD_API_ADDR);
        ApiKeyAuth api_key = (ApiKeyAuth) client.getAuthentication("api_key");
        api_key.setApiKey(ALGOD_API_TOKEN);
        AlgodApi algodApiInstance = new AlgodApi(client); 
        // Get the lastest Block
        try {
            NodeStatus status = algodApiInstance.getStatus();
            System.out.println("Algorand network status: " + status);
            //Get block for the latest round
            Block blk = algodApiInstance.getBlock(status.getLastRound());
            System.out.println( blk.toString());
        } catch (ApiException e) {
            System.err.println("Exception when calling algod#getStatus or getBlock");
            e.printStackTrace();
        }

Creating a wallet and adding an account to the wallet

To create a kmd managed wallet, you will need to connect to a node. The following example shows how to connect to a node and create a wallet. In addition, it shows how to create an account using the wallet.


    public static void main(String args[]) throws Exception {
        //Get the values for the following two settings in the
        //kmd.net and kmd.token files within the data directory 
        //of your node.        
        final String KMD_API_ADDR = "http://yourhost:yourport";
        final String KMD_API_TOKEN = "yourtoken";

        // Create a wallet with kmd rest api
        KmdClient client = new KmdClient();
        client.setBasePath(KMD_API_ADDR);
        // Configure API key authorization: api_key
        ApiKeyAuth api_key = (ApiKeyAuth) client.getAuthentication("api_key");
        api_key.setApiKey(KMD_API_TOKEN);
        KmdApi kmdApiInstance = new KmdApi(client);

        APIV1POSTWalletResponse wallet;
        try {
            //create the REST request
            CreateWalletRequest req = new CreateWalletRequest()
                    .walletName("mywallet")
                    .walletPassword("test")
                    .walletDriverName("sqlite");
            //create the wallet        
            wallet = kmdApiInstance.createWallet(req);
            String wallId = wallet.getWallet().getId();
            //create REST request to get wallet token
            InitWalletHandleTokenRequest walletHandleRequest = new InitWalletHandleTokenRequest();
            walletHandleRequest.setWalletId(wallId);
            walletHandleRequest.setWalletPassword("test");
            //execute request to get the wallet token
            String token = kmdApiInstance.initWalletHandleToken(walletHandleRequest).getWalletHandleToken();
            //create REST request to create new key with wallet token
            GenerateKeyRequest genAcc = new GenerateKeyRequest();
            genAcc.setWalletHandleToken(token);
            //execute request to generate new key(account)
            String newAccount = kmdApiInstance.generateKey(genAcc).getAddress();
            System.out.println("New Address = " + newAccount);

        } catch (ApiException e) {
            e.printStackTrace();
        }
    }

Backing up a Wallet

Both Accounts and Wallets can be recovered using backup mnemonics. By using a backup mnemonic for the wallet, all generated accounts from the original wallet can be regenerated. Imported accounts must be backup separately. The following example shows how to generate the backup mnemonic for a wallet. Always store backup phrases in a safe location.


    public static void main(String args[]) throws Exception {
        // Get the values for the following two settings in the
        // kmd.net and kmd.token files within the data directory
        // of your node.
        final String KMD_API_ADDR = "http://yourhost:yourport";
        final String KMD_API_TOKEN = "yourtoken";

        // Create a wallet with kmd rest api
        KmdClient client = new KmdClient();
        client.setBasePath(KMD_API_ADDR);
        // Configure API key authorization: api_key
        ApiKeyAuth api_key = (ApiKeyAuth) client.getAuthentication("api_key");
        api_key.setApiKey(KMD_API_TOKEN);
        KmdApi kmdApiInstance = new KmdApi(client);

        APIV1GETWalletsResponse wallets;
        String walletId = null;
        try {
            // Get all wallets from kmd
            // Loop through them and find the one we
            // are interested in them
            wallets = kmdApiInstance.listWallets();
            for (APIV1Wallet wal : wallets.getWallets()) {
                System.out.println(wal.getName());
                if (wal.getName().equals("mywallet")) {
                    walletId = wal.getId();
                    break;
                }
            }
            if (walletId != null) {
                // create REST request to get wallet token
                InitWalletHandleTokenRequest walletHandleRequest = new InitWalletHandleTokenRequest();
                walletHandleRequest.setWalletId(walletId);
                walletHandleRequest.setWalletPassword("test");
                // execute request to get the wallet token
                String token = kmdApiInstance.initWalletHandleToken(walletHandleRequest).getWalletHandleToken();
                // create REST request to create new key with wallet token
                ExportMasterKeyRequest expRequest = new ExportMasterKeyRequest();
                expRequest.setWalletHandleToken(token);
                expRequest.setWalletPassword("test");

                APIV1POSTMasterKeyExportResponse expResponse = kmdApiInstance.exportMasterKey(expRequest);
                String mnem = Mnemonic.fromKey(expResponse.getMasterDerivationKey());
                
                System.out.println("Backup Phrase = " + mnem);

            }else{
                System.out.println("Did not Find Wallet");
            }

        } catch (ApiException e) {
            e.printStackTrace();
        }
    }

Restoring a Wallet

To restore a wallet you can use the static functions in the com.algorand.sdk.Mnemonic class. You can then regenerate the accounts using the same method described in the creating a wallet and adding an account section described earlier on this page.


    public static void main(String args[]) throws Exception {
        //Get the values for the following two settings in the
        //kmd.net and kmd.token files within the data directory 
        //of your node.        
        final String KMD_API_ADDR = "http://yourhost:yourport";
        final String KMD_API_TOKEN = "yourtoken";
        final String BACKUP_PHRASE = "your25wordbackupphrase";
        // Create a wallet with kmd rest api
        KmdClient client = new KmdClient();
        client.setBasePath(KMD_API_ADDR);
        // Configure API key authorization: api_key
        ApiKeyAuth api_key = (ApiKeyAuth) client.getAuthentication("api_key");
        api_key.setApiKey(KMD_API_TOKEN);
        KmdApi kmdApiInstance = new KmdApi(client);
        byte[] mkd = Mnemonic.toKey(BACKUP_PHRASE);
        APIV1POSTWalletResponse wallet;
        try {
            //create the REST request
            CreateWalletRequest req = new CreateWalletRequest()
                    .walletName("mywallet")
                    .walletPassword("test")
                    .masterDerivationKey(mkd)
                    .walletDriverName("sqlite");
            //create the wallet        
            wallet = kmdApiInstance.createWallet(req);
            String wallName = wallet.getWallet().getName();
            System.out.println("Restored Wallet = " + wallName);

        } catch (ApiException e) {
            e.printStackTrace();
        }
    }

Signing and Submitting a Transaction

You can create a transaction and sign it without connecting to an Algorand node, but to submit to the network you need to connect to a nodes algod process and submit it. The following example shows how to use the Transaction and SignedTransaction classes from the com.algorand.algosdk.transaction package to create and sign the transaction and then a connection is created to a local node submit the transaction. The example polls the node to wait for the transaction to be confirmed. Finally, the example also shows how to use the notes field to store additional information in the transaction.


    public static void main(String args[]) throws Exception {
        final String ALGOD_API_ADDR = "http://yourhost:yourport";
            final String ALGOD_API_TOKEN = "yourtoken";

        // Create the node algod client
        AlgodClient client = (AlgodClient) new AlgodClient().setBasePath(ALGOD_API_ADDR);
        ApiKeyAuth api_key = (ApiKeyAuth) client.getAuthentication("api_key");
        api_key.setApiKey(ALGOD_API_TOKEN);
        AlgodApi algodApiInstance = new AlgodApi(client);

        // Using a backup mnemonic to recover the source account to send tokens from
        final String SRC_ACCOUNT = "you25wordbackupphrase";
        final String DEST_ADDR = "KV2XGKMXGYJ6PWYQA5374BYIQBL3ONRMSIARPCFCJEAMAHQEVYPB7PL3KU";
        // if an account drops below the minimum where should the remaining funds be sent
        final String REM_ADDR = "KV2XGKMXGYJ6PWYQA5374BYIQBL3ONRMSIARPCFCJEAMAHQEVYPB7PL3KU";

        // Get last round and suggested tx fee
        BigInteger suggestedFeePerByte = BigInteger.valueOf(1);
        BigInteger firstRound = BigInteger.valueOf(301);
        String genId = null;
        Digest genesisHash = null;
        try {
            // Get suggested parameters from the node
            TransactionParams params = algodApiInstance.transactionParams();
            suggestedFeePerByte = params.getFee();
            firstRound = params.getLastRound();
            System.out.println("Suggested Fee: " + suggestedFeePerByte);
            // genesisID and genesisHash are optional on testnet, but will be mandatory on release
            // to ensure that transactions are valid for only a single chain. GenesisHash is preferred.
            // genesisID will be deprecated soon.
            genId = params.getGenesisID();
            genesisHash = new Digest(params.getGenesishashb64());

        } catch (ApiException e) {
            System.err.println("Exception when calling algod#transactionParams");
            e.printStackTrace();
        }


        // Add some notes to the transaction
        byte[] notes = "These are some notes encoded in some way!".getBytes();

        
        // Instantiate the transaction
        Account src = new Account(SRC_ACCOUNT);
        BigInteger amount = BigInteger.valueOf(1000);
        BigInteger lastRound = firstRound.add(BigInteger.valueOf(1000)); // 1000 is the max tx window
        // Setup Transaction
        // Use a fee of 0 as we will set the fee per 
        // byte when we sign the tx and overwrite it
        Transaction tx = new Transaction(src.getAddress(),
                BigInteger.valueOf(0), firstRound, lastRound, notes, genId, genesisHash, amount, new Address(DEST_ADDR), new Address(REM_ADDR));

        // Sign the Transaction
        SignedTransaction signedTx = src.signTransactionWithFeePerByte(tx, suggestedFeePerByte);
        System.out.println("Signed transaction with txid: " + signedTx.transactionID);
        // Send the transaction to the network
        try {
            // Msgpack encode the signed transaction
            byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTx);
            TransactionID id = algodApiInstance.rawTransaction(encodedTxBytes);
            System.out.println("Successfully sent tx with id: " + id);
        } catch (ApiException e) {
            // This is generally expected but should give us an informative error message.
            System.err.println("Exception when calling algod#rawTransaction: " + e.getResponseBody());
        }

        // Wait for transaction to be confirmed
        while(true) {
            try {
                //Check the pending tranactions
                com.algorand.algosdk.algod.client.model.Transaction b3 = algodApiInstance.pendingTransactionInformation(signedTx.transactionID);
                if (b3.getRound() != null && b3.getRound().longValue() > 0) {
                    System.out.println("Transaction " + b3.getTx() + " confirmed in round " + b3.getRound().longValue());
                    break;
                } else {
                    System.out.println("Waiting for confirmation... (pool error, if any:)" + b3.getPoolerror());
                }
            } catch (ApiException e) {
                System.err.println("Exception when calling algod#pendingTxInformation: " + e.getMessage());
            }
        }

        // Read the transaction
        try {
            com.algorand.algosdk.algod.client.model.Transaction rtx = algodApiInstance.transactionInformation(DEST_ADDR, signedTx.transactionID);
            System.out.println("Transaction information (with notes): " + rtx.toString());
            System.out.println("Decoded notes: [" + new String(rtx.getNoteb64()) + "]");
        } catch (ApiException e) {
            System.err.println("Exception when calling algod#transactionInformation: " + e.getCode());
        }
    }

Finding Transactions

Once a transaction is submitted and finalized into a block, you can find it later using several methods. If the node's archival property is not set to true, you will only be allowed to search a limited number of local blocks on your node. If the archival property is set to true the entire blockchain will be available for searching. It is important to understand that changing the archival mode property only affects future block storage, so if you set this to true and want to get the oldest blocks of the ledger stored locally you will need to remove your existing ledger files and let the node rebuild the ledger. See the node configuration settings page for details.


The SDK provides several ways to find transactions that were submitted to the Blockchain. The previous example used the transactionInformation method to locate a specific transaction that was recently submitted. You can also find transactions in a block range if you have the address of either the sender or the receiver of the transaction. The following example uses the transactions function to find transactions for a specific account in the last 1000 rounds.


    public static void main(String args[]) throws Exception {
        final String ALGOD_API_ADDR = "http://yourhost:yourport";
        final String ALGOD_API_TOKEN = "yourtoken";
        final String SRC_ACCOUNT_BACKUP = "your25wordbackupphrase";

        AlgodClient client = (AlgodClient) new AlgodClient().setBasePath(ALGOD_API_ADDR);
        ApiKeyAuth api_key = (ApiKeyAuth) client.getAuthentication("api_key");
        api_key.setApiKey(ALGOD_API_TOKEN);
        AlgodApi algodApiInstance = new AlgodApi(client);
        // First, get network status
        try {
            NodeStatus status = algodApiInstance.getStatus();
            BigInteger lastRound = status.getLastRound();
            BigInteger firstRound = lastRound.subtract(BigInteger.valueOf(1000)); // 1000 is the max tx window
            Account recoveredAccount = new Account(SRC_ACCOUNT_BACKUP);
            //Get the transactions for the address in the last 1k rounds
            //Note that this call requires that the node is an archival node as we are going back 1k rounds
            TransactionList tList = algodApiInstance.transactions(recoveredAccount.getAddress().toString(), firstRound, lastRound);
            for (Transaction tx : tList.getTransactions()) {
                System.out.println(tx.toString()); 
            }
            System.out.println("Finished");
        } catch (ApiException e) {
            e.printStackTrace();
        }
 
    }