Smart Contract for ERC-20 Tokens
The first contract we are writing is the ERC20.sol smart contract. This contract will be inherited by the main token smart contract we will be writing later. For now, this ERC20.sol smart contract will implement the methods specified in the ERC-20 token standard.
To kick things off, create a file in the
contracts folder and name it
ERC20.sol. As we do with all smart contracts, start off by declaring the version of Solidity that we are writing the smart contract with.
In addition to declaring the version of Solidity we will be using, we will also import the interface and libraries that we created in the previous chapter.
Now we need to specify that the contract will inherit from the IERC20 interface and the Context library. Below the imports, let's add the contract declaration:
Let's revisit the IERC20 interface to help write the required methods and events for this contract. If our contract is not as specified in the IERC20 interface, we will run into errors at compilation.
ERC20.sol smart contract, we will be implementing the methods and events that we said needed to exist using the interface smart contract. In some of the methods we write, we will also trigger the events that we add to this smart contract.
There are a handful of methods we need to implement in the ERC-20 token we are creating, including:
transferFrom. There are also two major events that an ERC-20 token needs to capture:
Approval. We will look at each of these in a bit more detail as we implement them.
Let's add a few variables that will allow us to track this information. Whenever we have
uint256 variables, we want to use the implementation we specified in
SafeMath, and ensure protection from overflow and underflow errors. Inside the contract brackets, add:
Next we will add two mapping variables. Mappings act as hash tables that consist of key types and corresponding value type pairs. To define a mapping, you can write:
mapping(_KeyType => _ValueType) public mappingName.
A more tangible example for our token is to actually keep track of everyone's balances. Let's use a mapping to ensure that information is available. Add the following code to introduce a mapping that tracks balances.
Another mapping will check how much different users are allowed to send for the token we are creating. ERC-20 tokens should only be spent by the owners of the token. However, since Ethereum is a world of smart contracts, we may need to delegate the "spending" ability to a smart contract address.
To keep track of allowances, add the following code:
Here we created a mapping inside a mapping because we want to check if the user has delegated spending ability to anyone else and by how much.
The last variable we will add is
total supply - it will keep track of how many tokens we have in circulation. Simply add:
We kept these variables private, but now we will be writing public methods for the
ERC20.sol smart contract so that people can use the information we are capturing for the token.
This is an event for the
ERC20.sol smart contract. It will be called whenever tokens are transferred from one address to another. Since we declared the event in the
IERC20.sol interface, we don't need to add a new declaration for the event here, but simply call
emit Transfer(sender, recipient, amount) whenever we need it.
This is another event, which will be called whenever the approval amount of an address (ie how much it can spend) is updated. Again, as the event is declared in the interface, we don't need to declare it here and just call
emit Approval(owner, spender, amount) whenever we need it.
Now - let's look at the methods we need to add to the
ERC20.sol smart contract:
Add this method to the contract
ERC20.sol. It will just return the
_totalSupply variable we added.
A few things to note:
the method is public and view only
the method returns a uint256 value (the same value as the
_totalSupplyvariable we declared above)
the name of the method must match the name of the method in
IERC20.sol. (This will be the case for the next set of methods and events we create)
This public method gets information from our private mapping. To get the balance of a specific address, we can also add an input variable. Add this method to
Note the way we access mappings in Solidity methods. Though it is a hash object, we follow this convention:
We need to create two methods to implement this properly, one public and another internal.
The internal method will take in three variables and carry out a few checks.
sender(will be an
recipient(will be an
amount(will be a
ensure that the sender and receiver are not a zero address. The zero address on the Ethereum blockchain (i.e. 0x0000000000000000000000000000000000000000). No one has access to the zero address. If the zero address is interacting with your smart contracts, it means that something is very wrong.