Michael.W基于Foundry精读Openzeppelin第36期——Ownable2Step.sol

  • Michael.W
  • 更新于 2023-09-21 20:16
  • 阅读 1763

Ownable2Step库是Ownable库的拓展版本。其提供的访问权限机制同Ownable完全一致,只是owner的更换机制从由原owner直接指定(一次交互)变成原owner指定 + 新owner确认(两次交互)。Ownable2Step库具有Ownable库所有的功能函数。

0. 版本

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

0.1 Ownable2Step.sol

Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/access/Ownable2Step.sol

Ownable2Step库是Ownable库的拓展版本。其提供的访问权限机制同Ownable完全一致,只是owner的更换机制从由原owner直接指定(一次交互)变成原owner指定 + 新owner确认(两次交互)。Ownable2Step库具有Ownable库所有的功能函数。

1. 目标合约

继承Ownable2Step成为一个可调用合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/access/MockOwnable2Step.sol

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

import "openzeppelin-contracts/contracts/access/Ownable2Step.sol";

contract MockOwnable2Step is Ownable2Step {
    uint public i;

    function transferOwnershipInternal(address newOwner) external {
        _transferOwnership(newOwner);
    }

    function setI(uint value) external onlyOwner {
        i = value;
    }
}

全部foundry测试合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/access/Ownable2Step.t.sol

2. 代码精读

2.1 pendingOwner() && transferOwnership(address newOwner) && _transferOwnership(address newOwner)

  • pendingOwner():返回当前合约处于待确认状态的owner地址;
  • transferOwnership(address newOwner):合约当前owner指定新的owner(步骤一),该操作会覆盖之前设置的_pendingOwner。注: 该函数重写了Ownerable库的transferOwnership函数;
  • _transferOwnership(address newOwner):转移本合约owner身份给一个新的地址。注:该函数重写了Ownerable库的_transferOwnership函数且不具有对访问权限的限制;
    // 处于待确认状态的owner地址
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    function pendingOwner() public view virtual returns (address) {
        // 返回状态变量_pendingOwner
        return _pendingOwner;
    }

    function transferOwnership(address newOwner) public virtual override onlyOwner {
        // 新owner地址处于待确认状态,写入状态变量_pendingOwner
        _pendingOwner = newOwner;
        // 抛出event
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    function _transferOwnership(address newOwner) internal virtual override {
        // 清空状态变量_pendingOwner
        delete _pendingOwner;
        // 调用Ownerable.transferOwnership()进行owner身份的转移
        super._transferOwnership(newOwner);
    }

foundry代码验证

contract Ownable2StepTest is Test {
    MockOwnable2Step private _testing;

    function setUp() external {
        _testing = new MockOwnable2Step();
    }

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    function test_TransferOwnership() external {
        // test for public function: transferOwnership
        assertEq(address(0), _testing.pendingOwner());
        // case 1: pass
        vm.expectEmit(true, true, false, false, address(_testing));
        emit OwnershipTransferStarted(address(this), address(1024));
        _testing.transferOwnership(address(1024));
        assertEq(address(1024), _testing.pendingOwner());

        // test for internal function: _transferOwnership
        // case 1: pass with clearing pending owner and new owner transferred
        vm.expectEmit(true, true, false, false, address(_testing));
        emit OwnershipTransferred(address(this), address(2048));
        _testing.transferOwnershipInternal(address(2048));
        // new owner transferred
        assertEq(address(2048), _testing.owner());
        // clear the pending owner
        assertEq(address(0), _testing.pendingOwner());
    }
}

2.2 acceptOwnership()

新owner确认owner身份的转移(步骤二)。

    function acceptOwnership() external {
        address sender = _msgSender();
        // 确认本次调用者为处于待确认状态的owner地址,否则revert
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        // 将owner身份转移给本次调用者(即处于待确认状态的owner地址)
        _transferOwnership(sender);
    }

foundry代码验证

contract Ownable2StepTest is Test {
    MockOwnable2Step private _testing;

    function setUp() external {
        _testing = new MockOwnable2Step();
    }

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    function test_AcceptOwnership() external {
        // case 1: pass
        _testing.transferOwnership(address(1024));
        assertEq(address(1024), _testing.pendingOwner());
        assertEq(address(this), _testing.owner());
        vm.prank(address(1024));
        vm.expectEmit(true, true, false, false, address(_testing));
        emit OwnershipTransferred(address(this), address(1024));
        _testing.acceptOwnership();
        // clear the pending owner
        assertEq(address(0), _testing.pendingOwner());
        // new owner transferred
        assertEq(address(1024), _testing.owner());

        // case 2: revert if _msgSender() is not pending owner
        // current pending owner is address(0)
        vm.expectRevert("Ownable2Step: caller is not the new owner");
        vm.prank(address(1));
        _testing.acceptOwnership();
    }

    function test_OnlyOwner() external {
        assertEq(address(this), _testing.owner());

        // test for modifier: onlyOwner
        // case 1: pass the check of modifier
        assertEq(0, _testing.i());
        _testing.setI(1024);
        assertEq(1024, _testing.i());
        // case 2: revert if the msgSender() is not owner
        vm.prank(address(1024));
        vm.expectRevert("Ownable: caller is not the owner");
        _testing.setI(1024);
    }
}

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

1.jpeg

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

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

0 条评论

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