Non-Fungible Tokens, or short NFTs, are all the rage right now. Everyone and their pets are starting an NFT project. Some people got rich from using NFTs; others did not. Some say it's the savior that will rip the power away from big corporations and give it back to the creators; others say it's just a giant pyramid scheme.
I don't know how things will play out, if blockchains will be the next big thing, fueled by the NFT hype, or if it will fizzle out like so many other technologies before it. But a question I got asked lately was, how does this precisely fit in with AWS and serverless technology?
That's why I thought, let's ride the hype train and write an article about NFTs and serverless technology!
My first thought when thinking about AWS and blockchain was, of course, the AWS service with blockchain in its name: Amazon Managed Blockchain (AMB).
With AMB, you get Ethereum nodes hosted on an EC2 machine of your choice, managed by AWS. You can think of it as RDS, but with blockchains instead of SQL databases.
You will need such a service if you don't want to rely on nodes of third parties, like Infura or Alchemy. These nodes can be seen as the bridge between on-chain services and off-chain services.
ABM is neither serverless nor cheap; you pay per hour and can end up with a bill that's over $300, no problem. So you should use it to make it safe that your nodes don't go down when the third party gets bankrupt or if you managed to get exorbitant bills from a third party service.
Another related service is Amazon Quantum Ledger Database (QLDB), a managed immutable blockchain/ledger. It's used if you need the immutability of a blockchain, but without everything decentralized, that comes with it. It has on-demand payment and is quite a bit cheaper than AMB, so if you just need an immutable database, go for QLDB.
The nodes that make up the decentralized system Ethereum can be seen as servers in a classical setup or the databases and functions in a serverless design. They mine new blocks or validate transactions; they also execute the software that is known as a smart contract. This means you need to connect to a node to access the Ethereum blockchain.
In this article we aren't focusing on nodes, so AMB doesn't interest us here. We want to implement a client that asks a node for data that's stored on the blockchain. So, our example will be located off-chain.
We will build a serverless system with the AWS CDK. It will consist of a Lambda function called every hour and an S3 bucket to store data off-chain. We will use JavaScript and the Ethers.js library to connect from AWS Lambda to Ethereum.
The data we will check is the supply of NFTs a smart contract has issued. Since many of the smart contracts on the Ethererum blockchain have well-defined interfaces, we can write a function that just needs a contract address to do its work.
Usually, AWS is seen as a competition to Ethereum because "Ethereum is just another way to build backends," but I think they can work together. Transactions on the blockchain are expensive, so some data and calculations could be outsourced to an off-chain system like AWS Lambda.
To connect a non-Ethereum (off-chain) system with an Etherum smart contract (on-chain), it must connect to a node. We already learned that AWS offers rather expensive nodes we could use, but there are many services out there that can be used for free. These services have harsh limits, but for this example, they should suffice.
Usually, transactions on the blockchain cost gas, but we will only call a function marked as view. This way, the node we connect to can simply read the data from its local blockchain copy, no transactions, and in turn, neither a wallet nor gas is needed.
Let's look at the example CDK stack that defines our infrastructure.
A bucket and a Lambda function. The bucket name and the address of the smart contract are passed to the Lambda function via environment variables.
In this example, I used the NFT smart contract of the Developer DAO because its code is open source, so I know what interfaces they implemented.
At the end of the stack, the schedule is set up with CloudWatch events.
Now, let us look at the code that will interact with Ethereum.
The first line defines the Application Binary Interface of the smart contract we want to call. It's just an array of strings that make up the method signatures of that contract. I didn't define all the contract methods because I will just call one of them anyway.
Next, I get the environment variables to know where to get the data from and where to save it.
In the function body, I get the default provider from Ethers.js. In this example, the function is only executed once an hour, so it won't hit the limits. Still, in a production system that might access a node provider more often, you should sign up for an Ethereum gateway service like Infura or Alchemy.
Then I set up the contract with the correct ABI, address, and provider. The provider makes sure we are connected to the proper chain (there are test chains for Ethereum, we don't want to communicate to those). The ABI tells Ethers.js which methods the contract provides. And finally, the contract's address so Ethers.js knows where to find the contract on the chain.
Finally, we call the method that gives us the number of NFTs already minted and save it to our S3 bucket as a JSON file. The file name is the current date and time as ISO-string; we will know how many NFTs were minted every hour.
If you moved some of your calculations off-chain and into AWS Lambda, you can monitor them with Dashbird like every other serverless system you build on AWS. No additional setup is needed. We can set up alarms for our NFT checker and get notified if things fail.
In Figure 1, we see the general information Dashbird gives us about a Lambda function. This just resembles a few test invocations, but we can already see some interesting things.
The executions were all free of charge and they fit snuggly into the smallest memory configuration. That's good to know. But if we look at the Duration tab in Figure 2, we see that our function has varying runtimes.
The function took anything from under 1 second to up to 3 seconds to do its work. With the default Lambda invocation timeout of 3,000 milliseconds, our function is at high risk to be shut down prematurely. Especially, since it's only called once every hour, it will have a cold-start every time.
We can use the insight gathered by Dashbird to update our Lambda function definition with a more generous timeout, so it won't accidentally crash in the future.
This small example application outlined how serverless technology can be used in tandem with blockchains. Lambda functions are cheaper and faster than blockchain transactions, so they lend themselves for non-critical calculations that don't have to be tracked by a blockchain.
This example was straightforward and somewhat contrived, but doing automated work that depends on the state of a blockchain is an actual use case. The Lambda function could render statistics on an HTML site or notify the NFT creator of changes to their supply.
For the copyable code snippets, see the original article