Tutorials
No Results
Tutorial Thumbnail Image

Beginner · 15 minutes or less

Creating a Java Transaction with the PureStake API

Creating a transaction and sending it in to the network programmatically are foundational tasks for working with a blockchain. Using the PureStake API allows a developer full access to the complete Algorand MainNet/Testnet ledgers without running any infrastructure, but does require a few minor changes to the code.

Requirements

Background

Steps

1. Set Up Dependencies

Signing and submitting a transaction in Java requires a fair number of imports. Here is a skeleton sample of a Java package with the required dependencies imported:

import java.math.BigInteger;
import java.security.GeneralSecurityException;
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.model.NodeStatus;
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;
import com.fasterxml.jackson.core.JsonProcessingException;

public class SubmitTx {
    public static void main(String[] args) throws GeneralSecurityException {

    }
}

2. Setting Up the client to Connect to the PureStake API

First, you’ll need to set the default configuration values for PureStake’s API. This process requires to add an additional header to the AlgodClient.

To do that, enter the following:

final String ALGOD_API_ADDR = "https://testnet-algorand.api.purestake.io/ps1";
final String ALGOD_API_TOKEN = "YOUR API KEY HERE";

AlgodClient client = new AlgodClient();
client.setBasePath(ALGOD_API_ADDR);
client.addDefaultHeader("X-API-Key", ALGOD_API_TOKEN);

3. Recover Account

Now you’ll need to recover your account using your mnemonic.

Important Note: Mnemonics control your account. Never share them or store insecurely. The code below is for demonstration purposes only.

final String SRC_ACCOUNT = "YOUR MNEMONIC HERE"; 
Account src = new Account(SRC_ACCOUNT);

4. Get and Set Transaction Parameters

Get the transaction parameters from the blockchain using the client object and set the amount and destination address.

BigInteger feePerByte;
String genesisID;
Digest genesisHash;
long firstRound = 0L;
try {
    TransactionParams params = algodApiInstance.transactionParams();
    feePerByte = params.getFee();
    genesisHash = new Digest(params.getGenesishashb64());
    genesisID = params.getGenesisID();
    NodeStatus s = algodApiInstance.getStatus();
    firstRound = s.getLastRound().longValue();
} catch (ApiException e) {
    throw new RuntimeException("Could not get params", e);
}
final String DEST_ADDR = "AEC4WDHXCDF4B5LBNXXRTB3IJTVJSWUZ4VJ4THPU2QGRJGTA3MIDFN3CQA";
long amount = 1L;
long lastRound = firstRound + 1000;

5. Create and Sign Transaction

Create a new transaction using the parameters defined above and sign it.

Transaction tx = new Transaction(src.getAddress(), new Address(DEST_ADDR), amount, firstRound, lastRound, genesisID, genesisHash);
SignedTransaction signedTx = src.signTransactionWithFeePerByte(tx, feePerByte);

6. Include Verification Method

After the transaction is complete and sent in, this method is called to verify that the transaction has been included in the blockchain:

// Function from Algorand Inc. - utility for waiting on a transaction confirmation
public static void waitForConfirmation(AlgodApi algodApiInstance, String txID) throws Exception {
    long lastRound = algodApiInstance.getStatus().getLastRound().longValue();
    while (true) {
        try {
            // Check the pending transactions
            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);
        }
    }
}

7. Submit and Verify Transaction

Lastly, send the signed transaction to the blockchain.

try {
    byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTx);
    TransactionID id = algodApiInstance.rawTransaction(encodedTxBytes);
    System.out.println("Successfully sent tx with id: " + id);
    waitForConfirmation(algodApiInstance, id.getTxId());
} catch (ApiException e) {
    System.err.println("Exception when calling algod#rawTransaction: " + e.getResponseBody());
} catch (JsonProcessingException e) {
    System.err.println("Exception when calling algod#rawTransaction: " + e);
} catch (java.lang.Exception e) {
    System.err.println("Exception when calling algod#rawTransaction: " + e);
}

You should see a response similar to this one:

Transaction YFMPNMI773YHBM765ZZLA5PMBMQBRZNB3MCUFSYVRVGUW3DCZ6PQ confirmed in round 6007773.

8. Run All of the Code Together

If you’d rather just drop in the code in its entirety, copy below. You’ll need to enter your API key, your mnemonic, and some other specifics in order for it to function properly.

import java.math.BigInteger;
import java.security.GeneralSecurityException;

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.model.NodeStatus;
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;
import com.fasterxml.jackson.core.JsonProcessingException;

public class SubmitTx {
    // Function from Algorand Inc. - Utility function to wait on a transaction to be confirmed
    public static void waitForConfirmation(AlgodApi algodApiInstance, String txID) throws Exception {
        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);
            }
        }
    }

    public static void main(String[] args) throws GeneralSecurityException {
        final String ALGOD_API_ADDR = "https://testnet-algorand.api.purestake.io/ps1";
        final String ALGOD_API_TOKEN = "YOUR API KEY HERE";

        final String SRC_ACCOUNT = "YOUR MNEMONIC HERE"; 
        final String DEST_ADDR = "AEC4WDHXCDF4B5LBNXXRTB3IJTVJSWUZ4VJ4THPU2QGRJGTA3MIDFN3CQA";

        //Setup HTTP client w/guest key provided by PureStake
        AlgodClient client = new AlgodClient();
        client.addDefaultHeader("X-API-Key", ALGOD_API_TOKEN);
        client.setBasePath(ALGOD_API_ADDR);

        AlgodApi algodApiInstance = new AlgodApi(client);

        Account src = new Account(SRC_ACCOUNT);
        System.out.println("My Address: " + src.getAddress());

        BigInteger feePerByte;
        String genesisID;
        Digest genesisHash;
        long firstRound = 0L;
        try {
            TransactionParams params = algodApiInstance.transactionParams();
            feePerByte = params.getFee();
            genesisHash = new Digest(params.getGenesishashb64());
            genesisID = params.getGenesisID();
            System.out.println("Suggested Fee: " + feePerByte);
            NodeStatus s = algodApiInstance.getStatus();
            firstRound = s.getLastRound().longValue();
            System.out.println("Current Round: " + firstRound);
        } catch (ApiException e) {
            throw new RuntimeException("Could not get params", e);
        }

        long amount = 1L;
        long lastRound = firstRound + 1000; // 1000 is the max tx window
        Transaction tx = new Transaction(src.getAddress(), new Address(DEST_ADDR), amount, firstRound, lastRound, genesisID, genesisHash);
        SignedTransaction signedTx = src.signTransactionWithFeePerByte(tx, feePerByte);
        System.out.println("Signed transaction with txid: " + signedTx.transactionID);

        // send the transaction to the network
        try {
            byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTx);
            TransactionID id = algodApiInstance.rawTransaction(encodedTxBytes);
            System.out.println("Successfully sent tx with id: " + id);
            waitForConfirmation(algodApiInstance, id.getTxId());
        } 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());
        } catch (JsonProcessingException e) {
            System.err.println("Exception when calling algod#rawTransaction: " + e);
        } catch (java.lang.Exception e) {
            System.err.println("Exception when calling algod#rawTransaction: " + e);
        }
    }
}

transactions

PureStake

java

v1 api

April 11, 2020