Governor Timelock

Overview

  • Replaces a Governor EOA with a contract-based timelock so all governor-privileged transactions execute after a delay.

  • Separates responsibilities via roles and enforces a schedule → wait → execute lifecycle.

  • Prevents immediate parameter and role changes. Certain meta‑changes are themselves timelocked.

Key Roles

  • PROPOSER_ROLE: Schedules proposals via scheduleProposals(...) or proposeRoleUpdates(...).

  • EXECUTOR_ROLE: Executes queued proposals during their execution window via executeProposals(...).

  • CANCELLER_ROLE: Unschedules proposals via unscheduleProposals(...) (cannot unschedule role updates).

  • ROLE_ADMIN: Proposes role changes using proposeRoleUpdates(...) and manages token withdrawer pending address.

  • tokenWithdrawer (separate address): Can rescue ERC20 tokens accidentally sent to the timelock.

Timelock Parameters

  • Defaults are set at deployment to at least MIN_DELAY = 1 day and MIN_EXECUTION_WINDOW = 1 day.

  • Default parameters can be changed with setDefaultTimelockParameters(delay, executionWindow). This function is onlySelf and must be executed via a timelocked proposal to the timelock itself.

  • Per‑function overrides: setFunctionTimelockParameters(target, selector, delay, executionWindow) supports either both values set to zero (use defaults) or both meeting minimums. This function is onlySelf and must be timelocked.

  • Changing a function’s timelock uses that function’s prior timelock parameters to gate the change (cannot reduce a delay instantly).

Proposal Lifecycle

  • Schedule: scheduleProposals(address[] targets, bytes[] data) records proposals with delayedUntil and validUntil and computes a proposalHash = keccak256(target, data).

  • Role updates: Must be scheduled via proposeRoleUpdates(bytes32[] roles, address[] accounts, bool[] shouldGrant) (not via scheduleProposals). These cannot be unscheduled.

  • Unschedule: unscheduleProposals(uint256[] proposalIds) allowed only when isUnschedulable is true (i.e., not role updates).

  • Execute: executeProposals(uint256[] ids, address[] targets, bytes[] data) checks existence, timing window, and data hash, deletes the proposal, then calls the target with the exact data.

  • Window semantics: Executable if block.timestamp is within [delayedUntil, validUntil] via isExecutable(id).

Security Notes

  • onlySelf gates sensitive meta‑operations (updateRole, timelock config), ensuring they can only be changed through the timelock.

  • Binds execution to exact calldata via the stored proposalHash.

  • Disallows scheduling to EOAs (target.code.length > 0).

  • Role updates, once queued, cannot be unscheduled.

Token Rescue

  • setPendingTokenWithdrawer(new) (ROLE_ADMIN) → acceptTokenWithdrawer() (new address) establishes the token withdrawer.

  • withdrawERC20Token(token, amount) callable only by tokenWithdrawer to rescue funds sent to the timelock.

Typical Workflows

  • Schedule and execute a MapleGlobals change:

    1. scheduleProposals([globals], [abi.encodeWithSelector(globals.setPlatformServiceFeeRate.selector, newRate)]) (PROPOSER).

    2. Wait until isExecutable(id) returns true within the execution window.

    3. executeProposals([id], [globals], [encodedData]) (EXECUTOR).

  • Grant EXECUTOR_ROLE to a new account:

    1. proposeRoleUpdates([EXECUTOR_ROLE], [newExecutor], [true]) (ROLE_ADMIN).

    2. After delay, executeProposals([id], [timelock], [abi.encodeWithSelector(timelock.updateRole.selector, EXECUTOR_ROLE, newExecutor, true)]) (EXECUTOR).

  • Set a per‑function override (e.g., extend delay for globals.setPriceOracle):

    1. Schedule setFunctionTimelockParameters(globals, setPriceOracle.selector, 2 days, 1 day) to the timelock (PROPOSER).

    2. Execute after the prior timelock for that function has elapsed (EXECUTOR).

Events and Monitoring

  • ProposalScheduled, ProposalExecuted, ProposalUnscheduled for lifecycle transitions.

  • FunctionTimelockSet, DefaultTimelockSet for configuration changes.

  • RoleUpdated for access changes. PendingTokenWithdrawerSet and TokenWithdrawerAccepted for rescue admin.

Deployment and Setup

  • Constructor arguments: tokenWithdrawer, proposer, executor, canceller, roleAdmin.

  • Defaults initialize to minimums and can be increased later via timelocked calls.

  • Recommended mapping:

    • Governance multisig: PROPOSER_ROLE, EXECUTOR_ROLE, ROLE_ADMIN.

    • Security/ops multisig: CANCELLER_ROLE (to unschedule non‑role proposals when needed).

Context

  • This contract replaces a Governor EOA so that all governor‑privileged transactions are enforced to pass through a timelock.

  • Pool Delegate–level timelocks (e.g., contract upgrades) remain documented separately. See technical-resources/admin-functions/timelocks.md.

Last updated