ERC20 Token

Warning

This is example code for learning purposes. Do not use in production without thorough review and testing.

A standard ERC20 fungible token implementation.

  1#pragma version >0.3.10
  2
  3###########################################################################
  4## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
  5###########################################################################
  6
  7# @dev example implementation of an ERC20 token
  8# @author Takayuki Jimba (@yudetamago)
  9# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
 10
 11from ethereum.ercs import IERC20
 12from ethereum.ercs import IERC20Detailed
 13
 14implements: IERC20
 15implements: IERC20Detailed
 16
 17name: public(String[32])
 18symbol: public(String[32])
 19decimals: public(uint8)
 20
 21# NOTE: By declaring `balanceOf` as public, vyper automatically generates a 'balanceOf()' getter
 22#       method to allow access to account balances.
 23#       The _KeyType will become a required parameter for the getter and it will return _ValueType.
 24#       See: https://docs.vyperlang.org/en/v0.1.0-beta.8/types.html?highlight=getter#mappings
 25balanceOf: public(HashMap[address, uint256])
 26# By declaring `allowance` as public, vyper automatically generates the `allowance()` getter
 27allowance: public(HashMap[address, HashMap[address, uint256]])
 28# By declaring `totalSupply` as public, we automatically create the `totalSupply()` getter
 29totalSupply: public(uint256)
 30minter: address
 31
 32
 33@deploy
 34def __init__(_name: String[32], _symbol: String[32], _decimals: uint8, _supply: uint256):
 35    init_supply: uint256 = _supply * 10 ** convert(_decimals, uint256)
 36    self.name = _name
 37    self.symbol = _symbol
 38    self.decimals = _decimals
 39    self.balanceOf[msg.sender] = init_supply
 40    self.totalSupply = init_supply
 41    self.minter = msg.sender
 42    log IERC20.Transfer(sender=empty(address), receiver=msg.sender, value=init_supply)
 43
 44
 45@external
 46def transfer(_to : address, _value : uint256) -> bool:
 47    """
 48    @dev Transfer token for a specified address
 49    @param _to The address to transfer to.
 50    @param _value The amount to be transferred.
 51    """
 52    # NOTE: vyper does not allow underflows
 53    #       so the following subtraction would revert on insufficient balance
 54    self.balanceOf[msg.sender] -= _value
 55    self.balanceOf[_to] += _value
 56    log IERC20.Transfer(sender=msg.sender, receiver=_to, value=_value)
 57    return True
 58
 59
 60@external
 61def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
 62    """
 63     @dev Transfer tokens from one address to another.
 64     @param _from address The address which you want to send tokens from
 65     @param _to address The address which you want to transfer to
 66     @param _value uint256 the amount of tokens to be transferred
 67    """
 68    # NOTE: vyper does not allow underflows
 69    #       so the following subtraction would revert on insufficient balance
 70    self.balanceOf[_from] -= _value
 71    self.balanceOf[_to] += _value
 72    # NOTE: vyper does not allow underflows
 73    #      so the following subtraction would revert on insufficient allowance
 74    self.allowance[_from][msg.sender] -= _value
 75    log IERC20.Transfer(sender=_from, receiver=_to, value=_value)
 76    return True
 77
 78
 79@external
 80def approve(_spender : address, _value : uint256) -> bool:
 81    """
 82    @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
 83         Beware that changing an allowance with this method brings the risk that someone may use both the old
 84         and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
 85         race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
 86         https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
 87    @param _spender The address which will spend the funds.
 88    @param _value The amount of tokens to be spent.
 89    """
 90    self.allowance[msg.sender][_spender] = _value
 91    log IERC20.Approval(owner=msg.sender, spender=_spender, value=_value)
 92    return True
 93
 94
 95@external
 96def mint(_to: address, _value: uint256):
 97    """
 98    @dev Mint an amount of the token and assigns it to an account.
 99         This encapsulates the modification of balances such that the
100         proper events are emitted.
101    @param _to The account that will receive the created tokens.
102    @param _value The amount that will be created.
103    """
104    assert msg.sender == self.minter
105    assert _to != empty(address)
106    self.totalSupply += _value
107    self.balanceOf[_to] += _value
108    log IERC20.Transfer(sender=empty(address), receiver=_to, value=_value)
109
110
111@internal
112def _burn(_to: address, _value: uint256):
113    """
114    @dev Internal function that burns an amount of the token of a given
115         account.
116    @param _to The account whose tokens will be burned.
117    @param _value The amount that will be burned.
118    """
119    assert _to != empty(address)
120    self.totalSupply -= _value
121    self.balanceOf[_to] -= _value
122    log IERC20.Transfer(sender=_to, receiver=empty(address), value=_value)
123
124
125@external
126def burn(_value: uint256):
127    """
128    @dev Burn an amount of the token of msg.sender.
129    @param _value The amount that will be burned.
130    """
131    self._burn(msg.sender, _value)
132
133
134@external
135def burnFrom(_to: address, _value: uint256):
136    """
137    @dev Burn an amount of the token from a given account.
138    @param _to The account whose tokens will be burned.
139    @param _value The amount that will be burned.
140    """
141    self.allowance[_to][msg.sender] -= _value
142    self._burn(_to, _value)

Key features:

  • Implements the IERC20 and IERC20Detailed interfaces from ethereum.ercs

  • Standard transfer, transferFrom, and approve functions

  • mint and burn functions for supply management

  • Uses HashMap for balances and allowances

Note

This is example code. Production tokens require additional security review.

Notice how Vyper’s overflow/underflow protection is built-in: the comment “vyper does not allow underflows” explains why no explicit check is needed when subtracting from balances.