today i go over aurora on hardhat shenanigans and compound’s ctoken smart contract frolicsomeness.
The Compound Protocol is an Ethereum smart contract for supplying or borrowing assets. Accounts supply capital (ether or ERC-20 tokens) to receive cTokens or borrow assets from the protocol with other collateral assets.
Each asset supported by the Compound Protocol is integrated through a cToken contract, an EIP-20 compliant representation of balances supplied to the protocol, and is assigned a rate and a risk model. CTokens contains CErc20 and CEther as public interfaces for ERC20 tokens and ether.
These are beautiful smart contracts, but like any code, they can carry some intriguing edge cases. Looking at the Compound’s cTokens contract, an interesting boundary condition happens when cToken.totalSupply == 0.
For context, the total supply of a coin includes only the coins that were already produced minus the units that were destroyed (for instance, in coin burn events).
When we look at the contract, we realize that if, for some reason, the total supply of the token goes to zero, then the exchange rate would depend on an input parameter called initialExchangeRateMantissa
:
We all know how popular the Compound protocol is. We all know that many sidechains implement the same concepts and primitives as the ones found in Ethereum - sometimes reproducing many parts of the original smart contracts.
Aurora is an Ethereum Virtual Machine (EVM) built on the NEAR Protocol, with Ethereum as its native token. With Aurora’s EVM, Ethereum native applications can be ported to NEAR’s L2 network, which is built as a smart contract on NEAR. Some non-custodial liquidity protocols in Aurora are Aurigami and WePiggy.
💡 How would I test submissions (PoCs and smart contracts) for these projects without testing code on public testnets or mainnets?
There are several development environment tools and tricks that I will be talking about in future posts (I am a nerd; I like to try and test all the things). But for this first post, we will be working with the good ol’ Hardhat, the popular Ethereum development environment.
In the last part of this post, let’s go over some tricks when deploying and interacting with the Solidity smart contracts on Aurora using Hardhat.
Hardhat will, by default, fork from the latest Ethereum mainnet block. However, often, a test suite might depend on forking a particular block number. Additionally, pinning a specific block enables caching and speed improvements, since if you don't pin the block, new data will be fetched with each new block.
For this setup, you will need access to a node with archival data, such as Alchemy or Infura or any in this list. Create an account and grab the RPC URL and API key.
To pin a block number, you can either simply add the info into the hardhat.config.js file:
Or, if you are using a node task, you can specify a block number with the --fork-block-number flag:
npx hardhat node --fork <node provider url> --fork-block-number <block number>
To confirm that you have forked the latest block from Ethereum mainnet, you can send the following curl request:
$ curl --location --request POST 'localhost:8545/' --header 'Content-Type: application/json' --data-raw '{ "jsonrpc":"2.0", "method":"eth_blockNumber", "params":[], "id":83 }'
You should receive a response like:
{"jsonrpc":"2.0","id":83,"result":"0xf1ed3"}
Use a Hex converter to retrieve the block number.
This session is partially based on Aurora’s official guide, found here. To set up Aurora in Hardhat, clone their ERC-20 examples:
git clone https://github.com/aurora-is-near/aurora-examples.git
cd aurora-examples/hardhat/erc20/
Now, let’s set up Hardhat from this folder (you can start a new Hardhat project there).
Place the contract with the proof of concept inside contracts/ and the deployment script inside scripts/.
By the way, this is a simple deployment script that I use often:
Finally, add the Aurora network configuration to hardhat.config.js. For private keys in localhost, you can copy the values provided by Hardhat accounts. Note that I am adding Aurora’s mainnet and testnet as well. However, we will only use aurora_local
, the defaultNetwork
:
We are ready to run things. Let’s compile the contract:
$ npx hardhat compile
And deploy it with:
$ make deploy NETWORK=aurora_local
which is the same as:
$ npx hardhat run scripts/deploy.js --network aurora_local
Now that the contract is deployed, you may interact with it the way you like. I use the Hardhat console often:
$ npx hardhat console
> const Contract = await ethers.getContractFactory('<contract name>');
> const c = await Contract.attach('<contract address>')
> await c.someFunction()