Split Contract Template with Java
This tutorial is intended to help you call a Split Contract using Java. Templates are prebuilt TEAL programs that allow parameters to be injected into them from the SDKs that configure the contract. In this example, we are going to instantiate the Split Template and show how it can be used with a transaction.
Requirements
- Algorand Java SDK
- Access to an Algorand node
Background
Algorand provides many templates for Smart Contract implementation in the SDKs. The Split Contract is just one of the templates and is described in the reference documentation. Split Contracts are contract accounts that can disburse funds to two accounts with a defined ratio. Additionally, a minimum withdrawal amount can be set. If the funds are not claimed after a certain period of time, the original owner can reclaim the remaining funds.
Steps
1. Assign the Contract Receiver Accounts
The Split template can be instantiated with a set of predefined parameters that configure the Split contract. These parameters should not be confused with Transaction parameters that are passed into the contract when using the Split. These parameters configure how the Split will function:
TMPL_RCV1
: the first recipient of the fundsTMPL_RCV2
: the second recipient of the fundsTMPL_RAT1
: for each TMPL_RAT2 microAlgos received by TMPL_RCV2, TMPL_RAT1 specifies how many are received by TMPL_RCV1TMPL_RAT2
: for each TMPL_RAT1 microAlgos received by TMPL_RCV1, TMPL_RAT2 specifies how many are received by TMPL_RCV2TMPL_MINPAY
: the minimum number of microAlgos that must be received by TMPL_RCV1 for a split withdrawal to succeedTMPL_TIMEOUT
: the round after which funds may be closed back to TMPL_OWNTMPL_OWN
: the address that funds may be closed to after TMPL_TIMEOUTTMPL_FEE
: the maximum fee that may be used by any individual transaction approved by this contract
So for example, if you want funds to be split such that for every 10 microAlgos, 3 will go to receiver 1 and 7 will go to receiver 2, TMPL_RAT1
will be set to 3 and TMPL_RAT2
will be set to 7. If the amount of a transaction can not be split with the desired ratio, the transactions will fail. The TMPL_MINPAY
parameter must also be met for the transaction to succeed. So in this example, if TMPL_MINPAY
was set to 3000, then the total transaction could be no less than 10000 microAlgos.
For this tutorial, we first need to define the owner of the contract, and the two receivers.
package com.algorand.algosdk.teal;
import java.math.BigInteger;
import com.algorand.algosdk.algod.client.AlgodClient;
import com.algorand.algosdk.algod.client.api.AlgodApi;
import com.algorand.algosdk.algod.client.auth.ApiKeyAuth;
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.templates.ContractTemplate;
import com.algorand.algosdk.templates.Split;
/**
* Sign and Submit a transaction example using HTLC
*
*/
public class SubmitSplitTransaction
{
public static void main(String args[]) throws Exception {
// Setup conection parameters
final String ALGOD_API_ADDR = "http://<your-algod-host>:<your-algod-port>";
final String ALGOD_API_TOKEN = "<your-api-token>";
// Instaniate the algod wrapper
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);
Address owner = new Address("726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM");
Address receiver1 = new Address("THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM");
Address receiver2 = new Address("AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU");
}
}
2. Create Split Template
The Split template can now be instantiated with specific parameters.
package com.algorand.algosdk.teal;
import java.math.BigInteger;
import com.algorand.algosdk.algod.client.AlgodClient;
import com.algorand.algosdk.algod.client.api.AlgodApi;
import com.algorand.algosdk.algod.client.auth.ApiKeyAuth;
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.templates.ContractTemplate;
import com.algorand.algosdk.templates.Split;
/**
* Sign and Submit a transaction example using Split
*
*/
public class SubmitSplitTransaction
{
public static void main(String args[]) throws Exception {
// Setup conection parameters
final String ALGOD_API_ADDR = "http://<your-algod-host>:<your-algod-port>";
final String ALGOD_API_TOKEN = "<your-api-token>";
// Instaniate the algod wrapper
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);
Address owner = new Address("726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM");
Address receiver1 = new Address("THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM");
Address receiver2 = new Address("AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU");
// Addition Inputs to the Template
int expiryRound = 5000000;
int maxFee = 2000;
int minPay = 3000;
Integer ratn = 3;
Integer ratd = 7;
// Instantiate the templates
ContractTemplate split = Split.MakeSplit(owner, receiver1, receiver2, ratn, ratd, expiryRound, minPay, maxFee);
// At this point the contract can be funded
// The program bytes can be saved to be used later
// or in a differnt application
System.out.println("Contract address: " + split.address);
byte[] contractProgram = split.program;
}
}
At this point, the split contract account must be funded before any transactions can be issued against it. Use the dispenser to do this now. Also, note that program bytes can be saved to use at a later time or in another application at this point.
Learn More
- Add Funds using Dispenser
- Smart Contracts - Contract Accounts
3. Create and Submit Transaction against Contract
The split contract template offers a helper method to create the proper transactions against the contract called GetSplitTransactions
. This function takes an amount
for the total transaction, creates two transactions (one for each receiver) and then groups these two transactions into an atomic transfer. These transactions are signed with the program logic and the function returns the resulting bytes to submit. These bytes can then be submitted to the blockchain.
package com.algorand.algosdk.teal;
import java.math.BigInteger;
import com.algorand.algosdk.algod.client.AlgodClient;
import com.algorand.algosdk.algod.client.api.AlgodApi;
import com.algorand.algosdk.algod.client.auth.ApiKeyAuth;
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.templates.ContractTemplate;
import com.algorand.algosdk.templates.Split;
/**
* Sign and Submit a transaction example using Split
*
*/
public class SubmitSplitTransaction
{
public static void main(String args[]) throws Exception {
// Setup conection parameters
final String ALGOD_API_ADDR = "http://<your-algod-host>:<your-algod-port>";
final String ALGOD_API_TOKEN = "<your-api-token>";
// Instaniate the algod wrapper
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);
Address owner = new Address("726KBOYUJJNE5J5UHCSGQGWIBZWKCBN4WYD7YVSTEXEVNFPWUIJ7TAEOPM");
Address receiver1 = new Address("THQHGD4HEESOPSJJYYF34MWKOI57HXBX4XR63EPBKCWPOJG5KUPDJ7QJCM");
Address receiver2 = new Address("AJNNFQN7DSR7QEY766V7JDG35OPM53ZSNF7CU264AWOOUGSZBMLMSKCRIU");
// Addition Inputs to the Template
int expiryRound = 5000000;
int maxFee = 2000;
int minPay = 3000;
Integer ratn = 3;
Integer ratd = 7;
// Instantiate the templates
ContractTemplate split = Split.MakeSplit(owner, receiver1, receiver2, ratn, ratd, expiryRound, minPay, maxFee);
// At this point the contract can be funded
// The program bytes can be saved to be used later
// or in a differnt application
System.out.println("Contract address: " + split.address);
byte[] contractProgram = split.program;
TransactionParams params = algodApiInstance.transactionParams();
// Load the contract template and create the grouped
// transactions for the split contract
ContractTemplate loadedContract = new ContractTemplate(contractProgram);
byte[] transactions = Split.GetSplitTransactions(
loadedContract,
50000,
params.getLastRound().intValue(),
params.getLastRound().add(BigInteger.valueOf(500)).intValue(),
1,
new Digest(params.getGenesishashb64()));
// Submit the transactions
try {
TransactionID id = algodApiInstance.rawTransaction(transactions);
System.out.println("Successfully sent tx with id: " + id);
} catch (Exception e) {
// This is generally expected, but should give us an informative error message.
System.err.println("Exception when calling algod#rawTransaction: " + e);
}
}
}
Learn More
- Atomic Transfers