Create a Wrapped ERC20 token on Avalanche with Vyper! 🐍🪙
Tags: vyper, blockchain, web3, python
Published: 2022-12-01 03:14:24

##  Introduction
In this article, we will describe the use cases of an ERC20 token and explain how to create one from scratch using Vyper, and we will deploy it to the Avalanche Fuji test network.

## Prerequisites
- Basic programming and console/terminal use experience
- A simple understanding of Solidity or Vyper

## Requirements
- [NodeJS](https://nodejs.org/en/)
- [Python](https://www.python.org/) and [Brownie ETH](https://eth-brownie.readthedocs.io/), or any smart contract framework
- [VSCode](https://code.visualstudio.com/), or another code editor

## What is an ERC20?
An ERC20 smart contract keeps track of fungible tokens, and the contract itself allows us to transfer, burn, and do much interesting stuff with that particular token.

The interface itself is really simple and doesn't include a lot of information about the token itself.

```python
# Functions
totalSupply()
balanceOf(account)
transfer(to, amount)
allowance(owner, spender)
approve(spender, amount)
transferFrom(from, to, amount)
```

These six functions are the original function defined on the EIP20, we can extend a lot of functionality by adding more functions (more features) to our smart contract.

In this article we are going to create an ERC20 smart contract, that takes in AVAX and returns WAVAX, our wrapped implementation of the cryptocurrency. 

We are going to allow the users to mint tokens by depositing AVAX and burn tokens and get AVAX in return.

## Why they are important?
Tokens are a fundamental part of the web3 space, they can be used in a lot of creative and unique ways, like shares in a community, transactional value, currency in a protocol, and much more. In this tutorial, we are going to create a token that wraps a cryptocurrency (AVAX in this case) and that token can be used in defi protocols, web3 protocols, and more.

## Setup
We are going to use [Brownie](https://eth-brownie.readthedocs.io) in this tutorial, [after you installed Brownie](https://eth-brownie.readthedocs.io/en/stable/install.html), create a new folder called `avalanche-swap` and inside it run the following command:

```bash
$ brownie init
```

Also, we are going to be using the Hardhat node to test our smart contracts, so inside your project folder, run the following command:

```bash
$ npm install --save-dev hardhat
```

## Smart contract
Let's start with the boilerplate code. We create a file named `contracts/WAVAX.vy`, define the vyper version as >= 0.3.7, and import the ERC20 and ERC20 Detailed interfaces from vyper.

```python
# @version >=0.3.7

from vyper.interfaces import ERC20
from vyper.interfaces import ERC20Detailed

implements: ERC20
implements: ERC20Detailed
```

### Events
We need to define our `events`, these are messages that the smart contract emits when called and the ones that we're defining at what the EIP20 proposed.

```python
event Transfer:
    sender: indexed(address)
    receiver: indexed(address)
    value: uint256

event Approval:
    owner: indexed(address)
    spender: indexed(address)
    value: uint256
```

### State
Our smart contract needs state, or variables that will persist across the entire life of the smart contract. These variables contain information that is going to be useful to our users and to the methods that we will define below, like `balanceOf`, `totalSupply`, and `allowance`

```python
# @dev name of the token ("Wrapped AVAX")
name: public(String[32])

# @dev symbol or ticker of the token ("WAVAX")
symbol: public(String[32])

# @dev the amount of decimals the token contains
decimals: public(uint8)

# @dev the balance of a particular address
balanceOf: public(HashMap[address, uint256])

# @dev addresses can allow other's addresses to spend their tokens
allowance: public(HashMap[address, HashMap[address, uint256]])

# @dev the number of tokens in circulation
totalSupply: public(uint256)
```
As you can see we define a variable to store how many decimals our token will have, this is because [Solidity](https://docs.soliditylang.org/en/v0.8.17/), the main programming langue used on EVM blockchains, has no support for using decimals, and we define decimals like integers, for example, we want to use 2 decimals, to express the number 100.99, we would put it like this 10099, being the last to digits the actual decimal part.

### Constructor
After that is complete we now define the constructor. Here we specify the name of the token, the symbol, and the number of decimals this token will have.

```python
@external
def __init__():
    self.name = "Wrapped AVAX"
    self.symbol = "WAVAX"
    self.decimals = 18
```

### Internal methods
The internal methods are really simple we need a method to mint our Wrapped AVAX tokens if the user sends the amount that they want and on to burn them to retrieve their tokens.

```python
@internal
def _mint(_to: address, _value: uint256):
    assert _to != empty(address)
    self.totalSupply += _value
    self.balanceOf[_to] += _value
    log Transfer(empty(address), _to, _value)
```

```python
@internal
def _burn(_to: address, _value: uint256):
    assert _to != empty(address)
    self.totalSupply -= _value
    self.balanceOf[_to] -= _value
    send(_to, _value)
    log Transfer(_to, empty(address), _value)
```

These two methods are standard on ERC20 implementations, since we need a way to mint the tokens, and (if we choose so) burn them as well.

## External methods
We now need to define the main methods of an ERC20 token, `transfer`, `transferFrom`, and `approve`. These three methods allow our token holders to use the tokens however they want.

The `transfer` method is self-explanatory and allows the token holder to transfer tokens to another user or a smart contract.

```python
@external
def transfer(_to : address, _value : uint256) -> bool:
    self.balanceOf[msg.sender] -= _value
    self.balanceOf[_to] += _value
    log Transfer(msg.sender, _to, _value)
    return True
```

The token standard needs a `approve` method, to enable the `transferFrom` method that we define next. The `approve` method gives the token holder a way to allow another account to manage a defined amount of tokens for them.

```python
@external
def approve(_spender : address, _value : uint256) -> bool:
    self.allowance[msg.sender][_spender] = _value
    log Approval(msg.sender, _spender, _value)
    return True
```

And the `transferFrom` method gives the user the ability to another account to transfer tokens on behalf of the token holder or to manage a set amount of tokens for them.

```python
@external
def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
    self.balanceOf[_from] -= _value
    self.balanceOf[_to] += _value

    self.allowance[_from][msg.sender] -= _value
    log Transfer(_from, _to, _value)
    return True
```

Since this is a wrapped token, we need a way to receive the cryptocurrency that we are trying to wrap and mint an equivalent amount of tokens, and also a way to do this in reverse, burn several tokens and receive the equivalent cryptocurrency back.

For the `mint` method, the user sends some cryptocurrency with the method call and gets the same amount back in tokens.
```python
@external
@payable
def mint():
    self._mint(msg.sender, msg.value)
```

And for the `burn` method, the user passes the number of tokens they want to burn, to receive the equivalent in cryptocurrency.
```python
@external
def burn(_value: uint256):
    self._burn(msg.sender, _value)
```

We will also define a **default method**, this method will be triggered if a user sends some AVAX to the contract directly, without calling the `mint` function. And in turn, the contract will mint that user the equivalent in tokens.

```python
@external
@payable
def __default__():
    self._mint(msg.sender, msg.value)
```

And that would be it. Now we have a token smart contract, that mints wrapped tokens. Yours should look something like this:

```python
# @version >=0.3.7

from vyper.interfaces import ERC20
from vyper.interfaces import ERC20Detailed

implements: ERC20
implements: ERC20Detailed

event Transfer:
    sender: indexed(address)
    receiver: indexed(address)
    value: uint256

event Approval:
    owner: indexed(address)
    spender: indexed(address)
    value: uint256

# @dev name of the token ("Wrapped AVAX")
name: public(String[32])

# @dev symbol or ticker of the token ("WAVAX")
symbol: public(String[32])

# @dev the amount of decimals the token contains
decimals: public(uint8)

# @dev the balance of a particular address
balanceOf: public(HashMap[address, uint256])

# @dev addresses can allow other's addresses to spend their tokens
allowance: public(HashMap[address, HashMap[address, uint256]])

# @dev the number of tokens in circulation
totalSupply: public(uint256)


@external
def __init__():
    self.name = "Wrapped AVAX"
    self.symbol = "WAVAX"
    self.decimals = 18


@internal
def _mint(_to: address, _value: uint256):
    assert _to != empty(address)
    self.totalSupply += _value
    self.balanceOf[_to] += _value
    log Transfer(empty(address), _to, _value)


@internal
def _burn(_to: address, _value: uint256):
    assert _to != empty(address)
    self.totalSupply -= _value
    self.balanceOf[_to] -= _value
    send(_to, _value)
    log Transfer(_to, empty(address), _value)


@external
def transfer(_to : address, _value : uint256) -> bool:
    self.balanceOf[msg.sender] -= _value
    self.balanceOf[_to] += _value
    log Transfer(msg.sender, _to, _value)
    return True


@external
def approve(_spender : address, _value : uint256) -> bool:
    self.allowance[msg.sender][_spender] = _value
    log Approval(msg.sender, _spender, _value)
    return True


@external
def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
    self.balanceOf[_from] -= _value
    self.balanceOf[_to] += _value

    self.allowance[_from][msg.sender] -= _value
    log Transfer(_from, _to, _value)
    return True


@external
@payable
def mint():
    self._mint(msg.sender, msg.value)


@external
def burn(_value: uint256):
    self._burn(msg.sender, _value)


@external
@payable
def __default__():
    self._mint(msg.sender, msg.value)
```

## Deploy using [Brownie](https://eth-brownie.readthedocs.io/)
In this tutorial, we are going to use Brownie to deploy our smart contracts, you can use any other smart contract framework if you are familiar with it [Hardhat](https://hardhat.org/) or [ApeWorx](https://academy.apeworx.io/) both have support for developing and deploying vyper smart contracts.

Since we are deploying to Avalanche, we need to add the Avalanche mainnet and the Avalanche Fuji testnet to our supported networks. In our terminal type the following:

```bash
$ brownie networks add Avalanche avax-testnet host=https://api.avax-test.network/ext/bc/C/rpc chainid=43113 explorer=https://testnet.snowtrace.io/ name=Fuji
```

```bash
$ brownie networks add Avalanche avax-mainnet host=https://api.avax.network/ext/bc/C/rpc chainid=43114 explorer=https://snowtrace.io/ name=Mainnet
```

After we run these two commands brownie will add Avalanche to Brownie.

```bash
Avalanche
  ├─Mainnet: avax-mainnet
  └─Fuji: avax-testnet
```

After that is complete, you can write our deploy script.

Our script is really simple we are going to take our smart contract, and deploy it.

Our script will first check if we are on any of the Avalanche networks, and if so will deploy the smart contracts using a real wallet (with some AVAX in it), if not we will assume that we are on our local network and deploy them there.

To deploy to a live blockchain (Fuji or Mainnet) we need to have a wallet with some AVAX on it, [you can get some testnet AVAX tokens on this faucet](https://faucet.avax.network/). Also, we need to define our `brownie-config.yaml` file to let Brownie know where to find our private key, and some important configurations as well.

Create a `brownie-config.yaml` on the root of your project. It should look something like this.

```yaml
dotenv: .env

networks:
  default: hardhat

wallets:
  from_key: ${PRIVATE_KEY}

compiler:
  vyper:
    version: "0.3.7"
```

The configuration file is expecting a `.env` file to work properly, create one on the root of your project and populate it with this.

```env
PRIVATE_KEY="YOUR_PRIVATE_KEY_HERE"
```

After we have our configuration setup, we need to write our deploy script. On the `scripts` folder create a new file called `deploy.py` this python program will be responsible for deploying our smart contracts.

The `deploy.py` file should look something like this:

```python
from brownie import accounts, network, config, WAVAX


def main():
    supported_networks = ["avax-mainnet", "avax-testnet"]
    active_network = network.show_active()

    deployer = accounts[0]
    if active_network in supported_networks:
        deployer = accounts.add(config["wallets"]["from_key"])
        
    WAVAX.deploy({"from": deployer}) 
```
To run this script write the following command on your terminal, this will deploy the contract to your hardhat local node.

```bash
$ brownie run deploy
```

To deploy it to a testnet write the following one.

```bash
$ brownie run deploy --network avax-testnet
```

And to deploy them to the Avalanche mainnet run the following command:

```bash
$ brownie run deploy --network avax-mainnet
```

And that would be it on the smart contract side, we created, tested, and deployed our wrapped token smart contract.

## Testnet smart contract (Fuji)

[WAVAX](https://testnet.snowtrace.io/address/0x4e9f59b082280c802922eb166d36cf1439ad0f17)