Using AWS Lambda & SQS With Web3
The first section explains the architecture we have in place to handle Ethereum transactions. Section two dives into the overall transaction life-cycle, and section three goes into detail on how to construct transactions with the required parameters and broadcast them on Ethereum. The final section is the most important of them all, and elaborates on how to handle pending transactions on Ethereum while keeping the cost of Ethereum gas low.
Architecture Behind Sila Ethereum Transactions
- DynamoDB to store the nonce associated with ethereum addresses authorized to send ethereum smart contract transactions
- Lambda functions triggered by SQS events for SilaToken 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 as an interface between different services like REST API, ACH, Ethereum issuance, redemption, and transfers.
Messages for SilaToken issuance, redemption, and transfer comes in through Sila APIs. Dependent on the action (issue, redeem, and transfer), the message is sent to the relevant queue by Orchestrator, which in turn triggers the send transaction Lambda function.
That sends the transaction, signed by Sila’s authorized address, to Ethereum node and increases the nonce in the database by one, for subsequent transactions. The message is deleted from the ethereum transaction queue and sent to the ethereum pending queue with the transaction hash and nonce, then sent with the block number appended in the message history. Replace and check for transaction send failure if there is a bad RPC connection.
Constructing a Transaction:
Sending a transaction:
After experimenting with several ways to manage the authorized address nonce we settled on storing it in a database, as it is faster to retrieve and update there than making a Web3 call to the RPC server and waiting for the transaction to be mined. It has its pitfalls, however. For example, subsequent transactions can get stuck until previous transactions have been mined, but that’s why we have three Lambda functions that are watching just the pending transactions — and we’ll discuss how to handle pending ethereum smart contract transactions in the next section.
Nonce management is not as straightforward, as we have three Lambda functions that can send transactions. In our case we have conditions in place that are dependent on message history.
Deciding gas price & gas limit
Gas limit is set based on the amount of computation involved in the smart contract function call. However we can play around with the gas price to make sure transactions are being mined in the desired time. We use a modified version of eth_gas_station engine to decide the gas price, based on the network mining requirements.
Handling Transactions in Pending Queue
Let’s dive deeper into the three Lambda functions that are watching the pending queue . . .
1. Check pending transactions for success or a fail
Previously we discussed how 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 a nonce increment for the authorized address, as the transaction was mined in some block. If the status is 0, which means the transaction failed, we retry the transaction by sending it back to the transaction queue. Each transaction is restricted to a maximum of 3 retries, after which it is dumped into Orchestrator. If the status is 1 which means that the transaction was successful, a message with success update is sent to Orchestrator.
2. Replacing stuck transactions
If the transaction hash gets a null and transaction has been pending in the node memory pool for a long time, consider how 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; if the difference is more than 80 blocks (and the difference can be set to higher or lower), which means the transaction has been pending for 80 blocks, we replace the transaction with a higher gas price, keeping the nonce value the same as in the message.
3. Handling transaction send failures
If you play with Ethereum long enough you will have certain cases where you are unable to find the sent transaction in the node memory pool. This means the transaction never hit the Ethereum RPC server, but we have another Lambda function that will redirect the transaction message to the queue.
Like this article? Share it with your network!
And check out the newly launched beta for Sila’s API!
Originally published at https://medium.com on May 28, 2019.