区块链学习第十期

myBlog

Contracts解读

[toc] ---

声明:此篇参照 openzepplin 的 Contracts,参杂了部分自己的理解 别说我抄袭球球了

EIP

EIP 代表“以太坊改进提案”,是为了更新以太坊网络而提出的标准。
人们提出来了很多 EIP 根据提交的顺序来定,比如 erc20 就是提交的并被采纳的第 20 个

以下是一些重要的 EIP:

  • EIP-1559:引入基于市场的费用模型,以更好地管理区块链交易费用。
  • EIP-721:提供非同质化代币(NFT)标准,使得在以太坊上创造和交易独特的数字资产成为可能。
  • EIP-1474:引入一个新的智能合约标准,该标准将通过注册表和接口使以太坊网络中的智能合约更易于发现和使用。
  • EIP-1014:引入状态通道,允许用户进行高速、低费用的交易。
  • EIP-155:引入链 ID,增加了以太坊网络与其他网络之间的互操作性。

IERC 和 ERC

  • IERC 是代币合约的接口。
  • ERC 是代币合约的实现。

    接口就像蓝图,以确保类(在我们的例子中是合约)实现具有正确的方法,具有正确的方法可见性,参数和返回值。

IERC 文件上的方法:

1
function balanceOf(address account) external view returns (uint256);

它不保存代码,但有一些有价值的信息:

  • 它有名字.balanceOf
  • 它需要一个类型和名称的参数。addressaccount
  • 它是一种基于的“只读”方法。view
  • 它返回一个值。uint256
  • 它没有代码。

ERC 文件上的相同方法:

1
2
3
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}

它有代码。

请注意,ERC20 文件导入 IERC20 文件,并在第 33 行定义该文件:。换句话说:“ERC20 应该实现接口 IERC20 的方法”。ERC20 is a IERC20


ERC20 导读

什么是 ERC20

ERC20 是以太坊上的代币标准,大多数基于以太坊的代币都使用 ERC20 标准。它规定了代币所需遵循的基本功能和接口,包括转账、余额查询等,为代币合约的开发提供了方便和统一性。因此,在 DeFi 热潮中,许多基于 ERC20 代币的项目也表现得非常火爆。

有什么特点

预设 ERC20 合约

预设 ERC20 合约可以通过 ERC20PresetMinterPauser 来实现。它可以预先设置,允许进行挖矿(create),停止所有的转账操作(pause),允许用户销毁 token(destroy)。合约使用访问控制来限定挖矿和暂停合约的函数操作。合约的部署账号默认拥有挖矿和暂停的角色,也就是默认的管理员角色。
这个合约可以不编写任何 Solidity 代码直接部署。它可以用来做原型搭建和测试,同时在生产环境中也可以正常使用

生成 ERC20 供应

固定供应量

如果需要供应 1000 个 token,在部署合约时将这些 token 分配给部署者。使用 v1 版本合约,可以编写以下代码:

1
2
3
4
5
6
7
solidity
contract ERC20FixedSupply is ERC20 {
constructor() {
totalSupply += 1000;
balances[msg.sender] += 1000;
}
}

但是从 v2 版本开始,上述的写法不仅不鼓励,而且是禁止的。totalSupply 和 balances 会作为 ERC20 实现中的私有元素,不能直接对他们进行写入操作,只能通过_mint 函数来完成:

1
2
3
4
5
6
solidity
contract ERC20FixedSupply is ERC20 {
constructor() ERC20("Fixed", "FIX") {
\_mint(msg.sender, 1000);
}
}

继承合约时,这样包装状态会让它变得更加安全。例如,在第一个示例中,必须手动保持 totalSupply 与修改后的余额同步,这很容易忘记。事实上,我们还忽略了一些东西,它也很容易忘记:那就是 Transfer 事件,这也是在标准中要求的,并且客户端也会依赖这个事件。第二个例子中解决了这个问题,因为它是通过调用_mint 函数来完成的。

奖励矿工

内部函数 _mint 是构建区块的关键,它可以让我们扩展 ERC20 的供应机制。
接下来是一个奖励挖矿者的奖励机制。在 Solidity 中我们可以通过全局变量** block.coinbase **获取到当前区块矿工的地址。有人调用 mintMinerReward()函数时,我们会奖励 token 给这个区块的矿工。

1
2
3
4
5
6
7
8
9
solidity
contract ERC20WithMinerReward is ERC20 {
constructor() ERC20("Reward", "RWD") {}

function mintMinerReward() public {
_mint(block.coinbase, 1000);
}

}

_mint 函数让这变得非常容易。

将机制模块化

合约中已经包含了一种供应机制:ERC20PresetMinterPauser。这是一种通用机制,其中一组帐户被分配了 minter 角色,授予他们调用 mint 函数的权限,即 _mint 的外部版本。
这可以用于中心化挖矿,外部拥有的帐户(即拥有一对加密密钥的人)决定创建多少供应以及为谁创建。这是一种非常合理的供应机制,比如传统资产背书的稳定币。
这个有铸币权限的账户,也可以不是一个外部账户,它可以是一个实现了去信任机制的智能合约。我们可以像之前一样实现这个功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
solidity
contract MinerRewardMinter {
ERC20PresetMinterPauser \_token;

constructor(ERC20PresetMinterPauser token) {
_token = token;
}

function mintMinerReward() public {
_token.mint(block.coinbase, 1000);
}

}

这个合约是通过 ERC20PresetMinterPauser 实例来初始化,然后把 minter 角色授权给这个合约,这样做也可以实现和之前例子一样的功能。这里比较有趣的是,我们使用了 ERC20PresetMinterPauser,我们可以通过将角色分配给多个合同来轻松组合多种供应机制,并且是动态的来实现。

自动化奖励机制

目前为止我们讲诉的都是手工编写奖励机制,ERC20 也允许我们通过_beforeTokenTransfer 这个 hook 来扩展 token 的核心函数。
在之前的例子中加入这个功能,我们可以用这个 hook 为区块链中包含的每个代币转移铸造矿工奖励。

solidity
contract ERC20WithAutoMinerReward is ERC20 {
constructor() ERC20(“Reward”, “RWD”) {}

function _mintMinerReward() internal {
    _mint(block.coinbase, 1000);
}

function _beforeTokenTransfer(address from, address to, uint256 value) internal virtual override {
    if (!(from == address(0) && to == block.coinbase)) {
    _mintMinerReward();
    }
    super._beforeTokenTransfer(from, to, value);
}

}

有哪些方法/事件?怎么用?

以下部分方法会类比微信的亲属卡功能

方法

  1. totalSupply()

    返回总 token 数量

  2. balanceOf(account)

    返回此 account 的 token 余额

  3. transfer(to, amount)

    当前调用者给 to 打 amount 数量的 token

  4. approve(spender, amount)

    当前调用者允许 spender 花的钱(钱在调用者身上)
    approve

  5. allowance(owner, spender)

    查询 owner approve 给 spender 了多少 token

  6. transferFrom(from, to, amount)

    从 from 给 to 转账 amount

  7. increaseAllowance(spender, addedValue)

    给 spender 增加 token

  8. decreaseAllowance(spender, subtractedValue)

    给 spender 减少 token

  9. _transfer(from, to, amount)

    (内部的)该内部函数等同于 transfer,可用于实现自动代币费用、罚没机制等。
    触发一个 transfer 事件。

  10. _mint(account, amount)

    在 account 中生成 amount 数量的 token,一般用于初始化

  11. _burn(account, amount)

    销毁 amount 的代币 account,减少总供应量。
    触发一个设置 to 为零地址的 transfer 事件

  12. _approve(owner, spender, amount)

  13. _spendAllowance(owner, spender, amount)

  14. _beforeTokenTransfer(from, to, amount)

  15. _afterTokenTransfer(from, to, amount)

事件

  1. Transfer(from, to, value)

  2. Approval(owner, spender, value)

元数据

  1. name()

    返回 token 的名字==》人民币

  2. symbol()

    返回 token 的符号==》¥

  3. decimals()

    当前代币支持的最小精度,也就是小数点后多少位
    因为转账要求是整数,有了精度之后就可以把 token 分割为非整数
    比如我发行一个币,精度为 2,查询到的结果就是 100,就这样实现了分割