Ethereum smart contracts are coded contracts written in Solidity, or other compatible languages, that exist in a solely digital form. Their purpose is to perform a specific task without the need of a middleman through the use of the immutable code of the Ethereum blockchain. To read more about smart contracts, check out this Help Center article.
Every token that exists on the Ethereum Blockchain is tied to a smart contract, each with its own set of functions used to perform tasks.
ERC20, which stands for ‘Ethereum Request for Comment’, is just one type of smart contract standard that exists within the Ethereum ecosystem, but it is by far the most recognizable. Understanding the framework of an ERC20 smart contract can help shed light on exactly what’s going on when you send transactions, participate in token sales, or even check your balance.
This is not a guide to learn how to write smart contracts, but instead is an outline for those interested in learning more about what’s going on behind-the-scenes when they interact with their crypto.
Jump To:
- ERC20 Smart Contracts
- ERC20 (Optional)
- ERC223 Smart Contracts
- ERC721 Smart Contracts
- ERC721 (Optional)
- Newer Token Standards
ERC20 Smart Contracts
ERC20 Smart Contracts are composed of six mandatory functions and three optional (but more familiar) ones.
These first six functions are:
balanceOf()totalSupply()transfer()transferFrom()approve()allowance()
The optional ones determine factors such as the token's name, symbol, and the number of decimal places it measures.
totalSupply( )
The totalSupply() function pulls the entire circulating supply of the token that contract is tied to. Even though there may be a predetermined maximum set limit for the total supply, this function shows the current total supply in circulation, which may be less than its future total.
Example:
function totalSupply() public view returns (uint256);
balanceOf( )
The balance of your token is stored and retrieved through the balanceOf() function. This function requires a public Ethereum address to work properly, or else there is nowhere to pull a balance from.
Example:
function balanceOf(address _owner) public view returns (uint256 balance);
approve( )
This function is easier to think of as an authentication step for the owner of the contract. It approves the withdrawal of tokens from the owner’s address to the address that is attempting to retrieve tokens, up to a specified amount. This function works hand-in-hand with allowance().
Example:
function approve(address _spender, uint256 _value) public returns (bool success);
transfer( )
This function is one many users are familiar with, as it allows the transfer of tokens from the contract owner’s address to another address. A token's contract can also add its own rules, such as restricting who can receive tokens. Most tokens are permissionless, but some, particularly tokenized securities, are permissioned.
Example:
function transfer(address _to, uint256 _value) public returns (bool success);
transferFrom( )
If we already have a transfer() function, why do we need a transferFrom()?
Well, think of it as an automatic transfer function. transferFrom() allows the smart contract to automatically send tokens from the token holder's address to another address, assuming the person has been approved to withdraw. It's generally used by the contract itself, not by an actual person.
Example:
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
allowance( )
This function checks the amount of tokens that the owner is allowing to be transferred by the smart contract from the owner’s address to another address. This function generally works hand-in-hand with approve(), as it checks the amount of tokens that have been approved for transfer. Think of the value seen here as a limit cap on token withdrawals, so the supply can be properly managed.
Example:
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
ERC20 (Optional)
These additional fields are more familiar to those who have added a custom token on MEW before. These details can generally be found pretty easily on an Ethereum blockchain explorer, like Etherscan. They delegate the name, symbol, and amount of decimal places that tokens are read to.
Token Name
This sets the name for the token.
Example:
string public constant name = “My Token Name”;
Token Symbol
This sets the 3 - 4 letter abbreviation for the token.
Example:
string public constant symbol = “MTN”;
Number of Decimals
This sets the decimal place for reading the amount of the token. You can’t write decimal places in Solidity, so the only way to specify how many decimal places you want to go out to is through whole numbers and this decimal function. Decimals are usually set to ‘18’.
Example:
uint8 public constant decimals = 18;
ERC223 Smart Contracts
After enough time, problems with the ERC20 standard began to pop up. One problem in particular has caused a lot of heartaches and lost assets. If the transfer() function is used to send tokens directly to the main address of a smart contract, they are locked forever within the contract and can never be retrieved.
ERC223 was one of several proposals to fix this issue, but ERC20 remained the dominant standard. ERC223 is built with a newly defined transfer() function that allows tokens to be sent to either a personal address or a smart contract. It also includes a tokenFallback() function that checks the receiving contract for the same function. If the receiving contract does not have a tokenFallback() function, then the transaction will fail and all assets are returned to the address that sent them initially.
This new definition of transfer() eliminates the issue of lost tokens locked in contracts until the end of time, but sadly has no effect on all the previous ERC20 contracts that have already been written. It’s also worth mentioning that sending ERC223 tokens to a contract is a one-step process (rather than the two step approve-transfer process for ERC20 tokens). As such, it requires less gas costs to execute, making it very energy efficient!
ERC721 Smart Contracts
The second most recognizable smart contract on Ethereum is the ERC721 contract, which we’ll go over briefly. ERC721 is famous for allowing the creation of unique tokens called Non-Fungible Tokens (NFTs) that cannot be duplicated. With a unique token, an address can have indisputable ownership of whatever that token represents, such as a house deed, a rare piece of art, or a crypto-collectible.
ERC721 contracts have eight core functions, plus a supportsInterface() function that lets apps check which features a contract supports. There are also two optional extensions. Some of these functions are similar to the ERC20 standard.
The core functions are defined as:
balanceOf()ownerOf()approve()getApproved()setApprovalForAll()isApprovedForAll()transferFrom()safeTransferFrom()(which comes in two versions)
balanceOf(), approve(), transferFrom()
These functions work much like their ERC20 versions, with one difference: they deal with individual NFTs instead of amounts of a token.
For review:
balanceOf()pulls the number of NFTs held by a specified addressapprove()sets permission for another address to transfer a specific NFTtransferFrom()sends an NFT from one address to another
ownerOf( )
This is where ERC721 starts to get different. ownerOf() retrieves the address that owns whichever NFT ID number is searched. Ownership is defined by simply having the token, as each NFT is completely unique and can only exist on one address at a time.
safeTransferFrom()
This works like transferFrom(), but first checks that a receiving smart contract is able to handle NFTs. This helps prevent NFTs from getting stuck in contracts that can't use them.
getApproved(), setApprovalForAll(), isApprovedForAll()
These functions manage permissions. getApproved() shows which address, if any, has been approved to transfer a specific NFT. setApprovalForAll() lets a holder approve another address, called an operator, to manage all of their NFTs, and isApprovedForAll() checks whether that permission has been given.
ERC721 (Optional)
ERC721 also has two optional extensions.
The metadata extension covers the NFT's descriptive details:
name()gets the name of the tokensymbol()gets the 3 - 4 letter abbreviationtokenURI()retrieves a link to the NFT's metadata, such as links showing what it's meant to represent or any other details that may be helpful for identifying its purpose
The enumerable extension makes it possible to list and search through NFTs:
totalSupply()pulls the total supply of the tokentokenByIndex()looks up an NFT by its position in the full list of tokenstokenOfOwnerByIndex()looks up an NFT by its position in the list of tokens owned by a specific address
tokenOfOwnerByIndex() is necessary for anyone who plans to have more than one NFT. It allows NFT IDs to be searched through a list of tokens owned by the user. Since each NFT is unique, they are each listed individually.
Newer Token Standards
Token standards keep evolving, too. A few worth knowing:
- EIP-2612 (permit): lets a token holder approve spending with a signed message instead of a separate onchain approval transaction, so approvals can be gasless for the holder.
- ERC-4337 (account abstraction): adds smart contract wallets on top of Ethereum without changing the network itself.
- ERC-4626 (tokenized vaults): a standard interface for contracts that hold deposited assets and issue share tokens in return.
As ERC standards evolve over time, it’s important to stay up-to-date with the newest protocols. Make sure you follow us on Twitter, Instagram, and YouTube to stay informed with the latest happenings in the crypto-sphere.