Michael.W基于Foundry精读Openzeppelin第40期——ERC20Burnable.sol

  • Michael.W
  • 更新于 2023-12-07 16:09
  • 阅读 450

ERC20Burnable库是ERC20的拓展。该库允许用户销毁自己和给自己授权的人名下的token。

0. 版本

[openzeppelin]:v4.8.3,[forge-std]:v1.5.6

0.1 ERC20Burnable.sol

Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/token/ERC20/extensions/ERC20Burnable.sol

ERC20Burnable库是ERC20的拓展。该库允许用户销毁自己和给自己授权的人名下的token。

1. 目标合约

继承ERC20Burnable合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/token/ERC20/extensions/MockERC20Burnable.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol";

contract MockERC20Burnable is ERC20Burnable {
    constructor(string memory name, string memory symbol)
    ERC20(name, symbol) {}

    function mint(address account, uint amount) external {
        _mint(account, amount);
    }
}

全部foundry测试合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/token/ERC20/extensions/ERC20Burnable.t.sol

2. 代码精读

2.1 burn(uint256 amount)

调用者销毁自己名下数量为amount的token。

    function burn(uint256 amount) public virtual {
        // 调用ERC20._burn()进行销毁
        _burn(_msgSender(), amount);
    }

foundry代码验证:

contract ERC20BurnableTest is Test {
    MockERC20Burnable private _testing = new MockERC20Burnable("test name", "test symbol");
    address private user1 = address(1);

    function setUp() external {
        _testing.mint(user1, 100);
    }

    function test_Burn() external {
        vm.prank(user1);
        _testing.burn(1);
        assertEq(_testing.balanceOf(user1), 100 - 1);

        // revert if burn more than balance
        vm.expectRevert("ERC20: burn amount exceeds balance");
        _testing.burn(100);
    }
}

2.2 burnFrom(address account, uint256 amount)

调用者销毁account名下数量为amount的token。要求调用者必须拥有account地址足够的授权额度。

注:销毁过程同样会减少account给调用者的授权额度。如果授权额度为type(uint).max,那么该过程将不做授权额度的检查和更新.

    function burnFrom(address account, uint256 amount) public virtual {
        // 调用ERC20._spendAllowance()进行授权额度的检查和更新
        _spendAllowance(account, _msgSender(), amount);
        // 调用ERC20._burn()进行销毁
        _burn(account, amount);
    }

foundry代码验证:

contract ERC20BurnableTest is Test {
    MockERC20Burnable private _testing = new MockERC20Burnable("test name", "test symbol");
    address private user1 = address(1);
    address private user2 = address(2);

    function setUp() external {
        _testing.mint(user1, 100);
    }

    function test_BurnFrom() external {
        // revert without approve
        vm.prank(user2);
        vm.expectRevert("ERC20: insufficient allowance");
        _testing.burnFrom(user1, 1);

        // revert if burn more than allowance
        vm.prank(user1);
        _testing.approve(user2, 1);
        vm.prank(user2);
        vm.expectRevert("ERC20: insufficient allowance");
        _testing.burnFrom(user1, 2);

        // revert if burn more than balance
        vm.prank(user1);
        _testing.approve(user2, 100 + 1);
        vm.prank(user2);
        vm.expectRevert("ERC20: burn amount exceeds balance");
        _testing.burnFrom(user1, 100 + 1);

        // pass
        vm.prank(user2);
        _testing.burnFrom(user1, 10);
        assertEq(_testing.allowance(user1, user2), 101 - 10);
        assertEq(_testing.balanceOf(user1), 100 - 10);

        // allowance not changed if it was set to type(uint).max
        vm.prank(user1);
        _testing.approve(user2, type(uint).max);
        vm.prank(user2);
        _testing.burnFrom(user1, 10);
        assertEq(_testing.allowance(user1, user2), type(uint).max);
    }
}

ps: 本人热爱图灵,热爱中本聪,热爱V神。 以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。 同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下! 如果需要转发,麻烦注明作者。十分感谢!

1.jpeg

公众号名称:后现代泼痞浪漫主义奠基人

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Michael.W
Michael.W
0x0BED...8888
狂热的区块链爱好者