2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > Solidity ERC777标准

Solidity ERC777标准

时间:2021-09-28 06:39:01

相关推荐

Solidity ERC777标准

Openzeppelin ERC777标准实现

ERC777是一种能够向后兼容ERC20的代币标准,与ERC20的区别在于其新功能:openrater和hooks。

文章目录

Openzeppelin ERC777标准实现一、Operator二、Hooks三、接口四、实现

一、Operator

operator可以代理用户进行transfer、mint、burn操作,并且没有allowance的限制。应用场景举例:

1.有些朋友有多个以太坊钱包地址,不管是用于薅空投还是分批管理自己的代币资产;这样的情况下有时候会遇到这样的情况,即多个地址中只有一个地址具有ETH,别的地址都没有ETH,当需要对没有ETH的钱包地址进行代币交易或者转账时候,一般情况下只能通过向这些地址发送一些ETH作为手续费,然后进行转账操作,这样的过程相对而言繁琐,且会耗费一定的手续费。而ERC777的运营商功能允许某个地址充作其它地址的运营商,即多个地址的持有人可以将自己唯一一个有ETH 的地址作为其它地址的运营商,当其它地址需要转移代币时,可以直接由有ETH的地址代为操作,及由运营商地址控制,且手续费由运营商地址出,实际转移代币的地址无需支付手续费,这样的情况大大降低了重复多个钱包地址之间切换的冗余操作。

2.又如,某区块链公司的老板使用某种基于ERC777的A代币作为工资发放,公司账户一共有500万的A代币,而经过人事的计算,本月共应发总工资为200万代币A,那么公司老板就可以将财务的以太坊钱包地址作为运营商,并授权200万 A代币的使用权限,那么财务便有权限使用公司地址500万中的200万 A代币,并且对于公司来说,其余的金额是安全的。

operator包括两种类型:defaultOperators和operators,即合约初始默认添加的operator和自定义的operator,defaultOperators在合约构建时必须定义,而operators可以通过调用函数来授权。

二、Hooks

hooks是与ERC777的sender和receipient绑定的函数,发送和接收ERC777资产需分别调用IERC777Sender接口的tokensToSend的函数,以及IERC777Recipient接口的tokensReceived函数。而IERC777Sender和IERC777Recipient接口不需要发送方和接收方地址本身实现,而可以通过ERC1820注册表来调用实现了接口的合约。也就是说无论外部地址和合约地址,都可以通过ERC1820标准来注册想要调用的hooks函数。

三、接口

FUNCTIONSname()symbol()granularity()//代币的粒度,当设为1时和ERC20代币相同totalSupply()balanceOf(owner)send(recipient, amount, data)burn(amount, data)isOperatorFor(operator, tokenHolder)//判断operator地址是否是tokenHolder的operatorauthorizeOperator(operator)//授权operator地址为当前msg.sender的operatorrevokeOperator(operator)//取消operator授权defaultOperators()//输出defaultOperators的地址列表operatorSend(sender, recipient, amount, data, operatorData)//operator代理sender调用send函数,同时可以传入operator想要传入的数据operatorDataoperatorBurn(account, amount, data, operatorData)//operator代理account调用burn函数,同时可以传入operator想要传入的数据operatorData

四、实现

//继承了Context合约,context合约提供了_msgSender()和_msgData()函数,在metatransaction中,往往需要找到最初始的调用者地址,而不是代理的地址,所以可以通过_msgSender()实现而不是简单调用msg.sendercontract ERC777 is Context, IERC777, IERC20 {using Address for address;//IERC1820Registry合约地址IERC1820Registry internal constant _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);mapping(address => uint256) private _balances;uint256 private _totalSupply;string private _name;string private _symbol;//得到合约名的哈希,通过keccak256哈希来查询该接口bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender");bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");// This isn't ever read from - it's only used to respond to the defaultOperators query.address[] private _defaultOperatorsArray;// Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).mapping(address => bool) private _defaultOperators;// For each account, a mapping of its operators and revoked default operators.mapping(address => mapping(address => bool)) private _operators;mapping(address => mapping(address => bool)) private _revokedDefaultOperators;// ERC20-allowancesmapping(address => mapping(address => uint256)) private _allowances;/*** @dev `defaultOperators` may be an empty array.*/constructor(string memory name_,string memory symbol_,address[] memory defaultOperators_) {_name = name_;_symbol = symbol_;_defaultOperatorsArray = defaultOperators_;for (uint256 i = 0; i < defaultOperators_.length; i++) {_defaultOperators[defaultOperators_[i]] = true;}// register interfaces_ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));_ERC1820_REGISTRY.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this));}/*** @dev See {IERC777-name}.*/function name() public view virtual override returns (string memory) {return _name;}/*** @dev See {IERC777-symbol}.*/function symbol() public view virtual override returns (string memory) {return _symbol;}/*** @dev See {ERC20-decimals}.** Always returns 18, as per the* [ERC777 EIP](/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 override returns (uint256) {return 1;}/*** @dev See {IERC777-totalSupply}.*/function totalSupply() public view virtual override(IERC20, IERC777) returns (uint256) {return _totalSupply;}/*** @dev Returns the amount of tokens owned by an account (`tokenHolder`).*/function balanceOf(address tokenHolder) public view virtual override(IERC20, IERC777) returns (uint256) {return _balances[tokenHolder];}/*** @dev See {IERC777-send}.** Also emits a {IERC20-Transfer} event for ERC20 compatibility.*/function send(address recipient,uint256 amount,bytes memory data) public virtual override {_send(_msgSender(), recipient, amount, data, "", true);}/*** @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 override returns (bool) {_send(_msgSender(), recipient, amount, "", "", false);return true;}/*** @dev See {IERC777-burn}.** Also emits a {IERC20-Transfer} event for ERC20 compatibility.*/function burn(uint256 amount, bytes memory data) public virtual override {_burn(_msgSender(), amount, data, "");}/*** @dev See {IERC777-isOperatorFor}.*/function isOperatorFor(address operator, address tokenHolder) public view virtual override returns (bool) {returnoperator == tokenHolder ||(_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||_operators[tokenHolder][operator];}/*** @dev See {IERC777-authorizeOperator}.*/function authorizeOperator(address operator) public virtual override {require(_msgSender() != operator, "ERC777: authorizing self as operator");if (_defaultOperators[operator]) {delete _revokedDefaultOperators[_msgSender()][operator];} else {_operators[_msgSender()][operator] = true;}emit AuthorizedOperator(operator, _msgSender());}/*** @dev See {IERC777-revokeOperator}.*/function revokeOperator(address operator) public virtual override {require(operator != _msgSender(), "ERC777: revoking self as operator");if (_defaultOperators[operator]) {_revokedDefaultOperators[_msgSender()][operator] = true;} else {delete _operators[_msgSender()][operator];}emit RevokedOperator(operator, _msgSender());}/*** @dev See {IERC777-defaultOperators}.*/function defaultOperators() public view virtual override returns (address[] memory) {return _defaultOperatorsArray;}/*** @dev See {IERC777-operatorSend}.** Emits {Sent} and {IERC20-Transfer} events.*/function operatorSend(address sender,address recipient,uint256 amount,bytes memory data,bytes memory operatorData) public virtual override {require(isOperatorFor(_msgSender(), sender), "ERC777: caller is not an operator for holder");_send(sender, recipient, amount, data, operatorData, true);}/*** @dev See {IERC777-operatorBurn}.** Emits {Burned} and {IERC20-Transfer} events.*/function operatorBurn(address account,uint256 amount,bytes memory data,bytes memory operatorData) public virtual override {require(isOperatorFor(_msgSender(), account), "ERC777: caller is not an operator for holder");_burn(account, amount, data, operatorData);}/*** @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 override returns (uint256) {return _allowances[holder][spender];}/*** @dev See {IERC20-approve}.** NOTE: If `value` is the maximum `uint256`, the allowance is not updated on* `transferFrom`. This is semantically equivalent to an infinite approval.** Note that accounts cannot have allowance issued by their operators.*/function approve(address spender, uint256 value) public virtual override returns (bool) {address holder = _msgSender();_approve(holder, spender, value);return true;}/*** @dev See {IERC20-transferFrom}.** NOTE: Does not update the allowance if the current allowance* is the maximum `uint256`.** 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 override returns (bool) {address spender = _msgSender();_spendAllowance(holder, spender, amount);_send(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 the caller address as the `operator` and with* `userData` 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 {_mint(account, amount, userData, operatorData, true);}/*** @dev Creates `amount` tokens and assigns them to `account`, increasing* the total supply.** If `requireReceptionAck` is set to true, and 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,bool requireReceptionAck) internal virtual {require(account != address(0), "ERC777: mint to the zero address");address operator = _msgSender();_beforeTokenTransfer(operator, address(0), account, amount);// Update state variables_totalSupply += amount;_balances[account] += amount;_callTokensReceived(operator, address(0), account, amount, userData, operatorData, requireReceptionAck);emit Minted(operator, account, amount, userData, operatorData);emit Transfer(address(0), account, amount);}/*** @dev Send tokens* @param from address token holder address* @param to address recipient address* @param amount uint256 amount of tokens to transfer* @param userData bytes extra information provided by the token holder (if any)* @param operatorData bytes extra information provided by the operator (if any)* @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient*/function _send(address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData,bool requireReceptionAck) internal virtual {require(from != address(0), "ERC777: transfer from the zero address");require(to != address(0), "ERC777: transfer to the zero address");address operator = _msgSender();_callTokensToSend(operator, from, to, amount, userData, operatorData);_move(operator, from, to, amount, userData, operatorData);_callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);}/*** @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 = _msgSender();_callTokensToSend(operator, from, address(0), amount, data, operatorData);_beforeTokenTransfer(operator, from, address(0), amount);// Update state variablesuint256 fromBalance = _balances[from];require(fromBalance >= amount, "ERC777: burn amount exceeds balance");unchecked {_balances[from] = fromBalance - amount;}_totalSupply -= amount;emit Burned(operator, from, amount, data, operatorData);emit Transfer(from, address(0), amount);}function _move(address operator,address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData) private {_beforeTokenTransfer(operator, from, to, amount);uint256 fromBalance = _balances[from];require(fromBalance >= amount, "ERC777: transfer amount exceeds balance");//unchecked用来减少gas消耗,因为0.8.0版本以后的solidity默认检查over/underflow,//而如果能保证计算不会over/underflow或者已经添加过检查的逻辑,则可以使用unchecked关键字包裹跳过溢出checkunchecked {_balances[from] = fromBalance - amount;}_balances[to] += amount;emit Sent(operator, from, to, amount, userData, operatorData);emit Transfer(from, 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 virtual {require(holder != address(0), "ERC777: approve from the zero address");require(spender != address(0), "ERC777: approve to the zero address");_allowances[holder][spender] = value;emit Approval(holder, spender, value);}/*** @dev Call from.tokensToSend() if the interface is registered* @param operator address operator requesting the transfer* @param from address token holder address* @param to address recipient address* @param amount uint256 amount of tokens to transfer* @param userData bytes extra information provided by the token holder (if any)* @param operatorData bytes extra information provided by the operator (if any)*/function _callTokensToSend(address operator,address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData) private {address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(from, _TOKENS_SENDER_INTERFACE_HASH);if (implementer != address(0)) {IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);}}/*** @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but* tokensReceived() was not registered for the recipient* @param operator address operator requesting the transfer* @param from address token holder address* @param to address recipient address* @param amount uint256 amount of tokens to transfer* @param userData bytes extra information provided by the token holder (if any)* @param operatorData bytes extra information provided by the operator (if any)* @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient*/function _callTokensReceived(address operator,address from,address to,uint256 amount,bytes memory userData,bytes memory operatorData,bool requireReceptionAck) private {address implementer = _ERC1820_REGISTRY.getInterfaceImplementer(to, _TOKENS_RECIPIENT_INTERFACE_HASH);if (implementer != address(0)) {IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);} else if (requireReceptionAck) {require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");}}/*** @dev Updates `owner` s allowance for `spender` based on spent `amount`.** Does not update the allowance amount in case of infinite allowance.* Revert if not enough allowance is available.** Might emit an {IERC20-Approval} event.*/function _spendAllowance(address owner,address spender,uint256 amount) internal virtual {uint256 currentAllowance = allowance(owner, spender);if (currentAllowance != type(uint256).max) {require(currentAllowance >= amount, "ERC777: insufficient allowance");unchecked {_approve(owner, spender, currentAllowance - amount);}}}/*** @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 {}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。