We’re going to make a crypto wallet that allows you to create accounts, recover accounts, send and receive crypto and view your transactions. Very similar to the popular crypto wallet Metamask but with a few extra features.

We’ll be creating the wallet in such a way that it can be used as a standalone web app or as a browser extension.

As an additional bonus, we’re going to be using ChatGPT to help us write our code. If you don’t have access to chatGPT you can use find the code on Github.

You can also follow along with this tutorial as a video, use the written tutorial (this page), view the source code on Github, live demo at wallet.atila.ca or view the browser extension in the Chrome webstore (link won’t work yet, it will viewable once approved).

aqua-wallet-demo-2022-11-19.gif

Getting Started

There are 3 different ways to get set up with the initial repo. Using a cloud IDE (option 3) is the simplest option and allows you to do everything in your browser without having to install anything. But I’ve found that sometimes the browser code takes longer to run and compile compared to running the code in your local machine.

Option 1 and 2 also gives you more customizability but requires more setup.

Option 1: Clone Existing Repo

  1. git clone [<https://github.com/atilatech/aqua-wallet>](<https://github.com/atilatech/aqua-wallet>)
  2. cd aqua-wallet
  3. Checkout the very first commit: git checkout 4341eb6d23407845678c8774003cac92c1a9e1c8
    1. https://github.com/atilatech/aqua-wallet/commit/4341eb6d23407845678c8774003cac92c1a9e1c8
  4. Checkout a new branch to avoid being in a detached head state: git checkout -b create_wallet
  5. yarn install

Option 2: Create React App from Scratch

  1. npx create-react-app aqua-wallet --template typescript

Option 3: Use a cloud IDE

A cloud IDE allows you to develop your React project quickly without having to download or install anything and customize your local environment.

  1. StackBlitz: https://stackblitz.com/fork/github/atilatech/aqua-wallet/tree/4341eb6d23407845678c8774003cac92c1a9e1c8
  2. Codesandbox: https://githubbox.com/atilatech/aqua-wallet/tree/4341eb6d23407845678c8774003cac92c1a9e1c8

Stackblitz is typically faster. With Codesandbox you can navigate to https://github.com/atilatech/aqua-wallet/tree/4341eb6d23407845678c8774003cac92c1a9e1c8 and then add “box” to the end of github in the URL.

Create Account

First we’re going to add a button that allows you to create an account. 3:20 in the video.

ChatGPT Prompt: create a react typescript component called AccountCreate that has button called Create Account, when the button is clicked, it calls a create account function

Screen Shot 2022-12-19 at 7.22.15 AM.png

Very good but we want createAccount to be defined inside AccountCreate:

Screen Shot 2022-12-19 at 7.13.47 AM.png

Now we can add that file to our project. If you are running the project locally: mkdir -p src/scenes/Account && touch src/scenes/Account/AccountCreate.tsx

import as React from 'react';

interface AccountCreateProps {

}

const AccountCreate: React.FC<AccountCreateProps> () => {
	const createAccount = () => { 
			// Your code to create an account goes here
	}
	return (
		<div>
			<button onClick={createAccount}>Create Account</button>
		</div>
	);
};

export default AccountCreate;

Create a Seed Phrase, Private Key and Address

ChatGPT Prompt: create a typescript function that generates a seed phrase, private key and address using the ethers library.7:30 in video

Screen Shot 2022-12-19 at 7.23.43 AM.png

We can then ask it to generate using a seedPhrase if it’s passed in: 9:05 in video.

Screen Shot 2022-12-19 at 7.37.30 AM.png

ChatGPT makes a mistake

ChatGPT made a mistake in the previous code. 11:05 in video

That function actually won’t work and generates the following error: Type 'Mnemonic' is not assignable to type 'string'.

Let’s see if ChatGPT can fix it’s mistake if we simply copy-paste the error message we’ve received:

Screen Shot 2022-12-19 at 7.35.06 AM.png

Install Ethers

If you are running the project locally: you can install it with yarn add ethers

If you’re using the web IDE like Stackblitz or CodeSandbox you can just search for ethers and add it to the project

How are Ethereum Accounts created

To understand how we created this account, let’s talk about how a new account is created and how the seed phrase is connected to the private key and address. 15:10 in video.

diagram (1).svg

Here is an overview of how to go from a seed phrase to a private key, public key, and address in Ethereum:

  1. A seed phrase (also known as a mnemonic phrase) is a sequence of words that represents a private key. It is a way to encode a private key in a form that is easier for humans to remember and record.
  2. To generate a private key from a seed phrase, you can use a key derivation function (KDF) such as BIP32 or BIP39. These functions use a mathematical algorithm to generate a private key from a seed phrase and other input parameters.
  3. Once you have a private key, you can use it to generate a public key. In Ethereum, a public key is a 64-byte (or 512-bit) value that is derived from a private key using the elliptic curve algorithm ECDSA.
  4. To generate an Ethereum address from a public key, you can use the Keccak-256 hash function to generate a 20-byte (or 160-bit) value from the public key. This value is the Ethereum address, which is used to identify the owner of the address on the Ethereum network.

Guess what. The previous section (and diagram!) was written by chatGPT. I used the following prompts:

can you explain how to go from seed phrase to private key to public key to address in ethereum

Source: @tomiwa1a twitter

Source: @tomiwa1a twitter

Creating the Diagram

Seeing a concept explained visually helps make it easier to understand. ChatGPT can’t draw diagrams but it can write scripts that can generate concepts. Thanks to Tony Hirst’s blog post, Generating (But not Previewing Diagrams) using ChatGPT for the idea. You can follow this Twitter thread of me live-tweeting the experience of getting the diagram to work. 18:10 in the video.

Update: It should have said Elliptic Curve Digital Signature Algorithm ECDSA not ECMDA (thank you Kavitha for pointing this out in the Youtube comments and the blog)

ChatGPT Prompt: usem mermaid.js to make a diagram of this processs

Screen Shot 2022-12-17 at 4.50.33 PM.png

can you please add labels to the arrows in the diagram showing where PBKDF2, ECMDA and Keccak are being used

Screen Shot 2022-12-17 at 4.50.40 PM.png

Protip: You can copy-paste the generated code inside html.onlineviewer.net to quickly preview the diagram and ask ChatGPT to add a button to download the image.

Here’s the finished product:

diagram (1).svg

You might be able to try your mileage with other diagram libraries like d3.js but I ran into token limits when I did that, maybe someone can try again with d3.js or another diagram library and share their results with me (@tomiwa1a or tomiwa@atila.ca ).

There are some pretty interesting bugs I ran into during this process, for example, ChatGPT was originally using an outdated version of mermaid.js but the solution was to ask it to “use latest version”.

Recover Account

We can ask ChatGPT to refactor our code to add a button that will allow us to recover existing accounts. 24:45 in the video.

refactor this component to also include a recover account button

when this button is clicked the first time only, it shows an input that accepts a seed phrase

When enter is pressed on that input, it calls the generate account function with the seedPhrase passed in

<Paste code in AccountCreate.tsx>

Screen Shot 2022-12-19 at 8.09.37 AM.png

Screen Shot 2022-12-19 at 8.09.23 AM.png

Add Bootstrap Styles

Our app will be launched as a browser extension and web app so we want to keep the dependencies simple, so we will manually download the comipled Bootstrap 5 css and js files and put them into our public/ folder and reference those files in index.html. 26:50 in video.

Ask ChatGPT to rewrite the AccountCreate component with Bootstrap styles. This part was impressive because it knew to use btn-primary for the Create Account button and btn-secondary for the recover account.

Build as extension

To convert our wallet from a browser into a browser extension following the following steps. 30: 15 in the video.

  1. Add a wallet app icon
  2. Edit manifest.json
  3. yarn build
  4. If you are running in desktop you will have to download the files and build them or you can just skip this step and only run as a web app.

The rest of the code can be found by watching the video at 35:05 onwards and using the code at aqua-wallet@1.0.0.

Getting Transactions

In order to get the Transactions we’ll be using the Moralis API. 53:55 in the video. We’re using a 3rd party API because reading from the blockchain is hard. Anytime you start integrating 3rd party APIs you want to make sure you’re very thoughtful about how and why you are integrating them. Here’s the thought process I used:

  • Why use Moralis?

    • The 2 main options are Alchemy and Moralis
    • Alchemy seems to offer more services however Moralis supports both Ethereum and BNB Chains
    • Etherscan could work but requires separate API key for each chain
  • Use Moralis HTTP API

    • Why not use the SDK?
      • We want to keep our site size small, important for when we turn it into an extension
      • We want to reduce our dependence on any centralized entity
      • We don’t want our code to be too coupled to one provider

    Browser Extension Tips

    // To test that your extension is working, we can trigger a request account
    // copy the following scripts into your browser console
    // source: <https://gist.github.com/ademidun/29268d38592a84703812c7984f4863cd>
    
    ethereum
        .request({ method: 'eth_requestAccounts' })
        .then((accounts)=>{
        	console.log({accounts});
        })
        .catch((error) => {
            console.error({error})
        });
        
    // add a Proxy to listen to when all methods on window.ethereum is called
    // ChatGPT prompt: how can i listen to all the function calls for a given object
    // ChatGPT: *returned a working function but only with `apply:`. Had to also include `get:` for it to work
    // Chat GPT prompt: the proxy doesn't get called, please fix
    
    window.ethereum = new Proxy(window.ethereum, {
      // Proxy handler that intercepts function calls and property accesses
      apply: function(target, thisArg, args) {
        // Log the function call
        console.log(`apply: ${target.name} called with arguments:`, args);
    
        // Call the original function
        return Reflect.apply(target, thisArg, args);
      },
    
      get: function(target, prop) {
        // Return a function if the property is a function
        if (typeof target[prop] === "function") {
          return function(...args) {
            // Log the function call
            console.log(`get: ${String(prop)} called with arguments:`, args);
    
            // Call the original function
            return target[prop](...args);
          };
        }
    
        // Return the original property value
        return target[prop];
      },
    });