Solidity 触发事件

文章讨论了如何在Solidity智能合约中实现ERC20标准的事件记录,强调了事件记录在状态变化时的重要性,并通过代码示例详细展示了如何在ERC20合约中添加Transfer和Approval事件。

从技术上讲,我们的 "ERC20" 代币并不完全符合 ERC20 标准。它缺少一个重要功能:事件

一般经验法则:如果一个函数导致状态变化,它应该被记录。


为什么要记录事件?区块链不是已经不可变地存储了每一笔交易吗?

这是事实,事件并不是严格必需的。然而,它们使得审计过去的事件变得更加容易。相比浏览一堆交易,用户可以按他们关心的日志进行过滤,并快速找到可能感兴趣的事件(交易)。

这就是你的加密货币钱包能够快速发现你的 ERC20 余额的方式。如果必须查看 ERC20 代币上发生的每笔交易来发现你是否拥有任何代币,那将是非常烦人的。但日志的存储方式使得这种检索变得高效。

事件不能被其他智能合约看到。它们是为链下查询而优化的。

让我们来看一个例子。


contract ExampleContract {

    event Deposit(address indexed depositor, uint256 amount);

    receive()
        external
        payable {
            emit Deposit(msg.sender, msg.value);
    }
}

一个事件最多可以有 3 个索引类型,但对未索引参数的数量没有严格的限制。

如果你有数据库背景,你可以将“索引”视为与数据库索引完全相同的方式。

顺便说一下,数据类型后的参数名称是可选的。我们可以将上述事件写成:


event Deposit(address indexed, uint256);

除了可能稍微降低可读性外,不会有任何不良影响。

什么时候应该对变量建立索引?如果你可能希望快速查找该值,比如“某个地址是否与此代币合约有关联”,那么你应该对其进行索引。你可能对“是否有人在此合约中正好转账了 1,370,904 个代币?”这样的问题不感兴趣,所以不要对金额建立索引。这是我们添加了事件的 ERC20 代币。请注意,这些事件是规范所要求的。

特别注意事件添加的位置,尤其是 mint 函数!address(0) 作为来源的约定意味着代币是从无到有产生的,而不是从另一个地址转移而来。推荐阅读:https://learnblockchain.cn/article/11239


contract ERC20 {
    string public name;
    string public symbol;

    mapping(address => uint256) public balanceOf;
    address public owner;
    uint8 public decimals;

    uint256 public totalSupply;

    // owner -> spender -> allowance
    // 这使所有者可以向多个地址授权
    mapping(address => mapping(address => uint256)) public allowance;

    event Transfer(
        address indexed _from,
        address indexed _to,
        uint256 _value
    );
    event Approval(
        address indexed _owner,
        address indexed _spender,
        uint256 _value
    );

    constructor(
        string memory _name,
        string memory _symbol
    ) {
        name = _name;
        symbol = _symbol;
        decimals = 18;

        owner = msg.sender;
    }

    function mint(
        address to,
        uint256 amount
    )
        public {
            require(msg.sender == owner, "only owner can create tokens");
            totalSupply += amount;
            balanceOf[owner] += amount;

            emit Transfer(address(0), owner, amount);
    }

    function transfer(
        address to,
        uint256 amount
    )
        public
        returns (bool) {
            return helperTransfer(msg.sender, to, amount);
    }

    function approve(
        address spender,
        uint256 amount
    )
        public
        returns (bool) {
            allowance[msg.sender][spender] = amount;
            emit Approval(msg.sender, spender, amount);

            return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    )
        public
        returns (bool) {
            if (msg.sender != from) {
                require(
                    allowance[from][msg.sender] >= amount,
                    "not enough allowance"
                );

                allowance[from][msg.sender] -= amount;
            }

            return helperTransfer(from, to, amount);
    }

    function helperTransfer(
        address from,
        address to,
        uint256 amount
    )
        internal
        returns (bool) {
            require(balanceOf[from] >= amount, "not enough money");
            require(to != address(0), "cannot send to address(0)");
            balanceOf[from] -= amount;
            balanceOf[to] += amount;

            emit Transfer(from, to, amount);
            return true;
    }
}

练习题

Emitter

了解更多

请查看 区块链培训营,了解更多关于智能合约开发和代币标准的内容。

  • 原文链接: rareskills.io/learn-soli...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/