This article takes a deep dive into how we use aws lambda functions and sqs at sila to broadcast silatoken smart contract transactions on ethereum. This article is divided into four sections.
First section explains the architecture that we have in place to handle ethereum transactions. Sections two dives into the overall transaction life-cycle. Section three goes into detail on how to construct the transactions with the required parameters and broadcast them on ethereum. The final section is the most important one and elaborates on how to handle pending transactions on ethereum while keeping the gas costs low.
- DynamoDB to store the nonce associated with ethereum addresses authorized to send ethereum smart contract transactions
- Lambda functions triggered by sqs events for Sila token issuance, redemption and transfer messages
- Ethereum RPC Ec2 servers running parity ethereum client
- AWS secrets manager to store private keys being used to sign the transactions
- Orchestrator as a bridge between REST API, ACH and Ethereum transactions. Orchestrator is a piece of code that handles the transaction state and reroutes them to right queue.
- SQS queues as an interface between different services like REST API, ACH, Ethereum issuance,redemption and transfers.
Message for Silatoken issuance, redemption and transfer event comes in through Sila Api’s, based on the action (issue, redeem and transfer), the message is sent to relevant queue by the orchestrator which in turn triggers the send transaction lambda function that sends the transaction signed by sila authorized address to ethereum node and increases the nonce by one in the database for subsequent transactions. The message is deleted from the ethereum transaction queue and sent to the ethereum pending queue with the transaction hash, nonce and sent at block number appended in the message history. Replace and check for transaction send failure due to bad RPC connection .
Constructing a transaction
Sending a transaction
After experimenting with several ways to manage the authorized address nonce we settled on storing the nonce in a database as it is faster to retrieve and update than making a web3 call to the rpc server and waiting for the transaction to be mined. It has its pitfalls for example subsequent transactions getting stuck until previous transaction have been mined etc but that’s why we have three lambda functions watching just the pending transactions. In the next section we discuss on how to handle pending ethereum smart contract transactions. Nonce management is not as straightforward as we have 3 lambda functions that can send transaction. In our case we have conditions in place depending on message history.
Deciding gas price and gas limit
Gas limit is set based on the amount of computation involved in the smart contract function call. How ever we can play around with the gas price to make sure that transactions are being mined in the desired time. We use modified version of eth_gas_station engine to decide the gas price based on the network mining requirements .
Handling the transactions in pending queue
There are three lambda functions watching the pending queue.Below we dive deeper into details.
Check pending transactions for success or a fail
If you remember we appended the tx_hash,nonce and sent_at_blockNumber in the message history, we use tx_hash and web3 module to get the transaction status. The transaction status can be 0,1 or null depending on if the transaction has been mined successfully. Status 0 and 1 both result in nonce increment for the authorized address as transaction was mined in some block. If the status is 0 which means the transaction failed, in this case we retry the transaction by sending it back to the transaction queue. Each transaction is restricted up to a maximum of 3 retries after which it is dumped into orchestra-tor. If the status is 1 which means that the transaction was successful, a message with success update is sent to orchestra-tor.
Replacing stuck transactions:
If the transaction hash gives a null and transaction has been pending in the node memory pool for a long time. You remember we appended the sent_at_block Number and nonce in the message history. We get the current block number using web3 and compare the difference and if the difference is more than 80 blocks (can be set to higher or lower) which means the transaction has been pending for 80 blocks, so we replace the transaction with a higher gas price, keeping the nonce value same as in the message.
Handling transaction send failures:
If you play with ethereum long enough you will have certain cases when you are unable to find the sent transaction in the node memory pool which means transaction never hit the ethereum rpc server. In this case we have another lambda function redirect the transaction message to the queue.