The Celo blockchain is completely independent from the Ethereum blockchain, but both compile their smart contracts to EVM bytecode. This means that any smart contract written for the Ethereum blockchain is already compatible with the Celo network.
To better understand this relationship, we’re going to take the faucet code from Chapter 7 of the Ethereum book, and customize it for the Celo network.
Please note that while this contract is written in Solidity, contracts written in any EVM-compatible high-level programming language (LLL, Serpent, Mutan) can be used for Celo. As long as the code is compiled to run on an [Ethereum Virtual Machine](- The Ethereum Virtual Machine) it is compatible with both chains.
Celo's native currency, CELO, is also an ERC-20 compliant token. The same is true for Celo's stable coin, cUSD. We'll see in the next section that our basic Ethereum faucet does not account for ERC-20 tokens. When it is run on the Celo blockchain, it will assume we want to withdraw the native currency, CELO, as default.
This tutorial aims to show how we can rewrite the faucet and capitalize on the fact that CELO is an ERC-20 compliant token just like its stable coin, cUSD.
Below is the code from the Ethereum book.
If we were to deploy this contract on the Celo network as is, it would work for withdrawing the native currency, Celo. This is because Celo is equivalent to Ether in Solidity contracts like this.
But what about Celo's stable currency, cUSD? How would we withdraw that token from this faucet?
To withdraw a specific token, we will have to rewrite our faucet to consider ERC-20 tokens.
While we're at it, we should utilize the Open Zeppelin library to avoid rewriting code that has already have been security audited and used in many Ethereum contracts without issue. There is no guarantee these contracts will provide absolute safety, but they've been tested and can be trusted more than any custom ones we whip up.
First, we'll remove our Owned and Mortal contracts and import Open Zeppelin contracts instead.
Add the Open Zeppelin library to your project.
Now we can import the relevant contracts in our Faucet.sol file.
The Owned and Mortal contracts from the code above are replaced below with the Open Zeppelin access contract called Ownable. Instead of us having to write basic behavior like this for every Solidity project, we can just utilize this library instead.
The Ownable contract provides administrative access control over the faucet. Only the creator of the contract can destroy the contract. What's nice about the Open Zeppelin contract is it adds a bit more functionality than ours. Specifically the ability to transfer or renounce ownership over a contract.
Our updated Faucet.sol file now has these imports at the top:
We also added the Reentrancy Guard contract to protect against nested calls to our faucet. We didn't need to add this for our faucet to be functional, but we should follow best practices to secure our code and protect our users and ourselves.
To complete our contract and have it work for both Celo and cUSD, we have to update our withdraw function.
Everything else stays the same. Our logging events for withdrawals and deposits don't change. Our receive function stays the same.
We need to add an additional parameter to the withdraw() function signature that allows us to specify a token name to the faucet. Then we will be able to send that token out of our faucet and to the user who requests a payout based on the name of the requested token.
By using the ERC20 interface from Open Zeppelin, we can pass any address that points to a Celo token contract and withdraw that token from this faucet, provided there is a sufficient balance beforehand.
Here is the full Faucet.sol contract after our changes:
This is our Ethereum faucet contract completely altered for Celo.
We even added some additional checks to make sure the token passed to the withdraw function matches the CELO or cUSD contract address. This ensures only Celo currencies can be withdrawn from this faucet.
To call this contract from a dApp kit frontend, we can run the following code. There is a truffle box kit that works out-of-the-box for Celo applications that we should use as our foundation for the project because it simplifies the setup process for our Celo full-stack projects. If we're not interested in seeing our blockchain results in GUI format, we could simply quickstart a Truffle project with no frontend consideration.
Run the following in the root of a project directory to set up the kit:
To deal with the big numbers we'll install the bignumber.js library.
A withdrawal from the faucet function would look like this. This code could be triggered from a button press in a react application as seen in the example here.
In this tutorial, we learned how to alter a smart contract originally written for Ethereum and customize it for use on the Celo network. We learned about the difference between a native token such as Ether or Celo, and an ERC-20 token such as cUSD. We altered some example Solidity code to illustrate the process generally used to prepare an Ethereum contract for use on another EVM compatible network, including specific mention of alternative tokens where necessary - such as inside the withdraw function, as part of a require statement.
As long as we understand that both blockchains are EVM compatible and run the same bytecode, we can customize any existing Ethereum contract for the Celo network.
I hope this tutorial helps to see the potential in looking at existing open source code and understanding how simple modifications can lead to efficient coding solutions. Please consider the following reading list to continue on your journey.
- The Ethereum Virtual Machine
- Celo Background and Key Concepts
- Open Zeppelin Source
- Celo Savings Circle
- Another dApp that utilizes the ERC20 interface in Celo contracts
- Ethereum Book
- Celo for Ethereum Developers
- Truffle Quickstart
This tutorial was created by Brittany Deventer. Brittany is a full-stack developer.