UNPKG

tenderly-wizard-v6

Version:

A tool for managing virtual testnets using Tenderly

123 lines (122 loc) 6.43 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Whitelist = exports.ExecutionOptions = void 0; exports.executeWhitelistV2 = executeWhitelistV2; const ethers_1 = require("ethers"); const roles_v1_json_1 = __importDefault(require("../contracts/roles_v1.json")); const roles_v2_json_1 = __importDefault(require("../contracts/roles_v2.json")); // @ts-ignore const hardhat_1 = require("hardhat"); const zodiac_roles_sdk_1 = require("zodiac-roles-sdk"); const ethers_multisend_1 = require("ethers-multisend"); const constants_1 = require("../utils/constants"); const roles_chain_config_1 = require("../utils/roles-chain-config"); const env_config_1 = __importDefault(require("../env-config")); var ExecutionOptions; (function (ExecutionOptions) { ExecutionOptions[ExecutionOptions["None"] = 0] = "None"; ExecutionOptions[ExecutionOptions["Send"] = 1] = "Send"; ExecutionOptions[ExecutionOptions["DelegateCall"] = 2] = "DelegateCall"; ExecutionOptions[ExecutionOptions["Both"] = 3] = "Both"; })(ExecutionOptions || (exports.ExecutionOptions = ExecutionOptions = {})); class Whitelist { constructor(rolesAddr, rolesVersion, caller) { if (rolesVersion === "v1") { this.roles = new ethers_1.Contract(rolesAddr, roles_v1_json_1.default, caller); } else { this.roles = new ethers_1.Contract(rolesAddr, roles_v2_json_1.default, caller); } this.caller = caller; } // roles.scopeTarget helper function async scopeTargets(targetAddrs, roleId) { const scopeTargetTxs = await Promise.all(targetAddrs.map(async (target) => { //Before granular function/parameter whitelisting can occur, you need to bring a target contract into 'scope' via scopeTarget const tx = await this.roles.scopeTarget.populateTransaction(roleId, target); return tx; })); return scopeTargetTxs; } // Helper to allows function calls without param scoping async scopeAllowFunctionsV1(target, sigs, roleId) { const scopeFuncsTxs = await Promise.all(sigs.map(async (sig) => { // scopeAllowFunction on Roles allows a role member to call the function in question with no paramter scoping const tx = await this.roles.scopeAllowFunction.populateTransaction(roleId, target, sig, ExecutionOptions.Both); return tx; })); return scopeFuncsTxs; } // Helper to allows function calls without param scoping async scopeAllowFunctionsV2(target, sigs, roleId) { const scopeFuncsTxs = await Promise.all(sigs.map(async (sig) => { // allowFunction on Roles allows a role member to call the function in question with no paramter scoping const tx = await this.roles.allowFunction.populateTransaction(roleId, target, sig, ExecutionOptions.Both); return tx; })); return scopeFuncsTxs; } // Helper for crafting erc20 approve related permissions async scopeFunctionERC20Approval(contractAddr, approvedSpender) { const scopedApproveFunctionTx = await this.roles.scopeFunction.populateTransaction(constants_1.MANAGER_ROLE_ID_V2, contractAddr, constants_1.APPROVAL_SIG, [true, false], [constants_1.TYPE_STATIC, constants_1.TYPE_STATIC], [constants_1.EQUAL_TO, constants_1.ANY], [approvedSpender, constants_1.EMPTY_BYTES], constants_1.OPTIONS_SEND); return scopedApproveFunctionTx; } } exports.Whitelist = Whitelist; // // Helper to assert a string is a hex address // export function asHexString(address: string): `0x${string}` { // if (!address.startsWith("0x")) throw new Error("Address must start with 0x"); // if (address.length !== 42) throw new Error("Address must be 42 characters"); // return address as `0x${string}`; // } /** * Executes whitelist permissions for a specific chain. * @note This function will work as long as it's connected to a Hardhat Tenderly testnet. * @param permissions Array of Permission objects to be executed * @param chainId The chain ID where the permissions will be applied * @returns Transaction receipt from the execution * */ async function executeWhitelistV2(permissions, chainId, rolesVersion) { const [caller, manager, dummyOwnerOne, dummyOwnerTwo, dummyOwnerThree, security,] = await hardhat_1.ethers.getSigners(); // get chain config const chainConfig = (0, roles_chain_config_1.getChainConfig)(chainId, rolesVersion); // Process any promises in the permissions array const processedPermissions = (await Promise.all(permissions.map(async (permission) => { if (permission instanceof Promise) { return await permission; } return permission; }))).flat(); // Process the permissions const { targets } = (0, zodiac_roles_sdk_1.processPermissions)(processedPermissions); // Apply the targets const calls = await (0, zodiac_roles_sdk_1.applyTargets)(constants_1.MANAGER_ROLE_ID_V2, targets, { chainId, address: env_config_1.default.ACCESS_CONTROL_ROLES_ADDRESS, mode: "replace", // or "extend" or "remove" log: console.debug, currentTargets: [], }); console.log(`${calls.length} permissions to execute`); const multiSendTx = (0, ethers_multisend_1.encodeMulti)(calls.map((data) => { return { to: env_config_1.default.INVESTMENT_ROLES_ADDRESS, value: "0", data: data, }; })); // Security needs to indirectly execute this bundle via acRoles if (!env_config_1.default.ACCESS_CONTROL_ROLES_ADDRESS) throw new Error("ACCESS_CONTROL_ROLES_ADDRESS is undefined"); const acRoles = new ethers_1.Contract(env_config_1.default.ACCESS_CONTROL_ROLES_ADDRESS, roles_v2_json_1.default, security); // role members wishing to transact as the Safe will always have to call via execTransactionWithRole const tx = await acRoles.execTransactionWithRole(chainConfig.MULTISEND_ADDR, constants_1.ZERO_VALUE, multiSendTx.data, constants_1.SAFE_OPERATION_DELEGATECALL, constants_1.SECURITY_ROLE_ID_V2, true, { gasLimit: constants_1.GAS_LIMIT, }); const receipt = await tx.wait(); console.log("Whitelist executed successfully", receipt.hash); }