{"sources":{"@openzeppelin/contracts/access/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"},"@openzeppelin/contracts/access/Ownable2Step.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.20;\n\nimport {Ownable} from \"./Ownable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * This extension of the {Ownable} contract includes a two-step mechanism to transfer\n * ownership, where the new owner must call {acceptOwnership} in order to replace the\n * old one. This can help prevent common mistakes, such as transfers of ownership to\n * incorrect accounts, or to contracts that are unable to interact with the\n * permission system.\n *\n * The initial owner is specified at deployment time in the constructor for `Ownable`. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n    address private _pendingOwner;\n\n    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Returns the address of the pending owner.\n     */\n    function pendingOwner() public view virtual returns (address) {\n        return _pendingOwner;\n    }\n\n    /**\n     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n     * Can only be called by the current owner.\n     *\n     * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.\n     */\n    function transferOwnership(address newOwner) public virtual override onlyOwner {\n        _pendingOwner = newOwner;\n        emit OwnershipTransferStarted(owner(), newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual override {\n        delete _pendingOwner;\n        super._transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev The new owner accepts the ownership transfer.\n     */\n    function acceptOwnership() public virtual {\n        address sender = _msgSender();\n        if (pendingOwner() != sender) {\n            revert OwnableUnauthorizedAccount(sender);\n        }\n        _transferOwnership(sender);\n    }\n}\n"},"@openzeppelin/contracts/utils/Context.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"},"contracts/interfaces/IProxyAdmin.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface for OpenZeppelin ProxyAdmin compatible contracts.\n * Proxy is typed as address for compatibility; it must be an ITransparentUpgradeableProxy.\n */\ninterface IProxyAdmin {\n    function upgradeAndCall(\n        address proxy,\n        address implementation,\n        bytes memory data\n    ) external payable;\n\n    function transferOwnership(address newOwner) external;\n}\n"},"contracts/interfaces/ITimelock.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ITimelock {\n    event UpgradeScheduled(\n        address indexed proxyAdmin,\n        address indexed proxy,\n        address implementation,\n        bytes32 operationId\n    );\n    event UpgradeExecuted(\n        address indexed proxyAdmin,\n        address indexed proxy,\n        address implementation,\n        bytes32 operationId\n    );\n    event UpgradeCancelled(\n        address indexed proxyAdmin,\n        address indexed proxy,\n        address implementation,\n        bytes32 operationId\n    );\n    event DelayUpdated(uint256 previousDelay, uint256 newDelay);\n    event ProxyAdminOwnershipTransferScheduled(\n        address indexed proxyAdmin,\n        address indexed newOwner,\n        bytes32 operationId\n    );\n    event ProxyAdminOwnershipTransferExecuted(\n        address indexed proxyAdmin,\n        address indexed newOwner,\n        bytes32 operationId\n    );\n    event ProxyAdminOwnershipTransferCancelled(\n        address indexed proxyAdmin,\n        address indexed newOwner,\n        bytes32 operationId\n    );\n\n    error OperationNotScheduled(bytes32 operationId);\n    error OperationAlreadyScheduled(bytes32 operationId);\n    error DelayNotElapsed(bytes32 operationId, uint256 executeAfter, uint256 currentTime);\n    error DelayTooShort(uint256 requested, uint256 minDelay);\n    error DelayTooLong(uint256 requested, uint256 maxDelay);\n}\n"},"contracts/Timelock.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/access/Ownable2Step.sol\";\nimport \"./interfaces/IProxyAdmin.sol\";\nimport \"./interfaces/ITimelock.sol\";\n\n/**\n * @title Timelock\n * @dev Enforces a delay on proxy upgrades performed via one or more ProxyAdmin contracts.\n *      The single owner (e.g. a multisig) must first schedule an upgrade; the upgrade\n *      can only be executed after the timelock delay has passed.\n *\n *      Use: set each ProxyAdmin's owner to this Timelock, and set this Timelock's owner\n *      to the multisig. All upgrades then go: multisig -> Timelock (schedule) -> wait ->\n *      multisig -> Timelock (execute) -> ProxyAdmin.upgradeAndCall.\n */\ncontract Timelock is Ownable2Step, ITimelock {\n    /// @dev Minimum delay that can be set (e.g. 1 day) to prevent bypass.\n    uint256 public constant MIN_DELAY = 1 days;\n\n    /// @dev Maximum delay cap (e.g. 2 days).\n    uint256 public constant MAX_DELAY = 2 days;\n    /// @dev Fixed delay for ProxyAdmin ownership transfer handover.\n    uint256 public constant OWNERSHIP_TRANSFER_DELAY = 2 days;\n\n    /// @dev Current delay that must pass between schedule and execute.\n    uint256 public delay;\n\n    /// @dev operationId => timestamp when the operation was scheduled (0 = not scheduled or executed/cancelled).\n    mapping(bytes32 => uint256) public scheduledAt;\n    /// @dev ownershipOperationId => timestamp when the ownership transfer was scheduled.\n    mapping(bytes32 => uint256) public scheduledOwnershipTransferAt;\n\n    /**\n     * @param initialOwner The single owner (e.g. multisig) that can schedule, execute, cancel, and set delay.\n     * @param initialDelay Initial timelock delay (must be between MIN_DELAY and MAX_DELAY).\n     */\n    constructor(address initialOwner, uint256 initialDelay) Ownable(initialOwner) {\n        if (initialDelay < MIN_DELAY) revert DelayTooShort(initialDelay, MIN_DELAY);\n        if (initialDelay > MAX_DELAY) revert DelayTooLong(initialDelay, MAX_DELAY);\n        delay = initialDelay;\n        emit DelayUpdated(0, initialDelay);\n    }\n\n    /**\n     * @dev Returns the unique id for a scheduled upgrade.\n     */\n    function getOperationId(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes memory data\n    ) public pure returns (bytes32) {\n        return keccak256(abi.encode(proxyAdmin, proxy, implementation, data));\n    }\n\n    /**\n     * @dev Returns the unique id for a scheduled ProxyAdmin ownership transfer.\n     */\n    function getOwnershipTransferOperationId(\n        address proxyAdmin,\n        address newOwner\n    ) public pure returns (bytes32) {\n        return keccak256(abi.encode(proxyAdmin, newOwner));\n    }\n\n    /**\n     * @dev Schedules an upgrade. Only the owner can call. Same operation can be rescheduled (overwrites).\n     */\n    function scheduleUpgrade(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes calldata data\n    ) external onlyOwner {\n        bytes32 opId = getOperationId(proxyAdmin, proxy, implementation, data);\n        if (scheduledAt[opId] != 0) revert OperationAlreadyScheduled(opId);\n        scheduledAt[opId] = block.timestamp;\n        emit UpgradeScheduled(proxyAdmin, proxy, implementation, opId);\n    }\n\n    /**\n     * @dev Executes a previously scheduled upgrade after the delay has passed.\n     *      Forwards msg.value to ProxyAdmin.upgradeAndCall for upgrade-and-initialize flows.\n     */\n    function executeUpgrade(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes calldata data\n    ) external payable onlyOwner {\n        bytes32 opId = getOperationId(proxyAdmin, proxy, implementation, data);\n        uint256 at = scheduledAt[opId];\n        if (at == 0) revert OperationNotScheduled(opId);\n\n        uint256 executeAfter = at + delay;\n        if (block.timestamp < executeAfter) {\n            revert DelayNotElapsed(opId, executeAfter, block.timestamp);\n        }\n\n        delete scheduledAt[opId];\n        IProxyAdmin(proxyAdmin).upgradeAndCall{value: msg.value}(proxy, implementation, data);\n        emit UpgradeExecuted(proxyAdmin, proxy, implementation, opId);\n    }\n\n    /**\n     * @dev Cancels a scheduled upgrade. Only the owner can call.\n     */\n    function cancelUpgrade(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes calldata data\n    ) external onlyOwner {\n        bytes32 opId = getOperationId(proxyAdmin, proxy, implementation, data);\n        if (scheduledAt[opId] == 0) revert OperationNotScheduled(opId);\n        delete scheduledAt[opId];\n        emit UpgradeCancelled(proxyAdmin, proxy, implementation, opId);\n    }\n\n    /**\n     * @dev Updates the timelock delay. Only the owner. New delay must be between MIN_DELAY and MAX_DELAY.\n     */\n    function setDelay(uint256 newDelay) external onlyOwner {\n        if (newDelay < MIN_DELAY) revert DelayTooShort(newDelay, MIN_DELAY);\n        if (newDelay > MAX_DELAY) revert DelayTooLong(newDelay, MAX_DELAY);\n        uint256 previousDelay = delay;\n        if (newDelay < previousDelay) revert DelayTooShort(newDelay, previousDelay);\n        delay = newDelay;\n        emit DelayUpdated(previousDelay, newDelay);\n    }\n\n    /**\n     * @dev Returns the timestamp after which the given operation can be executed (schedule time + delay).\n     */\n    function getExecuteAfter(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes calldata data\n    ) external view returns (uint256) {\n        bytes32 opId = getOperationId(proxyAdmin, proxy, implementation, data);\n        uint256 at = scheduledAt[opId];\n        return at == 0 ? 0 : at + delay;\n    }\n\n    /**\n     * @dev Schedules a ProxyAdmin ownership transfer. Only owner.\n     *      This path is always guarded by a fixed 30-day delay.\n     */\n    function scheduleProxyAdminOwnershipTransfer(\n        address proxyAdmin,\n        address newOwner\n    ) external onlyOwner {\n        bytes32 opId = getOwnershipTransferOperationId(proxyAdmin, newOwner);\n        if (scheduledOwnershipTransferAt[opId] != 0) revert OperationAlreadyScheduled(opId);\n        scheduledOwnershipTransferAt[opId] = block.timestamp;\n        emit ProxyAdminOwnershipTransferScheduled(proxyAdmin, newOwner, opId);\n    }\n\n    /**\n     * @dev Executes a scheduled ProxyAdmin ownership transfer after 30 days.\n     */\n    function executeProxyAdminOwnershipTransfer(\n        address proxyAdmin,\n        address newOwner\n    ) external onlyOwner {\n        bytes32 opId = getOwnershipTransferOperationId(proxyAdmin, newOwner);\n        uint256 at = scheduledOwnershipTransferAt[opId];\n        if (at == 0) revert OperationNotScheduled(opId);\n\n        uint256 executeAfter = at + OWNERSHIP_TRANSFER_DELAY;\n        if (block.timestamp < executeAfter) {\n            revert DelayNotElapsed(opId, executeAfter, block.timestamp);\n        }\n\n        delete scheduledOwnershipTransferAt[opId];\n        IProxyAdmin(proxyAdmin).transferOwnership(newOwner);\n        emit ProxyAdminOwnershipTransferExecuted(proxyAdmin, newOwner, opId);\n    }\n\n    /**\n     * @dev Cancels a scheduled ProxyAdmin ownership transfer.\n     */\n    function cancelProxyAdminOwnershipTransfer(\n        address proxyAdmin,\n        address newOwner\n    ) external onlyOwner {\n        bytes32 opId = getOwnershipTransferOperationId(proxyAdmin, newOwner);\n        if (scheduledOwnershipTransferAt[opId] == 0) revert OperationNotScheduled(opId);\n        delete scheduledOwnershipTransferAt[opId];\n        emit ProxyAdminOwnershipTransferCancelled(proxyAdmin, newOwner, opId);\n    }\n\n    /**\n     * @dev Returns timestamp after which ownership transfer can execute.\n     */\n    function getOwnershipTransferExecuteAfter(\n        address proxyAdmin,\n        address newOwner\n    ) external view returns (uint256) {\n        bytes32 opId = getOwnershipTransferOperationId(proxyAdmin, newOwner);\n        uint256 at = scheduledOwnershipTransferAt[opId];\n        return at == 0 ? 0 : at + OWNERSHIP_TRANSFER_DELAY;\n    }\n}\n"}},"abi":[{"type":"constructor","inputs":[{"name":"initialOwner","type":"address","internalType":"address"},{"name":"initialDelay","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"name":"DelayNotElapsed","type":"error","inputs":[{"name":"operationId","type":"bytes32","internalType":"bytes32"},{"name":"executeAfter","type":"uint256","internalType":"uint256"},{"name":"currentTime","type":"uint256","internalType":"uint256"}]},{"name":"DelayTooLong","type":"error","inputs":[{"name":"requested","type":"uint256","internalType":"uint256"},{"name":"maxDelay","type":"uint256","internalType":"uint256"}]},{"name":"DelayTooShort","type":"error","inputs":[{"name":"requested","type":"uint256","internalType":"uint256"},{"name":"minDelay","type":"uint256","internalType":"uint256"}]},{"name":"OperationAlreadyScheduled","type":"error","inputs":[{"name":"operationId","type":"bytes32","internalType":"bytes32"}]},{"name":"OperationNotScheduled","type":"error","inputs":[{"name":"operationId","type":"bytes32","internalType":"bytes32"}]},{"name":"OwnableInvalidOwner","type":"error","inputs":[{"name":"owner","type":"address","internalType":"address"}]},{"name":"OwnableUnauthorizedAccount","type":"error","inputs":[{"name":"account","type":"address","internalType":"address"}]},{"name":"DelayUpdated","type":"event","inputs":[{"name":"previousDelay","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"newDelay","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"OwnershipTransferStarted","type":"event","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"name":"OwnershipTransferred","type":"event","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"name":"ProxyAdminOwnershipTransferCancelled","type":"event","inputs":[{"name":"proxyAdmin","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"},{"name":"operationId","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"name":"ProxyAdminOwnershipTransferExecuted","type":"event","inputs":[{"name":"proxyAdmin","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"},{"name":"operationId","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"name":"ProxyAdminOwnershipTransferScheduled","type":"event","inputs":[{"name":"proxyAdmin","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"},{"name":"operationId","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"name":"UpgradeCancelled","type":"event","inputs":[{"name":"proxyAdmin","type":"address","indexed":true,"internalType":"address"},{"name":"proxy","type":"address","indexed":true,"internalType":"address"},{"name":"implementation","type":"address","indexed":false,"internalType":"address"},{"name":"operationId","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"name":"UpgradeExecuted","type":"event","inputs":[{"name":"proxyAdmin","type":"address","indexed":true,"internalType":"address"},{"name":"proxy","type":"address","indexed":true,"internalType":"address"},{"name":"implementation","type":"address","indexed":false,"internalType":"address"},{"name":"operationId","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"name":"UpgradeScheduled","type":"event","inputs":[{"name":"proxyAdmin","type":"address","indexed":true,"internalType":"address"},{"name":"proxy","type":"address","indexed":true,"internalType":"address"},{"name":"implementation","type":"address","indexed":false,"internalType":"address"},{"name":"operationId","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"name":"MAX_DELAY","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MIN_DELAY","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"OWNERSHIP_TRANSFER_DELAY","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"acceptOwnership","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"name":"cancelProxyAdminOwnershipTransfer","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"cancelUpgrade","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"proxy","type":"address","internalType":"address"},{"name":"implementation","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"delay","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"executeProxyAdminOwnershipTransfer","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"executeUpgrade","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"proxy","type":"address","internalType":"address"},{"name":"implementation","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"payable"},{"name":"getExecuteAfter","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"proxy","type":"address","internalType":"address"},{"name":"implementation","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"getOperationId","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"proxy","type":"address","internalType":"address"},{"name":"implementation","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"pure"},{"name":"getOwnershipTransferExecuteAfter","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"getOwnershipTransferOperationId","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"pure"},{"name":"owner","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"pendingOwner","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"renounceOwnership","type":"function","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"name":"scheduleProxyAdminOwnershipTransfer","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"scheduleUpgrade","type":"function","inputs":[{"name":"proxyAdmin","type":"address","internalType":"address"},{"name":"proxy","type":"address","internalType":"address"},{"name":"implementation","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"scheduledAt","type":"function","inputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"scheduledOwnershipTransferAt","type":"function","inputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"setDelay","type":"function","inputs":[{"name":"newDelay","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"transferOwnership","type":"function","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"}],"metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"uint256","name":"initialDelay","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"operationId","type":"bytes32"},{"internalType":"uint256","name":"executeAfter","type":"uint256"},{"internalType":"uint256","name":"currentTime","type":"uint256"}],"name":"DelayNotElapsed","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"maxDelay","type":"uint256"}],"name":"DelayTooLong","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"minDelay","type":"uint256"}],"name":"DelayTooShort","type":"error"},{"inputs":[{"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"OperationAlreadyScheduled","type":"error"},{"inputs":[{"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"OperationNotScheduled","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newDelay","type":"uint256"}],"name":"DelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proxyAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"},{"indexed":false,"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"ProxyAdminOwnershipTransferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proxyAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"},{"indexed":false,"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"ProxyAdminOwnershipTransferExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proxyAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"},{"indexed":false,"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"ProxyAdminOwnershipTransferScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proxyAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"proxy","type":"address"},{"indexed":false,"internalType":"address","name":"implementation","type":"address"},{"indexed":false,"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"UpgradeCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proxyAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"proxy","type":"address"},{"indexed":false,"internalType":"address","name":"implementation","type":"address"},{"indexed":false,"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"UpgradeExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proxyAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"proxy","type":"address"},{"indexed":false,"internalType":"address","name":"implementation","type":"address"},{"indexed":false,"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"UpgradeScheduled","type":"event"},{"inputs":[],"name":"MAX_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNERSHIP_TRANSFER_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"cancelProxyAdminOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"proxy","type":"address"},{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"cancelUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"executeProxyAdminOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"proxy","type":"address"},{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"executeUpgrade","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"proxy","type":"address"},{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"getExecuteAfter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"proxy","type":"address"},{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"getOperationId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"getOwnershipTransferExecuteAfter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"getOwnershipTransferOperationId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"scheduleProxyAdminOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAdmin","type":"address"},{"internalType":"address","name":"proxy","type":"address"},{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"scheduleUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"scheduledAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"scheduledOwnershipTransferAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newDelay","type":"uint256"}],"name":"setDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"details":"Enforces a delay on proxy upgrades performed via one or more ProxyAdmin contracts.      The single owner (e.g. a multisig) must first schedule an upgrade; the upgrade      can only be executed after the timelock delay has passed.      Use: set each ProxyAdmin's owner to this Timelock, and set this Timelock's owner      to the multisig. All upgrades then go: multisig -> Timelock (schedule) -> wait ->      multisig -> Timelock (execute) -> ProxyAdmin.upgradeAndCall.","errors":{"OwnableInvalidOwner(address)":[{"details":"The owner is not a valid owner account. (eg. `address(0)`)"}],"OwnableUnauthorizedAccount(address)":[{"details":"The caller account is not authorized to perform an operation."}]},"kind":"dev","methods":{"acceptOwnership()":{"details":"The new owner accepts the ownership transfer."},"cancelProxyAdminOwnershipTransfer(address,address)":{"details":"Cancels a scheduled ProxyAdmin ownership transfer."},"cancelUpgrade(address,address,address,bytes)":{"details":"Cancels a scheduled upgrade. Only the owner can call."},"constructor":{"params":{"initialDelay":"Initial timelock delay (must be between MIN_DELAY and MAX_DELAY).","initialOwner":"The single owner (e.g. multisig) that can schedule, execute, cancel, and set delay."}},"executeProxyAdminOwnershipTransfer(address,address)":{"details":"Executes a scheduled ProxyAdmin ownership transfer after 30 days."},"executeUpgrade(address,address,address,bytes)":{"details":"Executes a previously scheduled upgrade after the delay has passed.      Forwards msg.value to ProxyAdmin.upgradeAndCall for upgrade-and-initialize flows."},"getExecuteAfter(address,address,address,bytes)":{"details":"Returns the timestamp after which the given operation can be executed (schedule time + delay)."},"getOperationId(address,address,address,bytes)":{"details":"Returns the unique id for a scheduled upgrade."},"getOwnershipTransferExecuteAfter(address,address)":{"details":"Returns timestamp after which ownership transfer can execute."},"getOwnershipTransferOperationId(address,address)":{"details":"Returns the unique id for a scheduled ProxyAdmin ownership transfer."},"owner()":{"details":"Returns the address of the current owner."},"pendingOwner()":{"details":"Returns the address of the pending owner."},"renounceOwnership()":{"details":"Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner."},"scheduleProxyAdminOwnershipTransfer(address,address)":{"details":"Schedules a ProxyAdmin ownership transfer. Only owner.      This path is always guarded by a fixed 30-day delay."},"scheduleUpgrade(address,address,address,bytes)":{"details":"Schedules an upgrade. Only the owner can call. Same operation can be rescheduled (overwrites)."},"setDelay(uint256)":{"details":"Updates the timelock delay. Only the owner. New delay must be between MIN_DELAY and MAX_DELAY."},"transferOwnership(address)":{"details":"Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. Can only be called by the current owner. Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer."}},"stateVariables":{"MAX_DELAY":{"details":"Maximum delay cap (e.g. 2 days)."},"MIN_DELAY":{"details":"Minimum delay that can be set (e.g. 1 day) to prevent bypass."},"OWNERSHIP_TRANSFER_DELAY":{"details":"Fixed delay for ProxyAdmin ownership transfer handover."},"delay":{"details":"Current delay that must pass between schedule and execute."},"scheduledAt":{"details":"operationId => timestamp when the operation was scheduled (0 = not scheduled or executed/cancelled)."},"scheduledOwnershipTransferAt":{"details":"ownershipOperationId => timestamp when the ownership transfer was scheduled."}},"title":"Timelock","version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"contracts/Timelock.sol":"Timelock"},"evmVersion":"paris","libraries":{},"metadata":{"bytecodeHash":"ipfs","useLiteralContent":true},"optimizer":{"enabled":true,"runs":200},"remappings":[]},"sources":{"@openzeppelin/contracts/access/Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * The initial owner is set to the address provided by the deployer. This can\n * later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n","keccak256":"0xff6d0bb2e285473e5311d9d3caacb525ae3538a80758c10649a4d61029b017bb","license":"MIT"},"@openzeppelin/contracts/access/Ownable2Step.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)\n\npragma solidity ^0.8.20;\n\nimport {Ownable} from \"./Ownable.sol\";\n\n/**\n * @dev Contract module which provides access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * This extension of the {Ownable} contract includes a two-step mechanism to transfer\n * ownership, where the new owner must call {acceptOwnership} in order to replace the\n * old one. This can help prevent common mistakes, such as transfers of ownership to\n * incorrect accounts, or to contracts that are unable to interact with the\n * permission system.\n *\n * The initial owner is specified at deployment time in the constructor for `Ownable`. This\n * can later be changed with {transferOwnership} and {acceptOwnership}.\n *\n * This module is used through inheritance. It will make available all functions\n * from parent (Ownable).\n */\nabstract contract Ownable2Step is Ownable {\n    address private _pendingOwner;\n\n    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Returns the address of the pending owner.\n     */\n    function pendingOwner() public view virtual returns (address) {\n        return _pendingOwner;\n    }\n\n    /**\n     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.\n     * Can only be called by the current owner.\n     *\n     * Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.\n     */\n    function transferOwnership(address newOwner) public virtual override onlyOwner {\n        _pendingOwner = newOwner;\n        emit OwnershipTransferStarted(owner(), newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual override {\n        delete _pendingOwner;\n        super._transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev The new owner accepts the ownership transfer.\n     */\n    function acceptOwnership() public virtual {\n        address sender = _msgSender();\n        if (pendingOwner() != sender) {\n            revert OwnableUnauthorizedAccount(sender);\n        }\n        _transferOwnership(sender);\n    }\n}\n","keccak256":"0xdcad8898fda432696597752e8ec361b87d85c82cb258115427af006dacf7128c","license":"MIT"},"@openzeppelin/contracts/utils/Context.sol":{"content":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n","keccak256":"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2","license":"MIT"},"contracts/Timelock.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/access/Ownable2Step.sol\";\nimport \"./interfaces/IProxyAdmin.sol\";\nimport \"./interfaces/ITimelock.sol\";\n\n/**\n * @title Timelock\n * @dev Enforces a delay on proxy upgrades performed via one or more ProxyAdmin contracts.\n *      The single owner (e.g. a multisig) must first schedule an upgrade; the upgrade\n *      can only be executed after the timelock delay has passed.\n *\n *      Use: set each ProxyAdmin's owner to this Timelock, and set this Timelock's owner\n *      to the multisig. All upgrades then go: multisig -> Timelock (schedule) -> wait ->\n *      multisig -> Timelock (execute) -> ProxyAdmin.upgradeAndCall.\n */\ncontract Timelock is Ownable2Step, ITimelock {\n    /// @dev Minimum delay that can be set (e.g. 1 day) to prevent bypass.\n    uint256 public constant MIN_DELAY = 1 days;\n\n    /// @dev Maximum delay cap (e.g. 2 days).\n    uint256 public constant MAX_DELAY = 2 days;\n    /// @dev Fixed delay for ProxyAdmin ownership transfer handover.\n    uint256 public constant OWNERSHIP_TRANSFER_DELAY = 2 days;\n\n    /// @dev Current delay that must pass between schedule and execute.\n    uint256 public delay;\n\n    /// @dev operationId => timestamp when the operation was scheduled (0 = not scheduled or executed/cancelled).\n    mapping(bytes32 => uint256) public scheduledAt;\n    /// @dev ownershipOperationId => timestamp when the ownership transfer was scheduled.\n    mapping(bytes32 => uint256) public scheduledOwnershipTransferAt;\n\n    /**\n     * @param initialOwner The single owner (e.g. multisig) that can schedule, execute, cancel, and set delay.\n     * @param initialDelay Initial timelock delay (must be between MIN_DELAY and MAX_DELAY).\n     */\n    constructor(address initialOwner, uint256 initialDelay) Ownable(initialOwner) {\n        if (initialDelay < MIN_DELAY) revert DelayTooShort(initialDelay, MIN_DELAY);\n        if (initialDelay > MAX_DELAY) revert DelayTooLong(initialDelay, MAX_DELAY);\n        delay = initialDelay;\n        emit DelayUpdated(0, initialDelay);\n    }\n\n    /**\n     * @dev Returns the unique id for a scheduled upgrade.\n     */\n    function getOperationId(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes memory data\n    ) public pure returns (bytes32) {\n        return keccak256(abi.encode(proxyAdmin, proxy, implementation, data));\n    }\n\n    /**\n     * @dev Returns the unique id for a scheduled ProxyAdmin ownership transfer.\n     */\n    function getOwnershipTransferOperationId(\n        address proxyAdmin,\n        address newOwner\n    ) public pure returns (bytes32) {\n        return keccak256(abi.encode(proxyAdmin, newOwner));\n    }\n\n    /**\n     * @dev Schedules an upgrade. Only the owner can call. Same operation can be rescheduled (overwrites).\n     */\n    function scheduleUpgrade(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes calldata data\n    ) external onlyOwner {\n        bytes32 opId = getOperationId(proxyAdmin, proxy, implementation, data);\n        if (scheduledAt[opId] != 0) revert OperationAlreadyScheduled(opId);\n        scheduledAt[opId] = block.timestamp;\n        emit UpgradeScheduled(proxyAdmin, proxy, implementation, opId);\n    }\n\n    /**\n     * @dev Executes a previously scheduled upgrade after the delay has passed.\n     *      Forwards msg.value to ProxyAdmin.upgradeAndCall for upgrade-and-initialize flows.\n     */\n    function executeUpgrade(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes calldata data\n    ) external payable onlyOwner {\n        bytes32 opId = getOperationId(proxyAdmin, proxy, implementation, data);\n        uint256 at = scheduledAt[opId];\n        if (at == 0) revert OperationNotScheduled(opId);\n\n        uint256 executeAfter = at + delay;\n        if (block.timestamp < executeAfter) {\n            revert DelayNotElapsed(opId, executeAfter, block.timestamp);\n        }\n\n        delete scheduledAt[opId];\n        IProxyAdmin(proxyAdmin).upgradeAndCall{value: msg.value}(proxy, implementation, data);\n        emit UpgradeExecuted(proxyAdmin, proxy, implementation, opId);\n    }\n\n    /**\n     * @dev Cancels a scheduled upgrade. Only the owner can call.\n     */\n    function cancelUpgrade(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes calldata data\n    ) external onlyOwner {\n        bytes32 opId = getOperationId(proxyAdmin, proxy, implementation, data);\n        if (scheduledAt[opId] == 0) revert OperationNotScheduled(opId);\n        delete scheduledAt[opId];\n        emit UpgradeCancelled(proxyAdmin, proxy, implementation, opId);\n    }\n\n    /**\n     * @dev Updates the timelock delay. Only the owner. New delay must be between MIN_DELAY and MAX_DELAY.\n     */\n    function setDelay(uint256 newDelay) external onlyOwner {\n        if (newDelay < MIN_DELAY) revert DelayTooShort(newDelay, MIN_DELAY);\n        if (newDelay > MAX_DELAY) revert DelayTooLong(newDelay, MAX_DELAY);\n        uint256 previousDelay = delay;\n        if (newDelay < previousDelay) revert DelayTooShort(newDelay, previousDelay);\n        delay = newDelay;\n        emit DelayUpdated(previousDelay, newDelay);\n    }\n\n    /**\n     * @dev Returns the timestamp after which the given operation can be executed (schedule time + delay).\n     */\n    function getExecuteAfter(\n        address proxyAdmin,\n        address proxy,\n        address implementation,\n        bytes calldata data\n    ) external view returns (uint256) {\n        bytes32 opId = getOperationId(proxyAdmin, proxy, implementation, data);\n        uint256 at = scheduledAt[opId];\n        return at == 0 ? 0 : at + delay;\n    }\n\n    /**\n     * @dev Schedules a ProxyAdmin ownership transfer. Only owner.\n     *      This path is always guarded by a fixed 30-day delay.\n     */\n    function scheduleProxyAdminOwnershipTransfer(\n        address proxyAdmin,\n        address newOwner\n    ) external onlyOwner {\n        bytes32 opId = getOwnershipTransferOperationId(proxyAdmin, newOwner);\n        if (scheduledOwnershipTransferAt[opId] != 0) revert OperationAlreadyScheduled(opId);\n        scheduledOwnershipTransferAt[opId] = block.timestamp;\n        emit ProxyAdminOwnershipTransferScheduled(proxyAdmin, newOwner, opId);\n    }\n\n    /**\n     * @dev Executes a scheduled ProxyAdmin ownership transfer after 30 days.\n     */\n    function executeProxyAdminOwnershipTransfer(\n        address proxyAdmin,\n        address newOwner\n    ) external onlyOwner {\n        bytes32 opId = getOwnershipTransferOperationId(proxyAdmin, newOwner);\n        uint256 at = scheduledOwnershipTransferAt[opId];\n        if (at == 0) revert OperationNotScheduled(opId);\n\n        uint256 executeAfter = at + OWNERSHIP_TRANSFER_DELAY;\n        if (block.timestamp < executeAfter) {\n            revert DelayNotElapsed(opId, executeAfter, block.timestamp);\n        }\n\n        delete scheduledOwnershipTransferAt[opId];\n        IProxyAdmin(proxyAdmin).transferOwnership(newOwner);\n        emit ProxyAdminOwnershipTransferExecuted(proxyAdmin, newOwner, opId);\n    }\n\n    /**\n     * @dev Cancels a scheduled ProxyAdmin ownership transfer.\n     */\n    function cancelProxyAdminOwnershipTransfer(\n        address proxyAdmin,\n        address newOwner\n    ) external onlyOwner {\n        bytes32 opId = getOwnershipTransferOperationId(proxyAdmin, newOwner);\n        if (scheduledOwnershipTransferAt[opId] == 0) revert OperationNotScheduled(opId);\n        delete scheduledOwnershipTransferAt[opId];\n        emit ProxyAdminOwnershipTransferCancelled(proxyAdmin, newOwner, opId);\n    }\n\n    /**\n     * @dev Returns timestamp after which ownership transfer can execute.\n     */\n    function getOwnershipTransferExecuteAfter(\n        address proxyAdmin,\n        address newOwner\n    ) external view returns (uint256) {\n        bytes32 opId = getOwnershipTransferOperationId(proxyAdmin, newOwner);\n        uint256 at = scheduledOwnershipTransferAt[opId];\n        return at == 0 ? 0 : at + OWNERSHIP_TRANSFER_DELAY;\n    }\n}\n","keccak256":"0x3091308d1c5ed8117e359d2d818e9b2de9e4f4d291307ed0e07a61743be78641","license":"MIT"},"contracts/interfaces/IProxyAdmin.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface for OpenZeppelin ProxyAdmin compatible contracts.\n * Proxy is typed as address for compatibility; it must be an ITransparentUpgradeableProxy.\n */\ninterface IProxyAdmin {\n    function upgradeAndCall(\n        address proxy,\n        address implementation,\n        bytes memory data\n    ) external payable;\n\n    function transferOwnership(address newOwner) external;\n}\n","keccak256":"0x5c9cbeee5223845d475de3e6ff17315ad1a0500c216a95e47c5a19287af71165","license":"MIT"},"contracts/interfaces/ITimelock.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface ITimelock {\n    event UpgradeScheduled(\n        address indexed proxyAdmin,\n        address indexed proxy,\n        address implementation,\n        bytes32 operationId\n    );\n    event UpgradeExecuted(\n        address indexed proxyAdmin,\n        address indexed proxy,\n        address implementation,\n        bytes32 operationId\n    );\n    event UpgradeCancelled(\n        address indexed proxyAdmin,\n        address indexed proxy,\n        address implementation,\n        bytes32 operationId\n    );\n    event DelayUpdated(uint256 previousDelay, uint256 newDelay);\n    event ProxyAdminOwnershipTransferScheduled(\n        address indexed proxyAdmin,\n        address indexed newOwner,\n        bytes32 operationId\n    );\n    event ProxyAdminOwnershipTransferExecuted(\n        address indexed proxyAdmin,\n        address indexed newOwner,\n        bytes32 operationId\n    );\n    event ProxyAdminOwnershipTransferCancelled(\n        address indexed proxyAdmin,\n        address indexed newOwner,\n        bytes32 operationId\n    );\n\n    error OperationNotScheduled(bytes32 operationId);\n    error OperationAlreadyScheduled(bytes32 operationId);\n    error DelayNotElapsed(bytes32 operationId, uint256 executeAfter, uint256 currentTime);\n    error DelayTooShort(uint256 requested, uint256 minDelay);\n    error DelayTooLong(uint256 requested, uint256 maxDelay);\n}\n","keccak256":"0xa9b1a8ef136875a0b416ba3ce2aa80a767c6c4d5b463fd703d715bdc0b4b2953","license":"MIT"}},"version":1},"matchId":"28512770","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-05-04T08:53:02Z","match":"exact_match","chainId":"1","address":"0xE5e412C212B4FBbF550A94e7BD5e83dB0B315A7F"}