-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add ownable-with-guardian (#35)
- Loading branch information
Showing
6 changed files
with
197 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
[submodule "lib/forge-std"] | ||
path = lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std | ||
[submodule "lib/openzeppelin-contracts-upgradeable"] | ||
path = lib/openzeppelin-contracts-upgradeable | ||
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule forge-std
updated
42 files
Submodule openzeppelin-contracts-upgradeable
added at
723f8c
83 changes: 83 additions & 0 deletions
83
src/contracts/access-control/UpgradableOwnableWithGuardian.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import {OwnableUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol'; | ||
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 { | ||
/// @custom:storage-location erc7201:aave.storage.OwnableWithGuardian | ||
struct OwnableWithGuardian { | ||
address _guardian; | ||
} | ||
|
||
// keccak256(abi.encode(uint256(keccak256("aave.storage.OwnableWithGuardian")) - 1)) & ~bytes32(uint256(0xff)) | ||
bytes32 private constant OwnableWithGuardianStorageLocation = | ||
0xdc8016945fab92f4608d8f23802ef36d865b35bd839402e24dec05cd76049e00; | ||
|
||
function _getOwnableWithGuardianStorage() private pure returns (OwnableWithGuardian storage $) { | ||
assembly { | ||
$.slot := OwnableWithGuardianStorageLocation | ||
} | ||
} | ||
|
||
/** | ||
* @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 Initializes the contract setting the address provided by the deployer as the initial owner. | ||
*/ | ||
function __Ownable_With_Guardian_init(address initialGuardian) internal onlyInitializing { | ||
_updateGuardian(initialGuardian); | ||
} | ||
|
||
modifier onlyGuardian() { | ||
_checkGuardian(); | ||
_; | ||
} | ||
|
||
modifier onlyOwnerOrGuardian() { | ||
_checkOwnerOrGuardian(); | ||
_; | ||
} | ||
|
||
function guardian() public view override returns (address) { | ||
OwnableWithGuardian storage $ = _getOwnableWithGuardianStorage(); | ||
return $._guardian; | ||
} | ||
|
||
/// @inheritdoc IWithGuardian | ||
function updateGuardian(address newGuardian) external override onlyOwnerOrGuardian { | ||
_updateGuardian(newGuardian); | ||
} | ||
|
||
/** | ||
* @dev method to update the guardian | ||
* @param newGuardian the new guardian address | ||
*/ | ||
function _updateGuardian(address newGuardian) internal { | ||
OwnableWithGuardian storage $ = _getOwnableWithGuardianStorage(); | ||
address oldGuardian = $._guardian; | ||
$._guardian = newGuardian; | ||
emit GuardianUpdated(oldGuardian, newGuardian); | ||
} | ||
|
||
function _checkGuardian() internal view { | ||
if (guardian() != _msgSender()) revert OnlyGuardianInvalidCaller(_msgSender()); | ||
} | ||
|
||
function _checkOwnerOrGuardian() internal view { | ||
if (_msgSender() != owner() && _msgSender() != guardian()) | ||
revert OnlyGuardianOrOwnerInvalidCaller(_msgSender()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import 'forge-std/Test.sol'; | ||
import {UpgradableOwnableWithGuardian} from '../src/contracts/access-control/UpgradableOwnableWithGuardian.sol'; | ||
|
||
contract ImplOwnableWithGuardian is UpgradableOwnableWithGuardian { | ||
function initialize(address owner, address guardian) public initializer { | ||
__Ownable_init(owner); | ||
__Ownable_With_Guardian_init(guardian); | ||
} | ||
|
||
function mock_onlyGuardian() external onlyGuardian {} | ||
|
||
function mock_onlyOwnerOrGuardian() external onlyOwnerOrGuardian {} | ||
} | ||
|
||
contract TestOfUpgradableOwnableWithGuardian is Test { | ||
UpgradableOwnableWithGuardian public withGuardian; | ||
|
||
address owner = address(0x4); | ||
address guardian = address(0x8); | ||
|
||
function setUp() public { | ||
withGuardian = new ImplOwnableWithGuardian(); | ||
ImplOwnableWithGuardian(address(withGuardian)).initialize(owner, guardian); | ||
} | ||
|
||
function test_initializer() external { | ||
assertEq(withGuardian.owner(), owner); | ||
assertEq(withGuardian.guardian(), guardian); | ||
} | ||
|
||
function test_onlyGuardian() external { | ||
vm.expectRevert( | ||
abi.encodeWithSelector( | ||
UpgradableOwnableWithGuardian.OnlyGuardianInvalidCaller.selector, | ||
address(this) | ||
) | ||
); | ||
ImplOwnableWithGuardian(address(withGuardian)).mock_onlyGuardian(); | ||
} | ||
|
||
function test_onlyOwnerOrGuardian() external { | ||
vm.expectRevert( | ||
abi.encodeWithSelector( | ||
UpgradableOwnableWithGuardian.OnlyGuardianOrOwnerInvalidCaller.selector, | ||
address(this) | ||
) | ||
); | ||
ImplOwnableWithGuardian(address(withGuardian)).mock_onlyOwnerOrGuardian(); | ||
} | ||
|
||
function test_updateGuardian_guardian(address newGuardian) external { | ||
vm.prank(guardian); | ||
withGuardian.updateGuardian(newGuardian); | ||
} | ||
|
||
function test_updateGuardian_owner(address newGuardian) external { | ||
vm.prank(owner); | ||
withGuardian.updateGuardian(newGuardian); | ||
} | ||
|
||
function test_updateGuardian_eoa(address eoa, address newGuardian) external { | ||
vm.assume(eoa != owner && eoa != guardian); | ||
|
||
vm.prank(eoa); | ||
vm.expectRevert( | ||
abi.encodeWithSelector( | ||
UpgradableOwnableWithGuardian.OnlyGuardianOrOwnerInvalidCaller.selector, | ||
eoa | ||
) | ||
); | ||
withGuardian.updateGuardian(newGuardian); | ||
} | ||
} |