Ownable库提供了一种基本的访问控制机制——设置一个owner具有对某些函数特殊的访问权限。通常owner就是本合约的deployer。合约部署后可通过函数transferOwnership()进行owner的修改。本库还提供了modifier onlyOwner,用于为函数限定访问权限。
[openzeppelin]:v4.8.3,[forge-std]:v1.5.6
Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/access/Ownable.sol
Ownable库提供了一种基本的访问控制机制——设置一个owner具有对某些函数特殊的访问权限。通常owner就是本合约的deployer。合约部署后可通过函数transferOwnership()进行owner的修改。本库还提供了modifier onlyOwner,用于为函数限定访问权限。
继承Ownable成为一个可调用合约:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "openzeppelin-contracts/contracts/access/Ownable.sol";
contract MockOwnable is Ownable {
uint public i;
function checkOwner() external view {
_checkOwner();
}
function transferOwnershipInternal(address newOwner) external {
_transferOwnership(newOwner);
}
function setI(uint value) external onlyOwner {
i = value;
}
}
全部foundry测试合约:
_checkOwner()
:检查当前_msgSender()是否为合约的owner;owner()
:返回当前合约的owner地址;onlyOwner()
:用于对外函数访问权限限制的modifier。访问权限:只有当前合约的owner才具有访问权限。 // 当前合约的owner地址
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
// 合约部署时,默认将deployer设置为本合约的owner
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
// 检查当前_msgSender()是否为合约owner,如果不是直接revert
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
// 直接返回状态变量_owner
return _owner;
}
function _checkOwner() internal view virtual {
// 检查当前_msgSender()是否为合约的owner,如果不是直接revert
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
foundry代码验证
contract OwnableTest is Test {
MockOwnable private _testing;
function setUp() external {
_testing = new MockOwnable();
}
function test_CheckOwnerAndOnlyOwner() external {
assertEq(address(this), _testing.owner());
// test for internal function: _checkOwner
// case 1: pass
_testing.checkOwner();
// case 2: revert if the msgSender() is not owner
vm.prank(address(1024));
vm.expectRevert("Ownable: caller is not the owner");
_testing.checkOwner();
// 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);
}
}
_transferOwnership(address newOwner)
:转移本合约owner身份给一个新的地址。注:该internal函数没有对访问权限的限制;transferOwnership(address newOwner)
:当前合约owner将owner身份转移给新地址newOwner。 function transferOwnership(address newOwner) public virtual onlyOwner {
// modifier onlyOwner对_msgSender()进行检查(必须为当前合约owner)
// 检查新owner地址不可为0地址,否则revert
require(newOwner != address(0), "Ownable: new owner is the zero address");
// 调用_transferOwnership()将newOwner设置为合约的owner
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
// 合约当前owner地址存储到局部变量oldOwner中
address oldOwner = _owner;
// 将_owner状态变量设置为新的owner地址
_owner = newOwner;
// 抛出event
emit OwnershipTransferred(oldOwner, newOwner);
}
foundry代码验证
contract OwnableTest is Test {
MockOwnable private _testing;
function setUp() external {
_testing = new MockOwnable();
}
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function test_TransferOwnership() external {
// test for public function: transferOwnership
assertEq(address(this), _testing.owner());
// case 1: pass
vm.expectEmit(true, true, false, false, address(_testing));
emit OwnershipTransferred(address(this), address(1024));
_testing.transferOwnership(address(1024));
assertEq(address(1024), _testing.owner());
// case 2: revert if new owner is 0
vm.prank(address(1024));
vm.expectRevert("Ownable: new owner is the zero address");
_testing.transferOwnership(address(0));
// test for internal function: _transferOwnership
// case 1: pass with any address of new owner
vm.expectEmit(true, true, false, false, address(_testing));
emit OwnershipTransferred(address(1024), address(2048));
_testing.transferOwnershipInternal(address(2048));
assertEq(address(2048), _testing.owner());
vm.expectEmit(true, true, false, false, address(_testing));
emit OwnershipTransferred(address(2048), address(0));
_testing.transferOwnershipInternal(address(0));
assertEq(address(0), _testing.owner());
}
}
当前合约owner在不设置新owner的条件下主动放弃owner身份。
注:当执行该函数后,合约内所有被onlyOwner修饰的函数都将无法被任何人调用。
function renounceOwnership() public virtual onlyOwner {
// modifier onlyOwner对_msgSender()进行检查(必须为当前合约owner)
// 将合约owner身份转移给零地址
_transferOwnership(address(0));
}
foundry代码验证
contract OwnableTest is Test {
MockOwnable private _testing;
function setUp() external {
_testing = new MockOwnable();
}
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function test_RenounceOwnership() external {
assertEq(address(this), _testing.owner());
vm.expectEmit(true, true, false, false, address(_testing));
emit OwnershipTransferred(address(this), address(0));
_testing.renounceOwnership();
assertEq(address(0), _testing.owner());
}
}
ps: 本人热爱图灵,热爱中本聪,热爱V神。 以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。 同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下! 如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!