100 Days of Web3 (Days 71-90)

I spent days 71-90 of the 100 Days of Web3 challenge immersed in layer two solutions. I got to learn more about Polygon, Arbitrum and Optimism. Layer two solutions are a critical effort in scaling the Ethereum ecosystem and they are one of the most exciting subjects in the Web3 space.

Day 71

I kept following along with the Polygon course from EatTheBlocks. The next module covered usage of the matic.js library. First, I used the official Polygon faucet to load my MetaMask wallet with some test tokens and Matic. I then used matic.js and a simple Node.js script to check my asset balances on the Polygon chain as well as transfer tokens between accounts.

Day 72

The next step was moving assets between the Ethereum and Polygon chains. Once again, I used the matic.js library and Node.js to move ether from the Ethereum Goerli network to the Polygon Mumbai chain using the proof-of-stake bridge. The ether appears as wrapped ether (WETH) tokens in the Polygon Mumbai network. I then moved the wrapped ether in the reverse direction: back to Goerli. Moving assets from Polygon back to Ethereum is a two-step process. First, we burn the assets on the Polygon chain. Once the transaction has been checkpointed on Ethereum, we then submit the proof-of-burn to Ethereum.

Day 73

The next module in the course was on the FxPortal. The FxPortal is a powerful state sync mechanism between Ethereum and Polygon that allows for arbitrary state transfers. Polygon’s proof-of-stake bridge is based on the same architecture as the FxPortal. Using the FxPortal, we created a simple application that syncs a message from Ethereum to Polygon and vice versa. To do this, we had to create one contract on Ethereum and one on Polygon. The Ethereum contract inherits from the FxBaseRootTunnel contract and the Polygon one inherits from the FxBaseChildTunnel contract. Both of these base contracts are provided on the FxPortal GitHub repository.

Day 74

The second step when completing a withdrawal from Polygon to Ethereum is to submit a proof-of-burn to the Ethereum contract. To generate this proof, we need both the transaction hash and the event signature. Matic.js has a utility function that can be used to generate this proof. I wrote a small script using matic.js to create the proof, and then I verified it by completing the withdrawal of some test tokens.

Day 75

The next section of the course was on meta transactions. Meta transactions allow users to interact with smart contracts without needing to pay gas fees. At their simplest, meta transactions are two-step processes. The first step involves the user signing a message with their private key. The second step is another party submitting the signed message to a smart contract to execute the transaction. Meta transactions are not unique to Polygon and are common in many different blockchains. I also learned about Biconomy which is a platform for making meta transactions simpler to implement.

Day 76

Having completed the Polygon course from EatTheBlocks, I decided to learn about another layer two solution: Arbitrum. Unlike Polygon which is a sidechain, Arbitrum is an optimistic rollup solution. Arbitrum essentially aggregates transactions together and syncs them back to the Ethereum chain. Another significant feature of Arbitrum is that withdrawals back to Ethereum take a minimum of 7 days. During these 7 days, a third party can contest a transaction by providing a fault proof.

I started to look at code for some simple transactions on Arbitrum such as token deposits and withdrawals. Most ERC-20 token deposits are routed through the standard token gateway of Arbitrum. If a standard ERC-20 token is deployed on Ethereum, on the first deposit to Arbitrum, a token contract is automatically deployed to Arbitrum.

Day 77

I continued looking at the Arbitrum code examples, in particular, focusing on one for arbitrary state transfer from Ethereum to Arbitrum. It is similar to the example for arbitrary state transfer from Ethereum to Polygon. The method on the Arbitrum contract for updating state should be protected so that only a message from the corresponding Ethereum contract can call it.

Day 78

There were a few more examples of interest in the Arbitrum examples repository including an exploration of the outbox and the bridging of custom ERC-20 tokens. The outbox contract handles all messages from Arbitrum to Ethereum. Once the 7 day waiting period has elapsed, the transaction can be completed by interacting with the outbox.

Bridging custom ERC-20 tokens is slightly different than standard ERC-20 tokens because a different token gateway is used. Also, the corresponding contract is not automatically deployed on Arbitrum. Instead, a token contract is deployed on each chain and they are linked together by registering them on the layer 1 and layer 2 gateways.

Day 79

Having learned the basics of Arbitrum, I started learning about another optimistic rollup solution called Optimism. I started by reading the starter docs on the official website. While optimism is similar to Arbitrum there are some differences. Optimism is a more conservative option as it stays as close to the layer 1 Ethereum chain as possible. For example, the optimism client is very similar to the vanilla Go Ethereum client, whereas Arbitrum uses its own solution called ArbOs.

Day 80

Since I had spent quite a long time learning about layer two solutions, I decided to work on a custom ERC-20 token that would be deployed to three different layer two solutions: Polygon, Arbitrum and Optimism.

I started by creating the token contract on Polygon. Although I had already learned how to create a custom token contract on Polygon using the standard bridge, I decided to use the FxPortal for this project. With the standard bridge, one needs to request the mapping of the Ethereum and Polygon tokens from the Polygon team. However, there is no need to do this with the FxPortal bridge. To get a better idea of how to implement the contracts, I went through the example contracts in the FxPortal GitHub repository.

Day 81

There were 4 different contracts needed to implement a custom token in both Ethereum and Polygon:

  1. The Root Token (token contract on Ethereum)
  2. The Root Tunnel (Ethereum contract which interacts with the FxPortal)
  3. The Child Token (token contract on Polygon)
  4. The Child Tunnel (Polygon contract which interacts with the FxPortal)

My Root Token contract is a simple wrapper of OpenZeppelin’s ERC-20 contract. The Child Token is the same, but has a few additional functions for burning and minting tokens when transferring them back and forth between Ethereum and Polygon. The tunnel contracts are responsible for interacting with FxPortal contracts to send messages between the two chains.

Day 82

I proceeded to write deployment scripts that deploy all four contracts and configure them to be used with the FxPortal. I wrote three different scripts which were to be run sequentially to complete the deployment:

  • 1-deploy-eth.js (Ethereum)
    • Deploys Root Token and Root Tunnel contracts to the Goerli Ethereum test network
  • 2-deploy-poly.js (Polygon)
    • Deploys Child Token and Child Tunnel contracts to the Polygon Mumbai test network
    • Registers the Root Tunnel contract address on the Child Tunnel
    • Updates the token mapping on the Child Tunnel contract
  • 3-deploy-eth.js (Ethereum)
    • Registers the Child Tunnel contract address on the Root Tunnel
    • Updates the token mapping on the Root Tunnel contract

Later, I discovered that it’s possible to write a single deployment script instead of writing three separate ones. I did this when implementing the custom token for Arbitrum.

Day 83

Next, I wrote scripts for depositing tokens to Polygon and withdrawing them back to Ethereum. The deposit script was relatively straight-forward and consisted of two steps:

  1. Approve the Root Tunnel contract to transfer the number of tokens to deposit
  2. Call the deposit method on the Root Tunnel to deposit the tokens to Polygon

I broke the withdrawal part into two scripts:

  • 1-withdraw-poly.js (Polygon)
    • Approves the Child Tunnel contract to burn the tokens on the Polygon chain
    • Calls the withdraw method on the Child Tunnel contract to burn the tokens on the Polygon chain
    • Prints the Polygon transaction hash
  • 2-withdraw-eth.js (Ethereum)
    • Generates proof-of-burn from the transaction hash produced by the previous script using matic.js
    • Calls the receiveMessage method on the Root Tunnel contract with the generated proof to complete the withdrawal

Day 84

I was running into some problems when trying to run the second withdrawal script. I was finally able to identify that an incorrect proof was being generated by matic.js. There are two ways to generate a proof using matic.js: by leveraging an API or by having matic.js do it. It was the latter that was giving me an error. I switched over to using the API method and my withdrawal script started working. Later on, I found out that the problem wasn’t with matic.js, but with the RPC node I was using. Switching my connection to another node also solved the problem.

This concluded the Polygon implementation of the custom token. I added a README.md file so anyone interested could run the project themselves.

Day 85

One layer two solution down, two more to go! I decided to do the Arbitrum implementation for the custom token next. I started by writing the contracts for the custom token. Only two contracts were required: the Root Token contract which lives on Ethereum and the Child Token contract which lives on Arbitrum.

The Root Token contract is a standard ERC-20 token with two additional methods (isArbitrumEnabled, registerTokenOnL2) which are used for registering the token with the Arbitrum custom token gateway. The Child Token contract is almost the same as the one for Polygon; it’s just a normal ERC-20 token with additional methods for minting and burning tokens.

Day 86

Having written contracts for both the Ethereum and Arbitrum versions of the token, the next step was to deploy them. I created a deployment script that was based on the example from the Arbitrum tutorial repository. The deployment involves the following steps:

  1. Deploy the Root Token on the Ethereum Rinkeby testnet
  2. Deploy the Child Token on the Arbitrum testnet
  3. Call the registerTokenOnL2 method on the Root Token contract to register the token with the Arbitrum gateway

Days 87

I was receiving some strange errors when running the deployment script for the Arbitrum solution. At times the script would just hang and not even deploy the tokens, and other times it would just hang when calling the registerTokenOnL2 method of the Root Token contract. I tried many things such as using a different RPC provider, logging more information in the script and doing a more detailed comparison between the example version provided by the Arbitrum team and my script. It turned out that even the example provided by the official Arbitrum tutorial wasn’t working and the team was updating it. I decided to put the Arbitrum implementation on hold and come back to it after.

Day 88

I then moved on to the final layer two solution I wanted to implement a custom token for: Optimism. Although I had already read about the basics of Optimism, I hadn’t seen any code examples. I decided to go through the examples from the official tutorial repository. Some of the examples relied on having a locally running version of Optimism on my machine. I did this with docker using the instructions on the website. I cloned the example repository to my machine and ran the token deposit and withdrawal example successfully.

Day 89

To get a better understanding of the token deposit and withdrawal example that I ran on the previous day, I decided to create my own version of it. I started with creating the contracts, of which there were just two. The Root Token contract was just a standard ERC-20 token contract. The Child Token contract was very similar to the ones I wrote for Polygon and Arbitrum; it was just a simple ERC-20 with additional functions for burning and minting tokens.

Day 90

The next step was to write scripts to deploy and configure the contracts. The deployment script was very simple and just deployed the two token contracts to their respective chains. I then worked on the script for depositing tokens from Ethereum to Arbitrum. This script consisted of the following steps:

  • Approved the Optimism standard bridge to transfer the number of tokens to be deposited
  • Call the depositERC20 method on the Optimism standard bridge contract

The deposit script made use of some utility functions provided by the @eth-optimism/contracts and @eth-optimism/core-utils npm packages provided by the Optimism team. In particular, there was a Watcher utility that waits for the deposit message to be relayed to Optimism.

Summary

During days 71-90 of the 100 Days of Web3 challenge, I learned a lot about layer two solutions. Since layer two solutions are relatively new, it has been difficult to find conventional resources to learn how to use them. However, the tutorial GitHub repositories provided by the Optimism and Arbitrum teams were really helpful. In the next 10 days of the challenge, I hope to finish up with my custom token project for layer two solutions and move on to another interesting Web3 topic.