Skip to content

Commit

Permalink
feat: add stateless variant
Browse files Browse the repository at this point in the history
  • Loading branch information
sakulstra committed Aug 8, 2024
1 parent 5d728d5 commit 85a7370
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 7 deletions.
41 changes: 41 additions & 0 deletions src/contracts/access-control/StatelessOwnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
// From commit https://github.com/OpenZeppelin/openzeppelin-contracts/commit/8b778fa20d6d76340c5fac1ed66c80273f05b95a
// @dev modified version without storage that is intended to be anchored to a ACL
pragma solidity ^0.8.0;

import '../oz-common/Context.sol';

/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract StatelessOwnable is Context {
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}

/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address);

/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), 'Ownable: caller is not the owner');
}
}
29 changes: 29 additions & 0 deletions src/contracts/access-control/StatelessOwnableWithGuardian.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;

import {IStatelessOwnableWithGuardian} from './interfaces/IStatelessOwnableWithGuardian.sol';
import {StatelessOwnable} from './StatelessOwnable.sol';

abstract contract StatelessOwnableWithGuardian is StatelessOwnable, IStatelessOwnableWithGuardian {
modifier onlyGuardian() {
_checkGuardian();
_;
}

modifier onlyOwnerOrGuardian() {
_checkOwnerOrGuardian();
_;
}

/// @inheritdoc IStatelessOwnableWithGuardian
function guardian() public view virtual returns (address);

function _checkGuardian() internal view {
if (guardian() != _msgSender()) revert OnlyGuardianInvalidCaller(_msgSender());
}

function _checkOwnerOrGuardian() internal view {
if (_msgSender() != owner() && _msgSender() != guardian())
revert OnlyGuardianOrOwnerInvalidCaller(_msgSender());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {IWithGuardian} from './interfaces/IWithGuardian.sol';
* Forked version of https://github.com/bgd-labs/solidity-utils/blob/main/src/contracts/access-control/OwnableWithGuardian.sol
* Relying on UpgradableOwnable & moving the storage to 7201
*/
abstract contract UpgradableOwnableWithGuardian is OwnableUpgradeable, IWithGuardian {
abstract contract UpgradeableOwnableWithGuardian is OwnableUpgradeable, IWithGuardian {
/// @custom:storage-location erc7201:aave.storage.OwnableWithGuardian
struct OwnableWithGuardian {
address _guardian;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0;

interface IStatelessOwnableWithGuardian {
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OnlyGuardianInvalidCaller(address account);

/**
* @dev The caller account is not authorized to perform an operation.
*/
error OnlyGuardianOrOwnerInvalidCaller(address account);

/**
* @dev get guardian address;
*/
function guardian() external view returns (address);
}
64 changes: 64 additions & 0 deletions test/StatelessOwnableWithGuardian.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import 'forge-std/Test.sol';
import {IStatelessOwnableWithGuardian} from '../src/contracts/access-control/interfaces/IStatelessOwnableWithGuardian.sol';
import {StatelessOwnableWithGuardian} from '../src/contracts/access-control/StatelessOwnableWithGuardian.sol';

contract ImplStatlessOwnableWithGuardian is StatelessOwnableWithGuardian {
address private _owner;
address private _guardian;

constructor(address owner, address guardian) {
_owner = owner;
_guardian = guardian;
}

function owner() public view override returns (address) {
return _owner;
}

function guardian() public view override returns (address) {
return _guardian;
}

function mock_onlyGuardian() external onlyGuardian {}

function mock_onlyOwnerOrGuardian() external onlyOwnerOrGuardian {}
}

contract TestOfUpgradableOwnableWithGuardian is Test {
ImplStatlessOwnableWithGuardian public withGuardian;

address owner = address(0x4);
address guardian = address(0x8);

function setUp() public {
withGuardian = new ImplStatlessOwnableWithGuardian(owner, guardian);
}

function test_getters() external {
assertEq(withGuardian.owner(), owner);
assertEq(withGuardian.guardian(), guardian);
}

function test_onlyGuardian() external {
vm.expectRevert(
abi.encodeWithSelector(
IStatelessOwnableWithGuardian.OnlyGuardianInvalidCaller.selector,
address(this)
)
);
withGuardian.mock_onlyGuardian();
}

function test_onlyOwnerOrGuardian() external {
vm.expectRevert(
abi.encodeWithSelector(
IStatelessOwnableWithGuardian.OnlyGuardianOrOwnerInvalidCaller.selector,
address(this)
)
);
withGuardian.mock_onlyOwnerOrGuardian();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
pragma solidity ^0.8.20;

import 'forge-std/Test.sol';
import {UpgradableOwnableWithGuardian} from '../src/contracts/access-control/UpgradableOwnableWithGuardian.sol';
import {UpgradeableOwnableWithGuardian} from '../src/contracts/access-control/UpgradeableOwnableWithGuardian.sol';

contract ImplOwnableWithGuardian is UpgradableOwnableWithGuardian {
contract ImplOwnableWithGuardian is UpgradeableOwnableWithGuardian {
function initialize(address owner, address guardian) public initializer {
__Ownable_init(owner);
__Ownable_With_Guardian_init(guardian);
Expand All @@ -16,7 +16,7 @@ contract ImplOwnableWithGuardian is UpgradableOwnableWithGuardian {
}

contract TestOfUpgradableOwnableWithGuardian is Test {
UpgradableOwnableWithGuardian public withGuardian;
UpgradeableOwnableWithGuardian public withGuardian;

address owner = address(0x4);
address guardian = address(0x8);
Expand All @@ -34,7 +34,7 @@ contract TestOfUpgradableOwnableWithGuardian is Test {
function test_onlyGuardian() external {
vm.expectRevert(
abi.encodeWithSelector(
UpgradableOwnableWithGuardian.OnlyGuardianInvalidCaller.selector,
UpgradeableOwnableWithGuardian.OnlyGuardianInvalidCaller.selector,
address(this)
)
);
Expand All @@ -44,7 +44,7 @@ contract TestOfUpgradableOwnableWithGuardian is Test {
function test_onlyOwnerOrGuardian() external {
vm.expectRevert(
abi.encodeWithSelector(
UpgradableOwnableWithGuardian.OnlyGuardianOrOwnerInvalidCaller.selector,
UpgradeableOwnableWithGuardian.OnlyGuardianOrOwnerInvalidCaller.selector,
address(this)
)
);
Expand All @@ -67,7 +67,7 @@ contract TestOfUpgradableOwnableWithGuardian is Test {
vm.prank(eoa);
vm.expectRevert(
abi.encodeWithSelector(
UpgradableOwnableWithGuardian.OnlyGuardianOrOwnerInvalidCaller.selector,
UpgradeableOwnableWithGuardian.OnlyGuardianOrOwnerInvalidCaller.selector,
eoa
)
);
Expand Down

0 comments on commit 85a7370

Please sign in to comment.