Scheduling Transactions for Sending in the Future
If you plan to make a future payment in about 2 hours, 2days or 4 days time, you can achieve that by submitting the transaction in the future round. This allows an account to pay after an expected job is done or where an account balance is low and will be funded in a future date or time.
Requirements
- Javascript Algorand SDK
- The AlgoExplorer API
Background
Most transactions submitted immediately are process in the current round range of 1000 blocks. An account or dapp can decide to schedule a transaction outside the current block and will require this transaction to be saved and submitted. The signed transaction can be saved and submitted later via a cronjob or set a timeout for short term transaction in a single secure session.
Here is a quick video explaining the tutorial
Steps
1. Set up Algo SDK, mnemonic phrase and utility function
<script src="scripts/algosdk.min.js"></script>
//PUT YOUR MNEMONIC KEY HERE.
var mnemonic = "Mnemonic phrase";
// get account from the mnemonic phrase
let account = algosdk.mnemonicToSecretKey(mnemonic);
//get the address from the mnemonic phrase
address = account.addr;
//set the urls for retrieving params and posting to the algorand blockchain
const getParamsURL = "https://api.testnet.algoexplorer.io/v2/transactions/params";
const posturl = "https://api.testnet.algoexplorer.io/v2/transactions";
//Function to GET and POST transactions
function getvals(fetchOption, url){
return fetch(url,
{
method: fetchOption,
headers: {
'Content-Type': 'application/x-binary',
},
})
.then((response) => response.json())
.then((responseData) => {
return responseData;
})
.catch(error => console.warn(error));
}
1. Calculate future time to submit the transaction
You will need to calculate the difference in time it takes between the future transaction dateTime and the current time when the script is executed. Block production time on the chain is in seconds and it is better to calculate the difference in seconds.
// get the different in time between the future dateTime and now.
// Change the date to a future date you prefer. It takes about 1:15 mins to complete the 1000 block rounds
var futureTrxDate = "19 January 2021 17:20 UTC";
var a = new Date(futureTrxDate);
b = new Date();
var diff_seconds = Math.abs(Math.round(((+b - +a) / 1000)));
2. Calculate Future Block Round
The average Algorand block production time is about 4.5. To get the total blocks or approximate last round reached by the future time, divide the time difference between future transaction date and now by the average block time of 4.5 seconds.
var blockRound = Math.abs(Math.round(diff_seconds/4.5));
3. Calculate First AND Last Valid Rounds
Get the first and last valid rounds for the future transaction submission time
// get the parameters of the current block round
fetchOption = "GET";
url = "https://api.testnet.algoexplorer.io/v2/transactions/params";
getvals(fetchOption,url).then(response=> {
// get the last round of the current block, genesis id and genesis hash
lastRound = response["last-round"];
genesisID = response["genesis-id"];
genesisHash = response["genesis-hash"]
// get the first round of the future transaction date by adding the current last round to the future block round
firstRoundFuture = lastRound + blockRound;
// get the last round of the future transaction.
// This is to make sure our transaction is
// submitted even if there is a delay.
// Add 1000 round to the first round
lastRoundFuture = firstRoundFuture + 1000;
4. Construct the Transaction Params
// I realised the the Algorand genesis hash and
// genesid ID don't change. So you can statically
//declare those
let suggestedParams = {
"flatFee": true,
"fee": response.fee,
"firstRound": firstRoundFuture,
"lastRound": lastRoundFuture,
"genesisID": genesisID,
"genesisHash": genesisHash,
};
// get the transaction details from the constants declared //for the future transaction
var futureRecipient = "6NGU52ZU3XPRH5QJFBFG62H3FNGGGOHOSP462RICAFZCKII56ZMYEFV5UU";
var amount = 1000000;
var note = algosdk.encodeObj("Invoice payment");
5. TimeOut or CronJob to Execute transaction
This step is important. Since this is a future transaction, you need to execute this script during the future round period. For the purpose of this tutorial and for convinience, I use the Timeout function to delay the script from submitting the transaction until the round passes. The script executes in millisecons and so I multiply the seconds by 1000 to give the milliseconds to timeout.
timeOutTime = diff_seconds*1000;
6. Process the Transaction
let txn = algosdk.makePaymentTxnWithSuggestedParams(address, futureRecipient, amount, undefined, note, suggestedParams);
let signedTxn = txn.signTxn(account.sk);
console.log(signedTxn);
**setTimeout**(()=> { fetch(posturl, {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/x-binary',
},
body: signedTxn,
})
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch((error) => {
console.error('Error:', error);
});
}, **timeOutTime**);
});
</script>