-
-
Save 0xadrii/3677f0b5dfb9dcfe6b8b3953115d03f5 to your computer and use it in GitHub Desktop.
Mock ERC777 token inspired in OpenZeppelin's implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.19; | |
contract ERC777Mock { | |
mapping(address => uint256) private _balances; | |
uint256 private _totalSupply; | |
string private _name; | |
string private _symbol; | |
// ERC20-allowances | |
mapping(address => mapping(address => uint256)) private _allowances; | |
/** | |
* @dev `defaultOperators` may be an empty array. | |
*/ | |
constructor(string memory name_, string memory symbol_, uint256 amount) { | |
_name = name_; | |
_symbol = symbol_; | |
_mint(msg.sender, amount, "", ""); | |
} | |
function mint(address receiver, uint256 amount) public { | |
_mint(receiver, amount, "", ""); | |
} | |
/** | |
* @dev See {IERC777-name}. | |
*/ | |
function name() public view virtual returns (string memory) { | |
return _name; | |
} | |
/** | |
* @dev See {IERC777-symbol}. | |
*/ | |
function symbol() public view virtual returns (string memory) { | |
return _symbol; | |
} | |
/** | |
* @dev See {ERC20-decimals}. | |
* | |
* Always returns 18, as per the | |
* [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility). | |
*/ | |
function decimals() public pure virtual returns (uint8) { | |
return 18; | |
} | |
/** | |
* @dev See {IERC777-granularity}. | |
* | |
* This implementation always returns `1`. | |
*/ | |
function granularity() public view virtual returns (uint256) { | |
return 1; | |
} | |
/** | |
* @dev See {IERC777-totalSupply}. | |
*/ | |
function totalSupply() public view virtual returns (uint256) { | |
return _totalSupply; | |
} | |
/** | |
* @dev Returns the amount of tokens owned by an account (`tokenHolder`). | |
*/ | |
function balanceOf(address tokenHolder) public view virtual returns (uint256) { | |
return _balances[tokenHolder]; | |
} | |
/** | |
* @dev See {IERC20-transfer}. | |
* | |
* Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient} | |
* interface if it is a contract. | |
* | |
* Also emits a {Sent} event. | |
*/ | |
function transfer(address recipient, uint256 amount) public virtual returns (bool) { | |
require(recipient != address(0), "ERC777: transfer to the zero address"); | |
address from = msg.sender; | |
_callTokensToSend(from, from, recipient, amount, "", ""); | |
_move(from, from, recipient, amount, "", ""); | |
_callTokensReceived(from, from, recipient, amount, "", "", false); | |
return true; | |
} | |
/** | |
* @dev See {IERC20-allowance}. | |
* | |
* Note that operator and allowance concepts are orthogonal: operators may | |
* not have allowance, and accounts with allowance may not be operators | |
* themselves. | |
*/ | |
function allowance(address holder, address spender) public view virtual returns (uint256) { | |
return _allowances[holder][spender]; | |
} | |
/** | |
* @dev See {IERC20-approve}. | |
* | |
* Note that accounts cannot have allowance issued by their operators. | |
*/ | |
function approve(address spender, uint256 value) public virtual returns (bool) { | |
address holder = msg.sender; | |
_approve(holder, spender, value); | |
return true; | |
} | |
/** | |
* @dev See {IERC20-transferFrom}. | |
* | |
* Note that operator and allowance concepts are orthogonal: operators cannot | |
* call `transferFrom` (unless they have allowance), and accounts with | |
* allowance cannot call `operatorSend` (unless they are operators). | |
* | |
* Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events. | |
*/ | |
function transferFrom(address holder, address recipient, uint256 amount) public virtual returns (bool) { | |
require(recipient != address(0), "ERC777: transfer to the zero address"); | |
require(holder != address(0), "ERC777: transfer from the zero address"); | |
address spender = msg.sender; | |
_callTokensToSend(spender, holder, recipient, amount, "", ""); | |
_move(spender, holder, recipient, amount, "", ""); | |
_approve(holder, spender, _allowances[holder][spender] - amount); | |
_callTokensReceived(spender, holder, recipient, amount, "", "", false); | |
return true; | |
} | |
/** | |
* @dev Creates `amount` tokens and assigns them to `account`, increasing | |
* the total supply. | |
* | |
* If a send hook is registered for `account`, the corresponding function | |
* will be called with `operator`, `data` and `operatorData`. | |
* | |
* See {IERC777Sender} and {IERC777Recipient}. | |
* | |
* Emits {Minted} and {IERC20-Transfer} events. | |
* | |
* Requirements | |
* | |
* - `account` cannot be the zero address. | |
* - if `account` is a contract, it must implement the {IERC777Recipient} | |
* interface. | |
*/ | |
function _mint(address account, uint256 amount, bytes memory userData, bytes memory operatorData) | |
internal | |
virtual | |
{ | |
require(account != address(0), "ERC777: mint to the zero address"); | |
address operator = msg.sender; | |
// Update state variables | |
_totalSupply = _totalSupply + amount; | |
_balances[account] = _balances[account] + amount; | |
} | |
/** | |
* @dev Burn tokens | |
* @param from address token holder address | |
* @param amount uint256 amount of tokens to burn | |
* @param data bytes extra information provided by the token holder | |
* @param operatorData bytes extra information provided by the operator (if any) | |
*/ | |
function _burn(address from, uint256 amount, bytes memory data, bytes memory operatorData) internal virtual { | |
require(from != address(0), "ERC777: burn from the zero address"); | |
address operator = msg.sender; | |
_callTokensToSend(operator, from, address(0), amount, data, operatorData); | |
_beforeTokenTransfer(operator, from, address(0), amount); | |
// Update state variables | |
_balances[from] = _balances[from] - amount; | |
_totalSupply = _totalSupply - amount; | |
} | |
function _move( | |
address operator, | |
address from, | |
address to, | |
uint256 amount, | |
bytes memory userData, | |
bytes memory operatorData | |
) private { | |
_beforeTokenTransfer(operator, from, to, amount); | |
_balances[from] = _balances[from] - amount; | |
_balances[to] = _balances[to] + amount; | |
} | |
/** | |
* @dev See {ERC20-_approve}. | |
* | |
* Note that accounts cannot have allowance issued by their operators. | |
*/ | |
function _approve(address holder, address spender, uint256 value) internal { | |
require(holder != address(0), "ERC777: approve from the zero address"); | |
require(spender != address(0), "ERC777: approve to the zero address"); | |
_allowances[holder][spender] = value; | |
} | |
function _callTokensToSend( | |
address operator, | |
address from, | |
address to, | |
uint256 amount, | |
bytes memory userData, | |
bytes memory operatorData | |
) private { | |
bytes memory data = abi.encodeWithSignature("tokensToSend(address,address,address,uint256,bytes,bytes)", operator, from, to, amount, userData, operatorData); | |
from.call(data); // don't check if call was successful | |
} | |
function _callTokensReceived( | |
address operator, | |
address from, | |
address to, | |
uint256 amount, | |
bytes memory userData, | |
bytes memory operatorData, | |
bool | |
) private { | |
bytes memory data = abi.encodeWithSignature("tokensReceived(address,address,address,uint256,bytes,bytes)", operator, from, to, amount, userData, operatorData); | |
to.call(data); // don't check if call was successful | |
} | |
/** | |
* @dev Hook that is called before any token transfer. This includes | |
* calls to {send}, {transfer}, {operatorSend}, minting and burning. | |
* | |
* Calling conditions: | |
* | |
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens | |
* will be to transferred to `to`. | |
* - when `from` is zero, `amount` tokens will be minted for `to`. | |
* - when `to` is zero, `amount` of ``from``'s tokens will be burned. | |
* - `from` and `to` are never both zero. | |
* | |
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. | |
*/ | |
function _beforeTokenTransfer(address operator, address from, address to, uint256 amount) internal virtual {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment