文章讨论了如何在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;
}
}
练习题
请查看 区块链培训营,了解更多关于智能合约开发和代币标准的内容。
- 原文链接: rareskills.io/learn-soli...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!