diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index 4ce51faf2..24eb1bc9d 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -4,7 +4,8 @@ Mapping Types ============= -You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``. +Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables +are declared as a mapping type using the syntax ``mapping (_KeyType => _ValueType) _VariableModifiers _VariableName``. The ``_KeyType`` can be any elementary type. This means it can be any of the built-in value types plus ``bytes`` and ``string``. User-defined or complex types like contract types, enums, mappings, structs and any array type @@ -27,11 +28,16 @@ They cannot be used as parameters or return parameters of contract functions that are publicly visible. You can mark state variables of mapping type as ``public`` and Solidity creates a -:ref:`getter ` for you. The ``_KeyType`` becomes a -parameter for the getter. If ``_ValueType`` is a value type or a struct, -the getter returns ``_ValueType``. +:ref:`getter ` for you. The ``_KeyType`` becomes a parameter for the getter. +If ``_ValueType`` is a value type or a struct, the getter returns ``_ValueType``. If ``_ValueType`` is an array or a mapping, the getter has one parameter for -each ``_KeyType``, recursively. For example with a mapping: +each ``_KeyType``, recursively. + +In the example below, the ``MappingExample`` contract defines a public ``balances`` +mapping, with the key type an ``address``, and a value type a ``uint``, mapping +an Ethereum address to an unsigned integer value. As ``uint`` is a value type, the getter +returns a value that matches the type, which you can see in the ``MappingUser`` +contract that returns the value at the specified address. :: @@ -53,6 +59,52 @@ each ``_KeyType``, recursively. For example with a mapping: } } +The example below is a simplified version of an `ERC20 token `_. +``_allowances`` is an example of a mapping type inside another mapping type. +The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account. + +:: + + pragma solidity >=0.4.0 <0.7.0; + + contract MappingExample { + + mapping (address => uint256) private _balances; + mapping (address => mapping (address => uint256)) private _allowances; + + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + + function allowance(address owner, address spender) public view returns (uint256) { + return _allowances[owner][spender]; + } + + function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { + _transfer(sender, recipient, amount); + approve(sender, msg.sender, amount); + return true; + } + + function approve(address owner, address spender, uint256 amount) public returns (bool) { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + return true; + } + + function _transfer(address sender, address recipient, uint256 amount) internal { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _balances[sender] -= amount; + _balances[recipient] += amount; + emit Transfer(sender, recipient, amount); + } + } + + .. index:: !iterable mappings .. _iterable-mappings: diff --git a/docs/types/operators.rst b/docs/types/operators.rst index afbe2a742..f53d6ee57 100644 --- a/docs/types/operators.rst +++ b/docs/types/operators.rst @@ -17,7 +17,7 @@ equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a d array of length zero or a static array of the same length with all elements set to their initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves all other elements and the length of the array untouched. This especially means that it leaves -a gap in the array. If you plan to remove items, a mapping is probably a better choice. +a gap in the array. If you plan to remove items, a :ref:`mapping ` is probably a better choice. For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat: