Openzeppelin之ERC777源码解析(下)

102 阅读3分钟

上一篇文章介绍了ERC777的规范以及一些基本函数,这篇会继续上篇讲解其他与ERC20不同的函数

一、重要函数

  • isOperatorFor用于判断指定地址是否是操作者(可以控制使用代币持有者的代币)。

函数提供了三种判断方法:

1)该操作者是代币持有者(tokenHolder确实可以使用自己的代币,也算是操作者)

2)该操作者地址是默认操作者并且不是被移除的默认操作者之一(_defaultOperators)

3)该操作者是指定操作者之一(_operators

    function isOperatorFor(address operator, address tokenHolder) public view virtual override returns (bool) {
        return
            operator == tokenHolder ||
            (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
            _operators[tokenHolder][operator];
    }
  • authorizeOperator 用于授权某用户为操作者

函数逻辑:

1)先判断该地址是否是msg.sender

2) 如果该地址是默认操作者,检查撤销操作者队列,如果该地址存在,则删除队列中的地址,不存在,则不用。

3)如果该地址不是默认操作者,则授权为指定操作者。

    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());
    }
  • revokedOperator用于撤销操作者权限

函数逻辑:

1)先判断被撤销操作者的地址是否是msg.sender

2) 如果是默认操作者,则使用_revokedDefaultOperators撤销该操作者

3)如果不是默认操作者,则从_operators撤销该操作者

    function revokeOperator(address operator) public virtual override {
        require(operator != _msgSender(), "ERC777: revoking self as operator");

        if (_defaultOperators[operator]) {//如果operator是默认操作者
            _revokedDefaultOperators[_msgSender()][operator] = true;//撤销该操作者
        } else {//不是默认操作者,而是指定操作者
            delete _operators[_msgSender()][operator];//撤销该指定操作者
        }

        emit RevokedOperator(operator, _msgSender());
    }
  • allowance是和ERC20的同名函数一样,允许某个用户可以使用自己一定数量的代币

被允许的spender不一定是操作者,操作者不一定是spender

    function allowance(address holder, address spender) public view virtual override returns (uint256) {
        return _allowances[holder][spender];
    }
  • approve和ERC20的同名函数一样,允许操作者使用自己多少数量的代币。

注意:不能让操作者来授权

    function approve(address spender, uint256 value) public virtual override returns (bool) {
        address holder = _msgSender();
        _approve(holder, spender, value);
        return true;
    }
  • send()用于代币转账交易,send是内部函数,用于transferFrom调用

  • move()实现了转账功能

函数逻辑:

1)判断msg.sender的余额是否大于要转账的数量

2)msg.sender的余额减去转账的数量

3)给接受者地址加转账的数量

    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);
    }
    
   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 {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Sent(operator, from, to, amount, userData, operatorData);
        emit Transfer(from, to, amount);
    }

  • _callTokensToSend()用于判断发送者地址是否为0地址,可以防止重入攻击
  • _callTokensReceived()用于判断接收者地址是否为0地址,如果是0地址,则转账终止

这两个函数是ERC20和ERC777的一个重要区别

    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);
        }
    }
  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");
        }
    }