How I Built An App That Wraps and Unwraps ETH

This article is not a tutorial. It's my thoughts and notes about my experience building an app that wraps and unwraps Ether. The app is called WrapETH.

What is WrapETH?

WrapETH is an app that allows you to wrap and unwrap ETH, (or another EVM chain's native asset). The current app version supports 7 different blockchains. The app is a front-end that communicates with the Wrapped Ether smart contract.

The Wrapped Ether (WETH) smart contract is decentralized code that exists on the Ethereum blockchain. It's not owned, or even affiliate with WrapETH the app. Other EVM chains also have a smart contract to wrap its native asset. For example, Polygon has WMATIC (Wrapped Matic). The WMATIC smart contract is a fork of the original WETH contract.

Funds are never in the custody of WrapETH. The app is only a front-end that calls functions on the smart contracts. The only 2 functions called are deposit and withdraw. You can view the code in the "contract" tab, and see what other functions are built into the contract.

Wrapped Ether smart contract functions
Wrapped Ether smart contract functions

Replacing Web3Modal with RainbowKit and WAGMI

My first major task was to replace Web3Modal with RainbowKit. Web3Modal is a JavaScript library that lets you connect apps to the blockchain. It makes it easy to connect your wallet to an app. It's makes developers' lives easier.

RainbowKit is similar to Web3Modal, but with a better UX for both users and developers. You can easily add in support for new EVM chains and various wallets. The "Connect Wallet" modal comes pre-built and styled, and you can customize it as desired.

RainbowKit UI - you can easily add support for various wallets
RainbowKit UI - you can easily add support for various wallets

What Are WAGMI Hooks?

"WAGMI makes it easy to "Connect Wallet," display ENS and balance information, sign messages, interact with contracts, and much more — all with caching, request deduplication, and persistence." (from the docs).

It's a library of pre-made hooks that uses Ethers.js under the hood. Ethers.js is a JavaScript library that lets you connect to the Ethereum blockchain. It has more features that you knew existed. WAGMI is an abstraction of Ethers.js.

An example of a WAGMI hook is useAccount. This returns an object that has details like the connected address, as well as boolean values such as isConnected.

Another useful hook is useBalance. The gets data about the connected wallet's account balance. It has a boolean property called watch, which updates the connected balance at every new block.

This hook saved me from writing many extra lines of code.

The ETH balance gets updated automatically
The ETH balance gets updated automatically

Another set of hooks I used were usePrepareContractWrite, useContractWrite and useWaitForTransaction. If you want to call "read" and "write" functions on a contract, the WAGMI docs suggest using these hooks together.

usePrepareContractWrite is used to prepare an instance of a smart contract. It returns an object that has a key called config. config can get passed into the useContractWrite hook, which is used to call smart contract functions that were extracted from usePrepareContractWrite.

useWaitForTransaction extracts data from useContractWrite and returns various data such as a hash of a successful transaction.

Implementing these hooks was challenging. I've only scratched the surface of what WAGMI hooks can do. Check out the WAGMI docs to learn more.

WAGMI logo
WAGMI logo

Refactoring Code And Building Custom Hooks

It was my first time using the WAGMI library. To get used to the hooks, I first placed everything into the main app component in my Next.js directory. This quickly became messy.

I tried prop drilling to pass hooks to child components of app, but this quickly becme messy, and introduced bugs to my code. I also tried calling hooks within a useEffect hook, but learned that you cannot nest hooks within other React hooks.

Like magic, refactoring all WAGMI hooks into custom hooks, within a /hooks directory, helped fix many of my bugs. Placing hooks into their own files made my code more readable and easier to maintain.

File structure containing hooks
File structure containing hooks

The idea of build custom React hooks used to intimidate me, but hooks are simple. Basically hooks are functions with the word use at the start of its name.

Replacing Formik With React-Hook-Form

This was my second major task of the project. It was also the most challenging for me. React-Hook-Form is a JavaScript library that helps manage forms, form state and validations.

React-form-hook has some funky syntax, but the documentation is well written and has good video explanations.

The form on WrapETH only has 1 input, but it takes in a monetary value that gets sent to a smart contract. When money is involved, it's critical that strict validations are in place. A bug in the front-end can cause an unexpected loss of funds.

People will track you down and send hate mail if they lose money in your app.

React-Hook-Form lets you create custom error messages
React-Hook-Form lets you create custom error messages

Integrating React-Hook-Form with Raid Guild Design System

Raid Guild has a Design System, which is built with Storybook and custom Chakra UI style components. Each design component aligns with the branding aesthetic that Raid Guild has in place.

My tasks were to upgrade some of the existing components and to create new ones. Chakra UI comes with pre-made React components, but it's also highly customizable.

One of my major challenges was setting up the NumberInput component, and integrating it with react-hook-form with it.

Raid Guild design system preview
Raid Guild design system preview

NumberInput takes in a spread of props from react-hook-form. If you're not used to Chakra, it can be confusing to figure out the correct syntax. After lots of practice and frustration, I eventually figured it out.

I received lots of help on this task. Since finishing the WrapETH project, I've been working on another project and still creating custom Chakra UI components. After more practice, I feel confident going back to Raid Guild Design-System and creating new components with less trouble.

Passing numeric values smart contracts

Another challenge I faced was passing data from the form to the usePrepareContractWrite WAGMI hook. The numeric value of Ether that users pass into the form get passed into this hook. If using JavaScript, passing numerical data to EVM blockchains is complicated.

Ethereum processes numbers in units of wei, (1 Ether is equal to 10^18 wei (source). Due to limitations that JavaScript has on integers, you need to use BigNumer to correctly pass numerical values to Ethereum smart contracts.

Figuring out the correct conversions took a bit of tinkering, but eventually I figured it out.

Other UX improvements

"Pending Transaction" toast is shown in the upper right
"Pending Transaction" toast is shown in the upper right

An important part of front-end development is giving users feedback when they perform an action. For example, if you click "submit", you probably want confirmation that the form was actually submitted

Feedback especially matters when money is involved. Your heart will skip a beat if you transfer funds, but don't get confirmation that the transaction actually gets sent.

With this in mind, I added a "toast" feature that alerts a user after a transaction has been initiated, and completed.

Chakra UI has a built in hook that triggers a toast. I changed its style on Raid Guild's Design System, and added the toast to WrapETH.

Chakra UI default Toast component. (Note the design difference between this, and the Toast in the upper right corner of the previous image).
Chakra UI default Toast component. (Note the design difference between this, and the Toast in the upper right corner of the previous image).

Here's how I implemented the toast hook:

A toast gets fired off in the following scenarios:

  • pending deposit

  • pending withdrawal

  • successful deposit

  • successful withdrawal

There are custom hooks in the app called useDeposit and useWithdraw. Within these hooks, I added the WAGMI hooks useContractWrite and useWaitForTransaction.

useContractWrite is used to write transactions to a contract. The WAGMI docs recommend using it with usePrepareContractWrite. This prepares an instance of a contract and takes in parameters such as address and abi.

After a transaction has been initiated with useContractWrite, you can use built-in logic to determine if the transaction is pending or not. The hook returns a function called onSuccess; this gets invoked when the hook fetches data.

Within the custon useDeposit hook, when onSuccess gets called, the useCustomToast hook from Design-System will be invoked. useCustomToast displays the alert you see in the upper right corner of the following image:

"Success" toast is shown in the upper right. Note that
"Success" toast is shown in the upper right. Note that

Similar logic is used within the useWaitForTransaction hook. This waits for a transaction to be processed, then returns an onSuccess function. onSuccess displays the "success" toast seen above.

TypeScript

This was my first time using TypeScript. It took me a while to get used to the syntax, but with practice I got the hang of it. This project was a great opportunity to "learn by doing." I prefer this to following tutorials nonstop.

At first I didn't know how to fix type errors, so I'd set the type to be any. This can work to troubleshoot unrelated problems, but it negates the benefits of using TypeScript in the first place.

As I got more experience with TypeScript, it grew to like it more. It seems useful for maintaining code, especially as your codebase grows, and more people contribute.

Since finishing the WrapETH project, I've continued using TypeScript in other apps. With more practice, it'll become second nature.

Local Environment Issues

Most new developers don’t think about their development environments. If you're new to tech, you likely don't even know what an environment is. I didn't.

Towards the end of this project, random package manager issues appeared. These were major blockers. I didn’t even know where begin troubleshooting. Thanks to a mentor, I was able to get help troubleshooting these issues. (More on mentors later in this post).

I cannot remember all of the bugs I fixed, but here are some of my notes:

Make sure to work on the same Node version as the most recent version of the app.

The first iteration of WrapETH was build using Node version 16.15. I was using Node version 18. Using different versions lead to bugs that were hard to troubleshoot. This can best avoided by… using different versions of Node.

Node Version Manager (NVM) solves this.

With NVM, you can run multiple versions of Node in separate terminals. For example, if you're building a new project, “Project A, ”you might want to run Node version 18. If you’re simultaneously working on “Project B,” you might want Node version 16. Without NVM, it’ll be a pain to manage different versions.

Check that your Path is setup correctly.

Path ($PATH) is a list of directories that your system uses, to search for anything that you type on the command line. I cannot explain this topic deeply because I don't fully understand it yet.

After troubleshooting bugs that were related to my dependency tree, it was determined that deleting and reinstalling my OS was a logical next step.

How'd I even get into this mess?

When I first started learning software development, I followed many YouTube tutorials. Often I’d install things that altered my path, and I didn’t know why. Often times these changes didn't solve my issues. This lead to a Path file so confusing, Satoshi Nakamoto wouldn't even make sense of it.

Reinstalling Mac OS fixed many of the dependency issues I was facing. This helped me understanding some of the reasons why many developers use a virtual machine for their projects. You can change your virtual machine environment in isolation from your regular computer's setup.

After reinstalling everything, I followed this guide ”Setting Up a New MacBook for JavaScript Development which worked well.

Git issues

Merge conflicts and other git issues are a pain point for me (and most developers).

Diagram of various Git features
Diagram of various Git features

Another person and I sometimes worked on the same branch. This caused merge conflicts. Git gave feedback that I needed to rebase. I botched some of the rebases, but I managed to work around it by switching between branches and copy pasting. This got messy and wasn't ideal.

The person who mentored me through this project recommended I try Git Tower, a visual tool for git. In the future I might write about this tool after using it more.

Git Stash

Before working on WrapETH I've never used git stash. Sometimes Git recommends this command if you're trying to commit changes, but your branch isn't up to date with the latest changes.

git stash saves the current version of your working branch. You can later apply your changes from the stash onto whatever branch you'd like.

Pro-tip (for git newbies): Name your stashes with the command: git stash save 'name-stash'. Naming a stash isn't required, but if you have too many stashes saved, you’ll get confused.

Visual Studio Code built in with Git features
Visual Studio Code built in with Git features

I learned that VS Code has a tab on the left side called 'Source Control'. This has visual helpers such as: commits, branches, remotes, stashes and more.

If you click the 'STASHES' dropdown, you'll see your stashes. Right clicking on a stash will give you several options, for example 'apply stash'.

Right clicking on STASHES gives you several options
Right clicking on STASHES gives you several options

My Apprenticeship At Raid Guild

This project was part of my apprenticeship at Raid Guild. Before becoming a full member of the DAO, you must be an apprentice first. The purpose of the apprenticeship is to gauge skill level, and alignment with other members. Once a full member, you can vote on proposals that affect the direction of the DAO.

To learn about my earlier experience at Raid Guild, read my article: "My Experience as a Raid Guild Apprentice".

The apprenticeship has certain terms on it. For example, the apprenticeship must last a minimum of 60 days before becoming a full member, as well as complete a project.

One of the most valuable parts my apprenticeship experience is receiving mentorship. This has lowered my learning curve. Having access to a mentor is a time saver because you can ask for help when you’re stuck. I ask a lot of questions, but I'm always working to balance asking for help, vs. struggling to find a solution by myself.

The Benefits Of Mentorship:

Mentorship is a use case for DAOs. I encourage all beginner (and all level) developers to join a DAO for this reason. Mentorship is a 2 way street. The mentee learns faster, and the mentor gains a better understand by explaining to someone else.

Raid Guild is a strong network of talented people who are on the cutting-edge of Web3. Learn more about Raid Guild here.

Conclusion

Working on WrapETH was both challenging and a great learning experience. I got practice reading documentation, implementing new libraries and building interesting tools.

By using many different libraries for the first time, I realized that many libraries are similar; it's all JavaScript. If you understand data types and data structures, you can figure out most libraries.

WrapETH currently supports these networks: Ethereum mainnet, Gnosis (XDAI), Polygon, Arbitrum, Optimism, Goerli and Sepolia testnets.

There are no fees (other than network fees), so try it out next time you need to wrap and unwrap ETH. I've used it several times on Ethereum mainnet, which was the most rewarding part of the experience.

WrapETH GitHub.

Subscribe to starrdev
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.