diff --git a/.github/actions/verify-network-timelock-authorizer-config/action.yml b/.github/actions/verify-network-timelock-authorizer-config/action.yml new file mode 100644 index 000000000..76ec8db8a --- /dev/null +++ b/.github/actions/verify-network-timelock-authorizer-config/action.yml @@ -0,0 +1,16 @@ +name: 'Verify Timelock Authorizer Config' +inputs: + network-name: + required: true +runs: + using: "composite" + steps: + - uses: actions/checkout@v3 + - name: Set up environment + uses: ./.github/actions/setup + - name: Prepare Config + run: yarn ci:prepare-config + shell: bash + - name: Check Timelock Authorizer config + run: yarn verify-network-timelock-authorizer-config ${{ inputs.network-name }} + shell: bash diff --git a/.github/workflows/deployment-checks.yml b/.github/workflows/deployment-checks.yml index 9ba8a887a..f5d02e252 100644 --- a/.github/workflows/deployment-checks.yml +++ b/.github/workflows/deployment-checks.yml @@ -281,6 +281,17 @@ jobs: - name: Check Deployment Address Lookup File run: yarn check-address-lookup + check-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up environment + uses: ./.github/actions/setup + - name: Prepare Config + run: yarn ci:prepare-config + - name: Check Timelock Authorizer Configuration Files + run: yarn check-timelock-authorizer-config + markdown-link-check: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/timelock-authorizer-config.yml b/.github/workflows/timelock-authorizer-config.yml new file mode 100644 index 000000000..058cd8994 --- /dev/null +++ b/.github/workflows/timelock-authorizer-config.yml @@ -0,0 +1,124 @@ +name: Timelock Authorizer + +on: + push: + branches: + - master + pull_request: + branches: ['*', '**/*'] + schedule: + - cron: "0 0 * * *" + +jobs: + verify-mainnet-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Mainnet Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + MAINNET_RPC_ENDPOINT: ${{ secrets.MAINNET_RPC_ENDPOINT }} + with: + network-name: mainnet + + verify-polygon-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Polygon Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + POLYGON_RPC_ENDPOINT: ${{ secrets.POLYGON_RPC_ENDPOINT }} + with: + network-name: polygon + + verify-arbitrum-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Arbitrum Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + ARBITRUM_RPC_ENDPOINT: ${{ secrets.ARBITRUM_RPC_ENDPOINT }} + with: + network-name: arbitrum + + verify-optimism-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Optimism Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + OPTIMISM_RPC_ENDPOINT: ${{ secrets.OPTIMISM_RPC_ENDPOINT }} + with: + network-name: optimism + + verify-bsc-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Binance Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + BINANCE_RPC_ENDPOINT: ${{ secrets.BINANCE_RPC_ENDPOINT }} + with: + network-name: bsc + + verify-gnosis-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Gnosis Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + GNOSIS_RPC_ENDPOINT: ${{ secrets.GNOSIS_RPC_ENDPOINT }} + with: + network-name: gnosis + + verify-avalanche-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Avalanche Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + AVALANCHE_RPC_ENDPOINT: ${{ secrets.AVALANCHE_RPC_ENDPOINT }} + with: + network-name: avalanche + + verify-zkevm-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Polygon-ZkEvm Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + ZKEVM_RPC_ENDPOINT: ${{ secrets.ZKEVM_RPC_ENDPOINT }} + with: + network-name: zkevm + + verify-goerli-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Goerli Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + GOERLI_RPC_ENDPOINT: ${{ secrets.GOERLI_RPC_ENDPOINT }} + with: + network-name: goerli + + verify-sepolia-timelock-authorizer-config: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Verify Sepolia Timelock Authorizer Configuration + uses: ./.github/actions/verify-network-timelock-authorizer-config + env: + SEPOLIA_RPC_ENDPOINT: ${{ secrets.SEPOLIA_RPC_ENDPOINT }} + with: + network-name: sepolia + +env: + CI: true diff --git a/hardhat.config.ts b/hardhat.config.ts index 911eee187..2b9177cf0 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -16,7 +16,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from 'fs'; import { checkArtifact, extractArtifact } from './src/artifact'; import test from './src/test'; -import Task, { TaskMode } from './src/task'; +import Task, { TaskMode, TaskStatus } from './src/task'; import Verifier from './src/verifier'; import logger, { Logger } from './src/logger'; import { @@ -26,7 +26,13 @@ import { getActionIdInfo, fetchTheGraphPermissions, } from './src/actionId'; -import { checkContractDeploymentAddresses, saveContractDeploymentAddresses } from './src/network'; +import { + checkContractDeploymentAddresses, + checkTimelockAuthorizerConfig, + getTimelockAuthorizerConfigDiff, + saveContractDeploymentAddresses, + saveTimelockAuthorizerConfig, +} from './src/network'; const THEGRAPHURLS: { [key: string]: string } = { goerli: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-authorizer-goerli', @@ -298,6 +304,103 @@ task( } }); +task('build-timelock-authorizer-config', `Builds JSON file with Timelock Authorizer configuration`).setAction( + async (args: { verbose?: boolean }, hre: HardhatRuntimeEnvironment) => { + Logger.setDefaults(false, args.verbose || false); + + if (hre.network.name === 'hardhat') { + logger.warn(`invalid network: ${hre.network.name}`); + return; + } + + // Get active timelock authorizer task. + const tasks = Task.getAllTaskIds() + .filter((taskId) => taskId.includes('timelock-authorizer')) + .map((taskId) => new Task(taskId, TaskMode.READ_ONLY, hre.network.name)) + .filter((task) => task.getStatus() === TaskStatus.ACTIVE); + + if (tasks.length !== 1) { + const errorMsg = tasks.length === 0 ? 'not found' : 'is not unique'; + logger.error(`Active timelock authorizer task ${errorMsg}`); + return; + } + + saveTimelockAuthorizerConfig(tasks[0], hre.network.name); + + logger.success(`Timelock Authorizer config JSON generated for network ${hre.network.name}`); + } +); + +task( + 'check-timelock-authorizer-config', + `Check whether the existing timelock authorizer configuration file is correct` +).setAction(async (args: { verbose?: boolean }, hre: HardhatRuntimeEnvironment) => { + Logger.setDefaults(false, args.verbose || false); + + if (hre.network.name === 'hardhat') { + logger.warn(`invalid network: ${hre.network.name}`); + return; + } + + // Get active timelock authorizer task. + const tasks = Task.getAllTaskIds() + .filter((taskId) => taskId.includes('timelock-authorizer')) + .map((taskId) => new Task(taskId, TaskMode.READ_ONLY, hre.network.name)) + .filter((task) => task.getStatus() === TaskStatus.ACTIVE); + + if (tasks.length !== 1) { + const errorMsg = tasks.length === 0 ? 'not found' : 'is not unique'; + logger.error(`Active timelock authorizer task ${errorMsg}`); + return; + } + + const isConfigOk = checkTimelockAuthorizerConfig(tasks[0], hre.network.name); + + if (isConfigOk) { + logger.success(`Timelock Authorizer config JSON is correct for network ${hre.network.name}`); + } else { + throw new Error( + `Timelock Authorizer config file is incorrect for network ${hre.network.name}. Please run 'build-timelock-authorizer-config' to regenerate it` + ); + } +}); + +task( + 'verify-timelock-authorizer-config', + `Check whether the existing timelock authorizer configuration file matches the delays configured onchain` +).setAction(async (args: { verbose?: boolean }, hre: HardhatRuntimeEnvironment) => { + Logger.setDefaults(false, args.verbose || false); + + if (hre.network.name === 'hardhat') { + logger.warn(`invalid network: ${hre.network.name}`); + return; + } + + // Get active timelock authorizer task. + const tasks = Task.getAllTaskIds() + .filter((taskId) => taskId.includes('timelock-authorizer')) + .map((taskId) => new Task(taskId, TaskMode.READ_ONLY, hre.network.name)) + .filter((task) => task.getStatus() === TaskStatus.ACTIVE); + + if (tasks.length !== 1) { + const errorMsg = tasks.length === 0 ? 'not found' : 'is not unique'; + logger.error(`Active timelock authorizer task ${errorMsg}`); + return; + } + + const configDiff = await getTimelockAuthorizerConfigDiff(tasks[0], hre.network.name); + + if (configDiff.length === 0) { + logger.success(`Timelock Authorizer config is correctly applied on-chain for network ${hre.network.name}`); + } else { + throw new Error( + `Timelock Authorizer config file is incorrect for network ${ + hre.network.name + }. Differences found:\n${JSON.stringify(configDiff, null, 2)}` + ); + } +}); + task(TASK_TEST).addOptionalParam('id', 'Specific task ID of the fork test to run.').setAction(test); export default { diff --git a/package.json b/package.json index 8d2378844..e9f722847 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,10 @@ "extract-artifacts": "hardhat extract-artifacts", "build-address-lookup": "hardhat build-address-lookup --network mainnet && hardhat build-address-lookup --network polygon && hardhat build-address-lookup --network arbitrum && hardhat build-address-lookup --network optimism && hardhat build-address-lookup --network gnosis && hardhat build-address-lookup --network bsc && hardhat build-address-lookup --network avalanche && hardhat build-address-lookup --network zkevm && hardhat build-address-lookup --network base && hardhat build-address-lookup --network fantom && hardhat build-address-lookup --network goerli && hardhat build-address-lookup --network sepolia", "check-address-lookup": "hardhat check-address-lookup --network mainnet && hardhat check-address-lookup --network polygon && hardhat check-address-lookup --network arbitrum && hardhat check-address-lookup --network optimism && hardhat check-address-lookup --network gnosis && hardhat check-address-lookup --network bsc && hardhat check-address-lookup --network avalanche && hardhat check-address-lookup --network zkevm && hardhat check-address-lookup --network base && hardhat check-address-lookup --network fantom && hardhat check-address-lookup --network goerli && hardhat check-address-lookup --network sepolia", + "build-timelock-authorizer-config": "hardhat build-timelock-authorizer-config --network mainnet && hardhat build-timelock-authorizer-config --network polygon && hardhat build-timelock-authorizer-config --network arbitrum && hardhat build-timelock-authorizer-config --network optimism && hardhat build-timelock-authorizer-config --network gnosis && hardhat build-timelock-authorizer-config --network bsc && hardhat build-timelock-authorizer-config --network avalanche && hardhat build-timelock-authorizer-config --network zkevm && hardhat build-timelock-authorizer-config --network base && hardhat build-timelock-authorizer-config --network fantom && hardhat build-timelock-authorizer-config --network goerli && hardhat build-timelock-authorizer-config --network sepolia", + "check-timelock-authorizer-config": "hardhat check-timelock-authorizer-config --network mainnet && hardhat check-timelock-authorizer-config --network polygon && hardhat check-timelock-authorizer-config --network arbitrum && hardhat check-timelock-authorizer-config --network optimism && hardhat check-timelock-authorizer-config --network gnosis && hardhat check-timelock-authorizer-config --network bsc && hardhat check-timelock-authorizer-config --network avalanche && hardhat check-timelock-authorizer-config --network zkevm && hardhat check-timelock-authorizer-config --network base && hardhat check-timelock-authorizer-config --network fantom && hardhat check-timelock-authorizer-config --network goerli && hardhat check-timelock-authorizer-config --network sepolia", + "verify-network-timelock-authorizer-config": "hardhat verify-timelock-authorizer-config --network", + "verify-timelock-authorizer-config": "hardhat verify-timelock-authorizer-config --network mainnet && hardhat verify-timelock-authorizer-config --network polygon && hardhat verify-timelock-authorizer-config --network arbitrum && hardhat verify-timelock-authorizer-config --network optimism && hardhat verify-timelock-authorizer-config --network gnosis && hardhat verify-timelock-authorizer-config --network bsc && hardhat verify-timelock-authorizer-config --network avalanche && hardhat verify-timelock-authorizer-config --network zkevm && hardhat verify-timelock-authorizer-config --network base && hardhat verify-timelock-authorizer-config --network fantom && hardhat verify-timelock-authorizer-config --network goerli && hardhat verify-timelock-authorizer-config --network sepolia", "lint": "yarn lint:solidity && yarn lint:typescript", "lint:solidity": "solhint 'src/helpers/contracts/**/*.sol'", "lint:typescript": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0", diff --git a/src/actionId.ts b/src/actionId.ts index 05eb1884f..b04901ead 100644 --- a/src/actionId.ts +++ b/src/actionId.ts @@ -249,7 +249,7 @@ async function checkFactoryOutput(task: Task, contractName: string, factoryOutpu } /** Returns full info for a given actionId and network */ -export async function getActionIdInfo(actionId: string, network: string): Promise { +export function getActionIdInfo(actionId: string, network: string): ActionIdInfo | undefined { // read network JSON file from action-ids dir const tasks = safeReadJsonFile(path.join(ACTION_ID_DIRECTORY, network, 'action-ids.json')); // filter all the entries which have the same actionId diff --git a/src/helpers/time.ts b/src/helpers/time.ts index ddb1f50f1..909c9befd 100644 --- a/src/helpers/time.ts +++ b/src/helpers/time.ts @@ -1,5 +1,4 @@ import { BigNumber, ContractReceipt } from 'ethers'; -import { ethers } from 'hardhat'; import { BigNumberish, bn } from './numbers'; @@ -33,6 +32,8 @@ export const setNextBlockTimestamp = async (timestamp: BigNumberish): Promise => await time.latestBlock(); export const receiptTimestamp = async (receipt: ContractReceipt | Promise): Promise => { + const { ethers } = await import('hardhat'); + const blockHash = (await receipt).blockHash; const block = await ethers.provider.getBlock(blockHash); return block.timestamp; @@ -44,3 +45,17 @@ export const HOUR = MINUTE * 60; export const DAY = HOUR * 24; export const WEEK = DAY * 7; export const MONTH = DAY * 30; + +export const timestampToString = (timestamp: number): string => { + if (timestamp >= SECOND && timestamp < MINUTE) { + return `${timestamp} ${timestamp > SECOND ? 'seconds' : 'second'}`; + } else if (timestamp >= MINUTE && timestamp < HOUR) { + return `${timestamp / MINUTE} ${timestamp > MINUTE ? 'minutes' : 'minute'}`; + } else if (timestamp >= HOUR && timestamp < DAY) { + return `${timestamp / HOUR} ${timestamp > HOUR ? 'hours' : 'hour'}`; + } else if (timestamp >= DAY && timestamp < MONTH) { + return `${timestamp / DAY} ${timestamp > DAY ? 'days' : 'day'}`; + } else { + return `${timestamp / MONTH} ${timestamp > MONTH ? 'months' : 'month'}`; + } +}; diff --git a/src/network.ts b/src/network.ts index dbd227d5b..426e75eaf 100644 --- a/src/network.ts +++ b/src/network.ts @@ -3,9 +3,14 @@ import path from 'path'; import Task, { TaskStatus } from './task'; import { Network } from './types'; +import { getActionIdInfo } from 'actionId'; +import { timestampToString } from '@helpers/time'; +import { BigNumber } from 'ethers'; +import { bn, decimal } from '@helpers/numbers'; const DEPLOYMENT_TXS_DIRECTORY = path.resolve(__dirname, '../deployment-txs'); const CONTRACT_ADDRESSES_DIRECTORY = path.resolve(__dirname, '../addresses'); +const TIMELOCK_AUTHORIZER_CONFIG_DIRECTORY = path.resolve(__dirname, '../timelock-authorizer-config'); export function saveContractDeploymentTransactionHash( deployedAddress: string, @@ -111,6 +116,151 @@ export function checkContractDeploymentAddresses(tasks: Task[], network: string) return _stringifyEntries(allTaskEntries) === existingFileContents; } +/** + * Builds and saves the timelock authorizer config JSON file, containing grant and execution delays. + * It is based on the input configuration in the deployment task for the given network. + */ +export async function saveTimelockAuthorizerConfig(task: Task, network: string) { + const allDelays = _buildTimelockAuthorizerConfig(task, network); + if (Object.keys(allDelays).length > 0) { + const filePath = path.join(TIMELOCK_AUTHORIZER_CONFIG_DIRECTORY, `${network}.json`); + fs.writeFileSync(filePath, _stringifyEntries(allDelays)); + } +} + +/** + * Returns true if the config file in `TIMELOCK_AUTHORIZER_CONFIG_DIRECTORY` has the right configuration for the + * network, and false otherwise. + * If the timelock authorizer is not deployed for a given network, the file should not exist. + * If the timelock authorizer is deployed for a given network, the file should exist and not be empty. + */ +export function checkTimelockAuthorizerConfig(task: Task, network: string): boolean { + // Returns an empty object if there are no delays defined + const allDelays = _buildTimelockAuthorizerConfig(task, network); + + const taskHasOutput = task.hasOutput(); + + const filePath = path.join(TIMELOCK_AUTHORIZER_CONFIG_DIRECTORY, `${network}.json`); + const fileExists = fs.existsSync(filePath) && fs.statSync(filePath).isFile(); + + // If the task has an output, there should be a file and vice-versa. + // If the task has an output, the configuration cannot be empty. + if (taskHasOutput !== fileExists || (taskHasOutput && Object.keys(allDelays).length === 0)) { + return false; + } + + // Load the existing content if any exists. + const existingFileContents: string = fileExists ? fs.readFileSync(filePath).toString() : '{}'; + + return _stringifyEntries(allDelays) === existingFileContents; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export async function getTimelockAuthorizerConfigDiff(task: Task, network: string): Promise { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const diff: any[] = []; + + if (!task.hasOutput()) { + // If the contract is not deployed for this network, return early. + return diff; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const allDelays: any = _buildTimelockAuthorizerConfig(task, network); + + const timelockAuthorizer = await task.deployedInstance('TimelockAuthorizer'); + + for (const delayInfo of allDelays.grantDelays) { + const actionId = delayInfo.actionIdInfo.actionId; + const onchainDelay: BigNumber = await timelockAuthorizer.getActionIdGrantDelay(actionId); + + if (!onchainDelay.eq(bn(delayInfo.delay.value))) { + diff.push({ + actionId: delayInfo.actionIdInfo, + onchainDelay: decimal(onchainDelay), + expectedDelay: delayInfo.delay.value, + type: 'Grant', + }); + } + } + + for (const delayInfo of allDelays.executeDelays) { + const actionId = delayInfo.actionIdInfo.actionId; + const onchainDelay: BigNumber = await timelockAuthorizer.getActionIdDelay(actionId); + + if (!onchainDelay.eq(bn(delayInfo.delay.value))) { + diff.push({ + actionId: delayInfo.actionIdInfo, + onchainDelay: decimal(onchainDelay), + expectedDelay: delayInfo.delay.value, + type: 'Execute', + }); + } + } + + return diff; +} + +/** + * Builds an object that contains the information for Grant delays and Execute delays. + * The resulting format reads as follows: + * grantDelays: [ + * { + * "actionIdInfo": { + * "taskId": "", + * "contractName": "", + * "useAdaptor": , + * "signature": "", + * "actionId": "" + * }, + * "delay": { + * "label": "", + * "value": + * } + * }, + * ], + * executeDelays: [ + * (...) + * ] + * (...) + */ +function _buildTimelockAuthorizerConfig(task: Task, network: string): object { + const settings = task.settings(); + + const grantDelays = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + settings.GrantDelays?.map((grantDelay: any) => { + return { + actionIdInfo: getActionIdInfo(grantDelay.actionId, network), + delay: { + label: timestampToString(grantDelay.newDelay), + value: grantDelay.newDelay, + }, + }; + }); + + const executeDelays = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + settings.ExecuteDelays?.map((executeDelay: any) => { + return { + actionIdInfo: getActionIdInfo(executeDelay.actionId, network), + delay: { + label: timestampToString(executeDelay.newDelay), + value: executeDelay.newDelay, + }, + }; + }); + + if (grantDelays === undefined && executeDelays === undefined) { + return {}; + } + + return { + grantDelays, + executeDelays, + }; +} + function _stringifyEntries(entries: object): string { return JSON.stringify(entries, null, 2); } diff --git a/src/task.ts b/src/task.ts index be0222bc8..bf31d6a0b 100644 --- a/src/task.ts +++ b/src/task.ts @@ -271,12 +271,7 @@ export default class Task { } rawInput(): RawInputKeyValue { - const taskInputPath = this._fileAt(this.dir(), 'input.ts'); - const rawInput = require(taskInputPath).default; - const globalInput = { ...rawInput }; - NETWORKS.forEach((network) => delete globalInput[network]); - const networkInput = rawInput[this.network] || {}; - return { ...globalInput, ...networkInput }; + return this._getDefaultExportForNetwork('input.ts'); } input(): Input { @@ -293,6 +288,21 @@ export default class Task { return this._read(taskOutputFile); } + settings(): RawInputKeyValue { + return this._getDefaultExportForNetwork('settings.ts'); + } + + hasOutput(): boolean { + let taskHasOutput = true; + try { + this.output(); + } catch { + taskHasOutput = false; + } + + return taskHasOutput; + } + save(rawOutput: RawOutput): void { const output = this._parseRawOutput(rawOutput); @@ -320,6 +330,15 @@ export default class Task { throw new Error('Unknown task status'); } + private _getDefaultExportForNetwork(script: string): RawInputKeyValue { + const taskInputPath = this._fileAt(this.dir(), script); + const rawInput = require(taskInputPath).default; + const globalInput = { ...rawInput }; + NETWORKS.forEach((network) => delete globalInput[network]); + const networkInput = rawInput[this.network] || {}; + return { ...globalInput, ...networkInput }; + } + private _checkManuallySavedArtifacts(output: Output) { for (const name of Object.keys(output)) { const expectedAddress = this.output()[name]; diff --git a/tasks/20230522-timelock-authorizer/input.ts b/tasks/20230522-timelock-authorizer/input.ts index d6610abdf..cb5187e91 100644 --- a/tasks/20230522-timelock-authorizer/input.ts +++ b/tasks/20230522-timelock-authorizer/input.ts @@ -21,8 +21,6 @@ export type TimelockAuthorizerDeploymentInputType = { Authorizer: Task; AuthorizerAdaptorEntrypoint: Task; networks: string[]; - goerli: any; - sepolia: any; [key: string]: any; // index signature }; /* eslint-enable @typescript-eslint/no-explicit-any */ @@ -31,8 +29,11 @@ const input: TimelockAuthorizerDeploymentInputType = { Authorizer, AuthorizerAdaptorEntrypoint, networks: ['goerli', 'sepolia'], - goerli: require('./input/goerli.ts'), - sepolia: require('./input/sepolia.ts'), }; +// Include input files for each network inside global inputs. +input.networks.forEach((network) => { + input[network] = require(`./input/${network}.ts`); +}); + export default input; diff --git a/tasks/20230522-timelock-authorizer/settings.ts b/tasks/20230522-timelock-authorizer/settings.ts new file mode 100644 index 000000000..2298c9466 --- /dev/null +++ b/tasks/20230522-timelock-authorizer/settings.ts @@ -0,0 +1,15 @@ +import input from './input'; + +// This file represents configuration settings used by the contract after its deployment. +// The original deployment input settings must be kept intact to be able to verify the deployments. + +const networks = input.networks; + +// Assign settings file for each deployed network. +const settings = Object.fromEntries( + networks.map((network: string) => { + return [network, require(`./settings/${network}.ts`)]; + }) +); + +export default settings; diff --git a/tasks/20230522-timelock-authorizer/settings/goerli.ts b/tasks/20230522-timelock-authorizer/settings/goerli.ts new file mode 100644 index 000000000..12cca5e30 --- /dev/null +++ b/tasks/20230522-timelock-authorizer/settings/goerli.ts @@ -0,0 +1,4 @@ +import { GrantDelays as DeploymentGrantDelays, ExecuteDelays as DeploymentExecuteDelays } from '../input/goerli'; + +export const GrantDelays = DeploymentGrantDelays; +export const ExecuteDelays = DeploymentExecuteDelays; diff --git a/tasks/20230522-timelock-authorizer/settings/sepolia.ts b/tasks/20230522-timelock-authorizer/settings/sepolia.ts new file mode 100644 index 000000000..a920156de --- /dev/null +++ b/tasks/20230522-timelock-authorizer/settings/sepolia.ts @@ -0,0 +1,4 @@ +import { GrantDelays as DeploymentGrantDelays, ExecuteDelays as DeploymentExecuteDelays } from '../input/sepolia'; + +export const GrantDelays = DeploymentGrantDelays; +export const ExecuteDelays = DeploymentExecuteDelays; diff --git a/timelock-authorizer-config/goerli.json b/timelock-authorizer-config/goerli.json new file mode 100644 index 000000000..824686753 --- /dev/null +++ b/timelock-authorizer-config/goerli.json @@ -0,0 +1,500 @@ +{ + "grantDelays": [ + { + "actionIdInfo": { + "taskId": "20220325-balancer-token-admin", + "contractName": "BalancerTokenAdmin", + "useAdaptor": false, + "signature": "mint(address,uint256)", + "actionId": "0x2315d78651468c46e4eb3cfca481a165fc94d355f1e38cb4e8c60fefdda8f86b" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "add_gauge(address,int128)", + "actionId": "0xf0a77343972ffaea6782864c7880d1cee9576736637f7702cb0a922223208118" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "add_gauge(address,int128,uint256)", + "actionId": "0xaf6651b82c68c85c61ca1ebbae0a7ef9e0ea7394dfdd74ee3b066be3b00b5bec" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "setRelayerApproval(address,address,bool)", + "actionId": "0x0014a06d322ff07fcc02b12f93eb77bb76e28cdee4fc0670b9dec98d24bbfec8" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)", + "actionId": "0x7b8a1d293670124924a0f532213753b89db10bde737249d4540e9a03657d1aff" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)", + "actionId": "0x1282ab709b2b70070f829c46bc36f76b32ad4989fecb2fcb09a1b3ce00bbfc30" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "joinPool(bytes32,address,address,(address[],uint256[],bytes,bool))", + "actionId": "0x78ad1b68d148c070372f8643c4648efbb63c6a8a338f3c24714868e791367653" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "exitPool(bytes32,address,address,(address[],uint256[],bytes,bool))", + "actionId": "0xc149e88b59429ded7f601ab52ecd62331cac006ae07c16543439ed138dcb8d34" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "manageUserBalance((uint8,address,uint256,address,address)[])", + "actionId": "0xeba777d811cd36c06d540d7ff2ed18ed042fd67bbf7c9afcf88c818c7ee6b498" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "ProtocolFeesCollector", + "useAdaptor": false, + "signature": "withdrawCollectedFees(address[],uint256[],address)", + "actionId": "0xb2b6e48fa160a7c887d9d7a68b6a9bb9d47d4953d33e07f3a39e175d75e97796" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "ProtocolFeesCollector", + "useAdaptor": false, + "signature": "setSwapFeePercentage(uint256)", + "actionId": "0xb28b769768735d011b267f781c3be90bce51d5059ba015bc7a28b3e882fb2083" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "ProtocolFeesCollector", + "useAdaptor": false, + "signature": "setFlashLoanFeePercentage(uint256)", + "actionId": "0xbe2a180d5cc5d803a8eec4cea569989fc1c593d7eeadd1f262f360a68b0e842e" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + } + ], + "executeDelays": [ + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "setAuthorizer(address)", + "actionId": "0x1cbb503dcc0f4acaedf71a098211ff8b15a220fc26a6974a8d9deaab040fa6e0" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220420-smart-wallet-checker", + "contractName": "SmartWalletChecker", + "useAdaptor": false, + "signature": "allowlistAddress(address)", + "actionId": "0x552cf8a9722ea54b6fc81eef9df54cbbb0bdd80c1bdacf23d965b7bf7bd9f431" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "VotingEscrow", + "useAdaptor": true, + "signature": "apply_smart_wallet_checker()", + "actionId": "0xaab3dcbbea6d6ade649059061ed3e599f6af4ef510107e1f3907a073c1f09244" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-ve-delegation", + "contractName": "VotingEscrowDelegationProxy", + "useAdaptor": false, + "signature": "setDelegation(address)", + "actionId": "0x34ca00eedd53ffb79d208b3a006775f9adee47b8035872d5bf5b44660794e7b7" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "change_type_weight(int128,uint256)", + "actionId": "0x152991b33a10294e25bee98ea5904acd66ba104e55686c0e5c2b7c60c8b5587a" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "change_gauge_weight(address,uint256)", + "actionId": "0xf4026bb29355fe5b78b8cfbcc14f402a288ec32f152c4ef7abb23bf2ca67588b" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "add_type(string)", + "actionId": "0xddcafee4026801d086053d165ec6a6104729fc0990d0ef6513cb2c418f390236" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "add_type(string,uint256)", + "actionId": "0xb799cbcaf2184ce1ba2d895a50758a4942b871e9db63344a532b545651d69536" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-balancer-token-admin", + "contractName": "BalancerTokenAdmin", + "useAdaptor": false, + "signature": "snapshot()", + "actionId": "0x96133dcf4672458fd010cde802d2f7f1fd5117d5bee5b993848b76059a4d205c" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220725-protocol-fee-percentages-provider", + "contractName": "ProtocolFeePercentagesProvider", + "useAdaptor": false, + "signature": "registerFeeType(uint256,string,uint256,uint256)", + "actionId": "0x57d35b2b35f2327f31e3eb2ca76e6cfffa8b460b1504edfba7b5954bad1d364b" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220725-protocol-fee-percentages-provider", + "contractName": "ProtocolFeePercentagesProvider", + "useAdaptor": false, + "signature": "setFeeTypePercentage(uint256,uint256)", + "actionId": "0xb83d1e9cbef2b3d9321c1d0eb607a8f868eddc97d838926a6697e122b9aad078" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230404-l2-layer0-bridge-forwarder", + "contractName": "L2LayerZeroBridgeForwarder", + "useAdaptor": false, + "signature": "setDelegation(address)", + "actionId": "0x2b1063482a56fcc1993bcf1ee2a264d8d49e7ce1e26e76544e5292ab97210119" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "OmniVotingEscrowAdaptor", + "useAdaptor": false, + "signature": "setAdapterParams(bytes)", + "actionId": "0x54c66f0926db043fd8e623ad2f5f286822e0505918e9a64d6ff6b2533148baef" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "OmniVotingEscrowAdaptor", + "useAdaptor": false, + "signature": "setOmniVotingEscrow(address)", + "actionId": "0x051999085ef95e994d249982784051930228fa5b1555e4b6191f4e2d87ef70c4" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "OmniVotingEscrowAdaptor", + "useAdaptor": false, + "signature": "setUseZero(bool)", + "actionId": "0x96cd69bdf8f32f9f13e2d10e9db83cf181e08962cc1e7a8523f1f9f8c22c0f16" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "OmniVotingEscrowAdaptor", + "useAdaptor": false, + "signature": "setZeroPaymentAddress(address)", + "actionId": "0xfa08331f0860684717f0e3fa283f539e186d06f3a9f1c1d7ce29a4f4f68afaac" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-mainnet-gauge-factory", + "contractName": "LiquidityGaugeV5", + "useAdaptor": true, + "signature": "set_reward_distributor(address,address)", + "actionId": "0x75f03d945d74639aa78784e5071604aa86c170a9f4c32bdf428231d560be70b7" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230316-l2-ve-delegation-proxy", + "contractName": "VotingEscrowDelegationProxy", + "useAdaptor": false, + "signature": "setDelegation(address)", + "actionId": "0xb37c54f1251a66c91992372dad9df4ea678283452056a1aa1e743b730d469cde" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230316-l2-balancer-pseudo-minter", + "contractName": "L2BalancerPseudoMinter", + "useAdaptor": false, + "signature": "addGaugeFactory(address)", + "actionId": "0x395452eddfaf475cccb315fe68df346df514a07264ba291edb128246b94a936a" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230316-l2-balancer-pseudo-minter", + "contractName": "L2BalancerPseudoMinter", + "useAdaptor": false, + "signature": "removeGaugeFactory(address)", + "actionId": "0xd10a81975bf0c68340e10129804be1e3c504a51ac73f693f1905a66da6f067bd" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-mainnet-gauge-factory", + "contractName": "LiquidityGaugeV5", + "useAdaptor": true, + "signature": "add_reward(address,address)", + "actionId": "0x6b5e3750e9adcbd4a75428ed3fc6d2d3fc8a52866660ad3778d3f8c33108edff" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20220822-mainnet-gauge-factory-v2", + "contractName": "LiquidityGaugeV5", + "useAdaptor": true, + "signature": "setRelativeWeightCap(uint256)", + "actionId": "0x53ded5c23d686756f2cc6a8feb053e1a98ea78534f788670216e0617a9cf7101" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20220517-protocol-fee-withdrawer", + "contractName": "ProtocolFeesWithdrawer", + "useAdaptor": false, + "signature": "withdrawCollectedFees(address[],uint256[],address)", + "actionId": "0xaa5a0ba6d1f9627e6905842fe5a64690d039e96637fbf3530abd0960a03bf1a6" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20230223-protocol-id-registry", + "contractName": "ProtocolIdRegistry", + "useAdaptor": false, + "signature": "registerProtocolId(uint256,string)", + "actionId": "0xdfc349d88d311b76af85f0bdc6a81be0e30485d488472e3813359f8e26fc1c95" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20230223-protocol-id-registry", + "contractName": "ProtocolIdRegistry", + "useAdaptor": false, + "signature": "renameProtocolId(uint256,string)", + "actionId": "0x397a10db2c15733cdb7c0fad6226786d83e216ed46c740257186765f4018bec5" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "VotingEscrowRemapper", + "useAdaptor": false, + "signature": "setNetworkRemappingManager(address,address)", + "actionId": "0xd6aa04e37affe1a66309250024fad872cec43e5e59e0b03ab48e00abb896af64" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + } + ] +} \ No newline at end of file diff --git a/timelock-authorizer-config/sepolia.json b/timelock-authorizer-config/sepolia.json new file mode 100644 index 000000000..fc3950777 --- /dev/null +++ b/timelock-authorizer-config/sepolia.json @@ -0,0 +1,500 @@ +{ + "grantDelays": [ + { + "actionIdInfo": { + "taskId": "20220325-balancer-token-admin", + "contractName": "BalancerTokenAdmin", + "useAdaptor": false, + "signature": "mint(address,uint256)", + "actionId": "0x504baaf96c678c93b68ca6c03922803f5a23101336eea2d6ea27b8b86059f257" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "add_gauge(address,int128)", + "actionId": "0x55f1ffb8528d2f64e7785cadb1d935a2bc01dcdb7b93db62bef21fd12cc493c2" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "add_gauge(address,int128,uint256)", + "actionId": "0xdbdac60bd2a4bb437f186e20beb701eb38cc48a3b442c4520849f494969282c6" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "setRelayerApproval(address,address,bool)", + "actionId": "0x0014a06d322ff07fcc02b12f93eb77bb76e28cdee4fc0670b9dec98d24bbfec8" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)", + "actionId": "0x7b8a1d293670124924a0f532213753b89db10bde737249d4540e9a03657d1aff" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)", + "actionId": "0x1282ab709b2b70070f829c46bc36f76b32ad4989fecb2fcb09a1b3ce00bbfc30" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "joinPool(bytes32,address,address,(address[],uint256[],bytes,bool))", + "actionId": "0x78ad1b68d148c070372f8643c4648efbb63c6a8a338f3c24714868e791367653" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "exitPool(bytes32,address,address,(address[],uint256[],bytes,bool))", + "actionId": "0xc149e88b59429ded7f601ab52ecd62331cac006ae07c16543439ed138dcb8d34" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "manageUserBalance((uint8,address,uint256,address,address)[])", + "actionId": "0xeba777d811cd36c06d540d7ff2ed18ed042fd67bbf7c9afcf88c818c7ee6b498" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "ProtocolFeesCollector", + "useAdaptor": false, + "signature": "withdrawCollectedFees(address[],uint256[],address)", + "actionId": "0xb2b6e48fa160a7c887d9d7a68b6a9bb9d47d4953d33e07f3a39e175d75e97796" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "ProtocolFeesCollector", + "useAdaptor": false, + "signature": "setSwapFeePercentage(uint256)", + "actionId": "0xb28b769768735d011b267f781c3be90bce51d5059ba015bc7a28b3e882fb2083" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "ProtocolFeesCollector", + "useAdaptor": false, + "signature": "setFlashLoanFeePercentage(uint256)", + "actionId": "0xbe2a180d5cc5d803a8eec4cea569989fc1c593d7eeadd1f262f360a68b0e842e" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + } + ], + "executeDelays": [ + { + "actionIdInfo": { + "taskId": "20210418-vault", + "contractName": "Vault", + "useAdaptor": false, + "signature": "setAuthorizer(address)", + "actionId": "0x1cbb503dcc0f4acaedf71a098211ff8b15a220fc26a6974a8d9deaab040fa6e0" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220420-smart-wallet-checker", + "contractName": "SmartWalletChecker", + "useAdaptor": false, + "signature": "allowlistAddress(address)", + "actionId": "0x16c29cc2ff29f318666ee5ca529f256e302522ac0f41cd5e248d90678288756c" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "VotingEscrow", + "useAdaptor": true, + "signature": "apply_smart_wallet_checker()", + "actionId": "0x5ec1e4fc7b095ded207161155c47fc79e74d04f6ec67addff7d9e6a3e0b0c0dd" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-ve-delegation", + "contractName": "VotingEscrowDelegationProxy", + "useAdaptor": false, + "signature": "setDelegation(address)", + "actionId": "0x9db73f473bffd0ddda1480f41895d1c57f8d1a3d23456f31dbfb7e3f12392bfa" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "change_type_weight(int128,uint256)", + "actionId": "0x3d679df2218f97925b84308eba8609ffb1fc5d0e18c31413a84272f9722e54cd" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "change_gauge_weight(address,uint256)", + "actionId": "0x9550f010a29ea9bad6fb138e92a994aebbb715cc2c98d978b02f403fa55d0758" + }, + "delay": { + "label": "1 day", + "value": 86400 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "add_type(string)", + "actionId": "0xb02d2f0808812c4ef5a66a4b83df0e6a5f0d3a21900556e1808bf834c41af6c6" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-gauge-controller", + "contractName": "GaugeController", + "useAdaptor": true, + "signature": "add_type(string,uint256)", + "actionId": "0xf1db5552c911608aee9eca30f1ef3632f153af1293386b1535d02eeea1ddb489" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220325-balancer-token-admin", + "contractName": "BalancerTokenAdmin", + "useAdaptor": false, + "signature": "snapshot()", + "actionId": "0xd604f9c832f168646f27b4d66fef7ffcc72c2c52b25bf1b53bb2b9d84ad7f448" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220725-protocol-fee-percentages-provider", + "contractName": "ProtocolFeePercentagesProvider", + "useAdaptor": false, + "signature": "registerFeeType(uint256,string,uint256,uint256)", + "actionId": "0x436389772101f36c82c9d26cb8ca7b7398211e38b2d394e380d43d6d4fa8027c" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220725-protocol-fee-percentages-provider", + "contractName": "ProtocolFeePercentagesProvider", + "useAdaptor": false, + "signature": "setFeeTypePercentage(uint256,uint256)", + "actionId": "0x8560db79be0c16c4273e143ce62ad6a198d391fda59a26fa371930c6c02a6194" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230404-l2-layer0-bridge-forwarder", + "contractName": "L2LayerZeroBridgeForwarder", + "useAdaptor": false, + "signature": "setDelegation(address)", + "actionId": "0xe1a833117678417df5b0aeb8002f897e410d84f7a8f1906e355a97151a3f38a9" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "OmniVotingEscrowAdaptor", + "useAdaptor": false, + "signature": "setAdapterParams(bytes)", + "actionId": "0x166b7d39605e6c0b90d43774990cf351a53a006b3d9b686a453142de3fbea628" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "OmniVotingEscrowAdaptor", + "useAdaptor": false, + "signature": "setOmniVotingEscrow(address)", + "actionId": "0x3ba753a2de5539879f481488cfc216f3fe3397789e5a944d17e61c3fbd569981" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "OmniVotingEscrowAdaptor", + "useAdaptor": false, + "signature": "setUseZero(bool)", + "actionId": "0xb5b7827cc778f4b4d8a0c0f40a66b0338ec17825f6f035d25e81906b69b92c69" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "OmniVotingEscrowAdaptor", + "useAdaptor": false, + "signature": "setZeroPaymentAddress(address)", + "actionId": "0x80a0ca6da9a96e84e5e55d5e9ba7c848aab6861998f0ae43d507d8f8835cd7b1" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220822-mainnet-gauge-factory-v2", + "contractName": "LiquidityGaugeV5", + "useAdaptor": true, + "signature": "set_reward_distributor(address,address)", + "actionId": "0xa68f8859aea0e52f285bf8615e8d0b1c2421e59f342cfea2e496bb19738e86ea" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230316-l2-ve-delegation-proxy", + "contractName": "VotingEscrowDelegationProxy", + "useAdaptor": false, + "signature": "setDelegation(address)", + "actionId": "0x033f3ebfdc58141797e7786a8115159707118fe37ae8f6d7b98929e4465349fb" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230316-l2-balancer-pseudo-minter", + "contractName": "L2BalancerPseudoMinter", + "useAdaptor": false, + "signature": "addGaugeFactory(address)", + "actionId": "0x8df4d209e3818fc96a60498ba4c8a9eaa8b01ce06f738f7467a5154acf092b63" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20230316-l2-balancer-pseudo-minter", + "contractName": "L2BalancerPseudoMinter", + "useAdaptor": false, + "signature": "removeGaugeFactory(address)", + "actionId": "0x7db24189c618e486646b59474c0641efdee8ad627a64e36b3378d1e81421b020" + }, + "delay": { + "label": "3 hours", + "value": 10800 + } + }, + { + "actionIdInfo": { + "taskId": "20220822-mainnet-gauge-factory-v2", + "contractName": "LiquidityGaugeV5", + "useAdaptor": true, + "signature": "add_reward(address,address)", + "actionId": "0xdc8b557d7b464894c525e5de06257694247b2b789b4948326752177399475439" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20220822-mainnet-gauge-factory-v2", + "contractName": "LiquidityGaugeV5", + "useAdaptor": true, + "signature": "setRelativeWeightCap(uint256)", + "actionId": "0x3b2b9d87161388370bfd791bf7a16ebb7ce03597d969b2ffb9d1e46b20c894a4" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20220517-protocol-fee-withdrawer", + "contractName": "ProtocolFeesWithdrawer", + "useAdaptor": false, + "signature": "withdrawCollectedFees(address[],uint256[],address)", + "actionId": "0x7ca4efe72b04296465b751e5dbd7598a2dba855cafef6004b3c952bf43487685" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20230223-protocol-id-registry", + "contractName": "ProtocolIdRegistry", + "useAdaptor": false, + "signature": "registerProtocolId(uint256,string)", + "actionId": "0xc26e21fc0aba43b199202ad3de0ba39a17c1006c1f7b3791ca2a3b9f86a4f0b9" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20230223-protocol-id-registry", + "contractName": "ProtocolIdRegistry", + "useAdaptor": false, + "signature": "renameProtocolId(uint256,string)", + "actionId": "0x717d11aa4cee062d5f4a3262a493c6cb8b8d7985b98d3a3fd2f80631feb89968" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + }, + { + "actionIdInfo": { + "taskId": "20230504-vebal-remapper", + "contractName": "VotingEscrowRemapper", + "useAdaptor": false, + "signature": "setNetworkRemappingManager(address,address)", + "actionId": "0xd2b0db71f202c1fe58ac19654ff20bc1979580e3bde8170f82eb11b3a1f773a0" + }, + "delay": { + "label": "30 minutes", + "value": 1800 + } + } + ] +} \ No newline at end of file