// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.21;
/// @title Auth
/// @notice Simple authentication pattern
/// @author Based on code from https://github.com/makerdao/dss
contract Auth {
mapping(address => uint256) public wards;
event Rely(address indexed user);
event Deny(address indexed user);
/// @dev Give permissions to the user
function rely(address user) external auth {
wards[user] = 1;
emit Rely(user);
}
/// @dev Remove permissions from the user
function deny(address user) external auth {
wards[user] = 0;
emit Deny(user);
}
/// @dev Check if the msg.sender has permissions
modifier auth() {
require(wards[msg.sender] == 1, "Auth/not-authorized");
_;
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.21;
import {Auth} from "./util/Auth.sol";
interface AuthLike {
function rely(address) external;
function deny(address) external;
}
/// @title Root
/// @notice Core contract that is a ward on all other deployed contracts.
/// @dev Pausing can happen instantaneously, but relying on other contracts
/// is restricted to the timelock set by the delay.
contract Root is Auth {
/// @dev To prevent filing a delay that would block any updates indefinitely
uint256 internal constant MAX_DELAY = 4 weeks;
address public immutable escrow;
mapping(address relyTarget => uint256 timestamp) public schedule;
uint256 public delay;
bool public paused;
// --- Events ---
event File(bytes32 indexed what, uint256 data);
event Pause();
event Unpause();
event ScheduleRely(address indexed target, uint256 indexed scheduledTime);
event CancelRely(address indexed target);
event RelyContract(address indexed target, address indexed user);
event DenyContract(address indexed target, address indexed user);
constructor(address _escrow, uint256 _delay, address deployer) {
require(_delay <= MAX_DELAY, "Root/delay-too-long");
escrow = _escrow;
delay = _delay;
wards[deployer] = 1;
emit Rely(deployer);
}
// --- Administration ---
function file(bytes32 what, uint256 data) external auth {
if (what == "delay") {
require(data <= MAX_DELAY, "Root/delay-too-long");
delay = data;
} else {
revert("Root/file-unrecognized-param");
}
emit File(what, data);
}
// --- Pause management ---
/// @notice Pause any contracts that depend on `Root.paused()`
function pause() external auth {
paused = true;
emit Pause();
}
/// @notice Unpause any contracts that depend on `Root.paused()`
function unpause() external auth {
paused = false;
emit Unpause();
}
/// --- Timelocked ward management ---
/// @notice Schedule relying a new ward after the delay has passed
function scheduleRely(address target) external auth {
schedule[target] = block.timestamp + delay;
emit ScheduleRely(target, schedule[target]);
}
/// @notice Cancel a pending scheduled rely
function cancelRely(address target) external auth {
require(schedule[target] != 0, "Root/target-not-scheduled");
schedule[target] = 0;
emit CancelRely(target);
}
/// @notice Execute a scheduled rely
/// @dev Can be triggered by anyone since the scheduling is protected
function executeScheduledRely(address target) external {
require(schedule[target] != 0, "Root/target-not-scheduled");
require(schedule[target] <= block.timestamp, "Root/target-not-ready");
wards[target] = 1;
emit Rely(target);
schedule[target] = 0;
}
/// --- External contract ward management ---
/// @notice Make an address a ward on any contract that Root is a ward on
function relyContract(address target, address user) external auth {
AuthLike(target).rely(user);
emit RelyContract(target, user);
}
/// @notice Removes an address as a ward on any contract that Root is a ward on
function denyContract(address target, address user) external auth {
AuthLike(target).deny(user);
emit DenyContract(target, user);
}
}
{
"compilationTarget": {
"src/Root.sol": "Root"
},
"evmVersion": "paris",
"libraries": {
"src/gateway/Messages.sol:Messages": "0xaf9f6ac63c057eb7f59b6fae2c3d447191b58ea5"
},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": [
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_escrow","type":"address"},{"internalType":"uint256","name":"_delay","type":"uint256"},{"internalType":"address","name":"deployer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"}],"name":"CancelRely","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"Deny","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"DenyContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"what","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"data","type":"uint256"}],"name":"File","type":"event"},{"anonymous":false,"inputs":[],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"Rely","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"RelyContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":true,"internalType":"uint256","name":"scheduledTime","type":"uint256"}],"name":"ScheduleRely","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpause","type":"event"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"cancelRely","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"deny","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address","name":"user","type":"address"}],"name":"denyContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"escrow","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"executeScheduledRely","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rely","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address","name":"user","type":"address"}],"name":"relyContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"relyTarget","type":"address"}],"name":"schedule","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"scheduleRely","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]