Smart Contracts on Ethereum
To understand the technology around the Ethereum eco-system a little better, I decided to create a small dummy application that would interact with a smart contract on the Ethereum blockchain.
To test things out I decided to create a small application that will provide a Hello World! API to users who are on a whitelist. This whitelist will be stored on the blockchain.
To get on the whitelist, either the owner of the whitelist can add you, or you can get on the whitelist for a price of USD 100.
With these requirements set, let’s see how we can create a Hello World app and a whitelist smart contract.
tl;dr & Show Me The Code!
I’m quite impressed with the whole Ethereum development eco-system. Things like truffle make working with Solidity contracts quite easy. The Solidity language itself can seem clunky to work with at times, but it is is rapidly improving. I especially like how seamless it is to interact with other contracts like Chainlink. I guess you can see the language as being in the same stage as where Java or Javascript was in the early stages of the Internet.
All in all, I can really see Ethereum and Solidity growing in popularity as more and more applications get built that use it. The momentum is there with Layer 2 roll-ups like Arbitrum coming online (see my Adopt Pets on Arbitrum post to see how easy it is to deploy apps there).
The smart contract can be found here: eth-demo-vault. The Hello World API application that interfaces with it is here: eth-demo-api.
If you are not interested in how these projects were created from scratch, then you can stop here. If you are, do read on :)
Hello World API App
Since I like rust a lot, we will create this application in rust. If you don’t have rust installed yet, you can go to rustup.rs to install it.
Create a new rust application using cargo:
$ cargo new eth-demo-api
Open up the Cargo.toml
file in the new project and add the following dependencies:
[dependencies]
tokio = { version = "1", features = ["full"] }
warp = "0.3"
web3 = "0.16.0"
hex = "0.4"
log = "0.4"
env_logger = "0.8"
dotenv = "0.15"
Create a new file src/validate.rs
with the following content:
This is a stub which will interact with our whitelist to check if an entry exists or not.
Now edit the src/main.rs
file and add the necessary imports and onvert the main
function to async with tokio:
In our main
function, we start by loading environment variables from .env
and initializing the logging engine and instantiate a Validator
struct:
Next we create a function that will use the validator to check whether the user’s IP matches what is on our whitelist, and if it does, print’s out a Hello World message with the user’s supplied name:
Now that we have our response, add the warp code to launch our Hello World API. Go back to the main function and add the following at the bottom of the function:
Whitelist Smart Contract
Now that we have stubbed out the application that will interact with the whitelist, let’s create the whitelist smart contract. We will call this contract DemoVault
. We will create it using Truffle.
Let’s create a new project called eth-demo-vault
:
$ mkdir $HOME/eth-demo-vault
$ cd $HOME/eth-demo-vault
$ truffle init
We create our new project under $HOME
but you can create it anywhere you like (I like to keep it separate from the etc-demo-api
project).
Truffle will have created all the artifacts needed to get started with a local testnet like Genache. However, since we will be using Chainlink to get the latest ETH/USD exchange rate, we will have to deploy to Kovan testnet instead.
Before we can deploy to the Kovan testnet, we will need to prepare some prerequisites first.
MetaMask and Infura Project
First make sure you have MetaMask installed, and that you have two accounts (see here how create a second account). Next create an Infura account at infura.io, and create a new project (if you already have a project you can also just use that).
Next, load up your MetaMask account with some kETH. You can do that by using the Kovan faucet and providing the wallet address of your first MetaMask account.
If all went well, your first MetaMask account should now have some kETH. Send some kETH (e.g. 0.5) to your second MetaMask account. Now both your first account and second account should have some kETH.
Configure Truffle
Now that you have two funded accounts in MetaMask and an Infura project, we can configure truffle to use those.
Create a .env
file in your newly created project with the following contents:
MNEMONIC="<your metamask recovery phrase>"
INFURA_PROJECT="<your infura project key for kovan network>"
Next, open up truffle-config.js
and edit it to look like the following:
Here we have added the configuration for the Kovan network, and indicated which solc
compiler version to use. Notice that we use a HDWalletProvider-provider
to connect to MetaMask, and dotenv
to load the .env
file. We will need to install these two libraries for truffle. Do so by running the following:
$ npm install -D @truffle/hdwallet-provider
$ npm install -D dotenv
Since we will be using Chainlink to get the latest ETHUSD exchange rate, we will need to add the AggregatorV3Interface.sol
to the project’s contracts
folder. You can download it from github.
Now create a new file contracts/DemoVault.sol
. This will be our whitelist smart contract. Add the following contents to the file:
For the AggregatorV3Interface
replace comment /* address on Kovan */
with the following contract address:
0x9326BFA02ADD2366b30bacB125260Af641031331
i.e.:
AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331)
This is the address of the Chainlink AggregatorV3Interface
contract on the Kovan testnet.
We now have the beginnings of our contract containing a reference to the Chainlink priceFeed, a contract owner (the one who will create the contract on the blockchain), a base price of USD 100, and a mapping containing keys (our whitelist).
Let’s now add a function to get the base price of USD100 converted to Wei:
The above function will retrieve the latest ETHUSD exchange rate from Chainlink, and apply it to the USD base price. Note that the exchange rate is denominated in ETH, so we need to convert it to Wei first.
Now create a function that will add a key to our whitelist:
This function adds a Key
to the whitelist mapping called keys
and emits a new NewKey
event. Note that this method is private so only other functions in the same contract can access it. Let’s now create a function that will allow the contract owner to add a new key:
This function is public and checks if the owner is the one calling it. If it is, it continues on to the private create
function.
Next we create a purchase
function that will allow anybody to add a key to the whitelist for a base price of USD 100 in Wei:
First we check if the value received by the user (i.e. msg.value
) is close to the current price of USD 100 in Wei. As the exchange rate fluctuates heavily over time, we add a 10% allowance.
If the received funds are ok, we forward the funds to the contract owner, and subsequently call the private create
function.
Lastly we create a function that will allow us to check if a key is in the whitelist:
With our DemoVault.sol
contract now ready, we can now add the necessary migration script so that truffle can upload it. Create a new file migrations/2_deploy_contracts.js
with the following content:
Now try to compile the contact as follows:
$ truffle compile
If all goes well, we can now deploy to the Kovan testnet:
$ truffle migrate --network kovan
Truffle will now deploy the contract using your first account in MetaMask, and this will cost you some ETH (this cost is called “gas”). If all went well you should see something like the following:
...2_deploy_contracts.js
=====================Deploying 'DemoVault'
---------------------
> transaction hash: 0x262641...6ea4f12c
> Blocks: 1 Seconds: 16
> contract address: 0xfDf181...65fA1D2F
> block number: 25230313
> block timestamp: 1622686868
> account: 0xFCa4C8...9d3D565f
> balance: 2.0539430121993214
> gas used: 1064540 (0x103e5c)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0212908 ETH> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.0212908 ETHSummary
=======
> Total deployments: 2
> Final cost: 0.02625488 ETH
(the transaction hash, contract address, and account have been shortened to keep things legible)
Take note of the contract_address
as we will need it later.
Finish the Validator Struct
Our contract has been successfully uploaded to the Kovan testnet, so now we can finish up our whitelist Validator
to interface with it. Go back to the etc-demo-api
project and add the an .env
file with the following content at the project root:
RUST_LOG=debug
MNEMONIC="<your metamask recovery phrase>"
INFURA_PROJECT="<your infura project key for kovan network>"
VAULT_ADDRESS="<contract address of your DemoVault contract>"
For VAULT_ADDRESS
use the contract_address
you got from the truffle migrate
output.
Now open up src/validate.rs
and add the necessary imports and update the Validator
struct to hold a Contract<Http>
instance:
Next, edit the new
function to look as follows:
First we retrieve the infura_project
and vault_address
variables. For the vault_address
we make sure to strip the 0x
prefix from the supplied contract_address
if it was included (if you copy/paste from truffle it is included).
Next we start up a Web3
instance from the web3 crate. With the Web3
instance we can now load up the contract ABI. Generate this at the root of the eth-demo-api
project as follows:
$ solc -o target --abi ../eth-demo-vault/contracts/*.sol
Now, when calling Validator::new()
you should now get an instance of the Validator
struct with our contract.
Now we can fill out the is_valid
function in the Validator
struct:
This function calls the isValid
method in our smart contract.
With everything filled up, start up the eth-demo-api
application!
$ cargo run
If you navigate to http://localhost:3030/hello/world you should see a message saying you have no key:
No key for 127.0.0.1!
To fix this, go back to the eth-demo-vault
project and let’s add 127.0.0.1
to the whitelist.
Interact with the Contract
You can interact with the contract using the truffle console. In the eth-demo-vault
project run:
$ truffle console --network kovan
Once the console launches, create a contract instance connecting to our contract:
let vault = await DemoVault.deployed();
With our contract instance, we can get the latest price to add yourself to the whitelist:
let price = await vault.getPrice();
Make the purchase using the second account on MetaMask to add 127.0.0.1
to the whitelist:
await vault.purchase(
"127.0.0.1",
{ from: accounts[1], value: price }
);
If the transaction succeeded 127.0.0.1
should now be added to the whitelist. The ETH equivalent of USD 100 should have been deducted from the second MetaMask account, and added to the first MetaMask account.
Check to see if the newly added entry is indeed valid:
await vault.isValid("127.0.0.1");
The return of this should be true
.
Now we can go back to http://localhost:3030/hello/world and you should now see:
Hello, world from 127.0.0.1!
Join Coinmonks Telegram Channel and learn about crypto trading and investing