Skip to content

Commit

Permalink
Add Owner Guardianship (#1732)
Browse files Browse the repository at this point in the history
* add owner guardian contracts

* fix ownershipRequestedAt usage
  • Loading branch information
mjlescano authored Aug 15, 2023
1 parent b19fa66 commit 698ffd2
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 1 deletion.
71 changes: 71 additions & 0 deletions protocol/governance/contracts/modules/core/GuardianModule.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import {AccessError} from "@synthetixio/core-contracts/contracts/errors/AccessError.sol";
import {AddressError} from "@synthetixio/core-contracts/contracts/errors/AddressError.sol";
import {ChangeError} from "@synthetixio/core-contracts/contracts/errors/ChangeError.sol";
import {Guardian} from "../../storage/Guardian.sol";

contract GuardianModule {
/**
* @notice Thrown when an address tries to accept guardian role but has not been nominated.
* @param addr The address that is trying to accept the guardian role.
*/
error NotNominated(address addr);

/**
* @notice Emitted when an address has been nominated.
* @param newOwner The address that has been nominated.
*/
event GuardianNominated(address newOwner);

/**
* @notice Emitted when the guardianship of the contract has changed.
* @param oldOwner The previous guardian of the contract.
* @param newOwner The new guardian of the contract.
*/
event GuardianChanged(address oldOwner, address newOwner);

function acceptGuardianship() public {
Guardian.Data storage store = Guardian.load();

address currentNominatedGuardian = store.nominatedGuardian;
if (msg.sender != currentNominatedGuardian) {
revert NotNominated(msg.sender);
}

emit GuardianChanged(store.guardian, currentNominatedGuardian);

store.guardian = currentNominatedGuardian;
store.nominatedGuardian = address(0);
store.ownershipRequestedAt = 0;
}

function nominateNewGuardian(address newNominatedGuardian) public {
Guardian.onlyGuardian();

Guardian.Data storage store = Guardian.load();

if (newNominatedGuardian == address(0)) {
revert AddressError.ZeroAddress();
}

if (newNominatedGuardian == store.nominatedGuardian) {
revert ChangeError.NoChange();
}

store.nominatedGuardian = newNominatedGuardian;
emit GuardianNominated(newNominatedGuardian);
}

function renounceGuardianNomination() external {
Guardian.Data storage store = Guardian.load();

if (store.nominatedGuardian != msg.sender) {
revert NotNominated(msg.sender);
}

store.nominatedGuardian = address(0);
store.ownershipRequestedAt = 0;
}
}
54 changes: 53 additions & 1 deletion protocol/governance/contracts/modules/core/OwnerModule.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,61 @@
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import {AccessError} from "@synthetixio/core-contracts/contracts/errors/AccessError.sol";
import {AddressError} from "@synthetixio/core-contracts/contracts/errors/AddressError.sol";
import {ChangeError} from "@synthetixio/core-contracts/contracts/errors/ChangeError.sol";
import {SafeCastU256} from "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";
import {OwnerModule as BaseOwnerModule} from "@synthetixio/core-modules/contracts/modules/OwnerModule.sol";
import {OwnableStorage} from "@synthetixio/core-contracts/contracts/ownership/OwnableStorage.sol";
import {Guardian} from "../../storage/Guardian.sol";

// solhint-disable-next-line no-empty-blocks
contract OwnerModule is BaseOwnerModule {
using SafeCastU256 for uint256;

/**
* @notice Thrown when an the ownership is accepted before the nomination delay
*/
error OwnershipAcceptanceTooEarly();

function acceptOwnership() public override {
super.acceptOwnership();

Guardian.Data storage store = Guardian.load();

if (block.timestamp.to64() - store.ownershipRequestedAt < Guardian.RESCUE_DELAY) {
revert OwnershipAcceptanceTooEarly();
}

store.ownershipRequestedAt = 0;
}

function nominateNewOwner(address newNominatedOwner) public override {
Guardian.onlyGuardian();

OwnableStorage.Data storage ownableStore = OwnableStorage.load();
Guardian.Data storage guardianStore = Guardian.load();

if (newNominatedOwner == address(0)) {
revert AddressError.ZeroAddress();
}

if (
newNominatedOwner == ownableStore.nominatedOwner ||
newNominatedOwner == ownableStore.owner
) {
revert ChangeError.NoChange();
}

guardianStore.ownershipRequestedAt = block.timestamp.to64();
ownableStore.nominatedOwner = newNominatedOwner;

emit OwnerNominated(newNominatedOwner);
}

function renounceNomination() external override {
super.renounceNomination();

Guardian.Data storage store = Guardian.load();
store.ownershipRequestedAt = 0;
}
}
34 changes: 34 additions & 0 deletions protocol/governance/contracts/storage/Guardian.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import {AccessError} from "@synthetixio/core-contracts/contracts/errors/AccessError.sol";

library Guardian {
bytes32 private constant _STORAGE_SLOT =
keccak256(abi.encode("io.synthetix.governance.Guardian"));

uint64 public constant RESCUE_DELAY = 7 days;

struct Data {
address guardian;
address nominatedGuardian;
uint64 ownershipRequestedAt;
}

function load() internal pure returns (Data storage store) {
bytes32 s = _STORAGE_SLOT;
assembly {
store.slot := s
}
}

function onlyGuardian() internal view {
if (msg.sender != getGuardian()) {
revert AccessError.Unauthorized(msg.sender);
}
}

function getGuardian() internal view returns (address) {
return Guardian.load().guardian;
}
}

0 comments on commit 698ffd2

Please sign in to comment.