0%

erc20 | 可销毁代币

参考


参考资料



可销毁代币


首先,这里认为已经创建好了 truffle solidity 项目。

npm install @openzeppelin/contracts@3.4.0
npm install @truffle/hdwallet-provider // 部署脚本

整个项目如下

--contracts
----ERC20WithBurnable.sol
--migrations
----2_deploy_ERC20WithBurnable.js
--.secret
--truffle-config.js

ERC20WithBurnable.sol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pragma solidity >=0.4.21 <0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";


//可销毁代币
contract ERC20WithBurnable is ERC20, ERC20Burnable {
constructor(
string memory name, //代币名称
string memory symbol, //代币缩写
uint256 totalSupply //发行总量
) public ERC20(name, symbol) {
_mint(msg.sender, totalSupply * (10 ** decimals()));
}
}

细节

我们先看 ERC20 的内容

1
2
3
4
5
6
7
8
9
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");

_beforeTokenTransfer(account, address(0), amount);

_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}

我们可以看到里面已经有一个 burn 燃烧方法,那为什么还需要继承 ERC20Burnable

这是因为,ERC20_burn 的修饰是 internal ,为内部调用,外部调用不了,所以,需要继承 ERC20Burnable ,因为,该合约进行了调用该方法,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pragma solidity >=0.6.0 <0.8.0;

import "../../utils/Context.sol";
import "./ERC20.sol";

abstract contract ERC20Burnable is Context, ERC20 {
using SafeMath for uint256;


function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}

function burnFrom(address account, uint256 amount) public virtual {
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");

_approve(account, _msgSender(), decreasedAllowance);
_burn(account, amount);
}
}

可以发现,该 ERC20Burnable 调用了 ERC20_burn 方法,并进行了 public 修饰。

让我们再来关注一下, _burn 的实现。

1
2
3
4
5
6
7
8
9
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");

_beforeTokenTransfer(account, address(0), amount);

_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
  • 首先判断账户不是 0x0000000 账户
  • _beforeTokenTransfer 是一个未实现方法,后面可以继承合约,实现这个方法,然后,在销毁前做一些操作
  • 接着从自身账户减去相应数量的代币
  • 总量中也减去该数量的代币
  • 发出转移给 0x00000 的事件

事实上,这些币是真的销毁了,而没有转给 0x00000 账户。

我们接着看另一个销毁方法 burnFrom

1
2
3
4
5
6
function burnFrom(address account, uint256 amount) public virtual {
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");

_approve(account, _msgSender(), decreasedAllowance);
_burn(account, amount);
}

这个可以参考一下

transferForm

互动

我们把合约的内容改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pragma solidity >=0.4.21 <0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";


//可销毁代币
contract ERC20WithBurnable is ERC20, ERC20Burnable {

string public burnMsg;

constructor(
string memory name, //代币名称
string memory symbol, //代币缩写
uint256 totalSupply //发行总量
) public ERC20(name, symbol) {
_mint(msg.sender, totalSupply * (10 ** decimals()));
}

function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
burnMsg = "second";
}
}

然后燃烧的时候,就能够调用 _beforeTokenTransfer 方法了。

请我喝杯咖啡吧~