UNPKG

tenderly-wizard-v6

Version:

A tool for managing virtual testnets using Tenderly

179 lines (178 loc) 12.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.deployAccessControlSystemV2 = void 0; exports.deployRolesV2 = deployRolesV2; exports.enableRolesModifier = enableRolesModifier; exports.setRolesMultisend = setRolesMultisend; exports.setRolesUnwrapper = setRolesUnwrapper; exports.assignRoles = assignRoles; const safe_master_copy_v2_json_1 = __importDefault(require("../contracts/safe_master_copy_v2.json")); const safe_module_proxy_factory_v2_json_1 = __importDefault(require("../contracts/safe_module_proxy_factory_v2.json")); const roles_v2_json_1 = __importDefault(require("../contracts/roles_v2.json")); const util_1 = require("../utils/util"); const colors_1 = __importDefault(require("colors")); const deploy_safe_v2_1 = require("./deploy-safe-v2"); // @ts-ignore const hardhat_1 = require("hardhat"); const EIP2470_1 = require("./EIP2470"); const constants_1 = require("../utils/constants"); const roles_chain_config_1 = require("../utils/roles-chain-config"); const scope_access_controller_v2_1 = require("../whitelist/acs/scope-access-controller-v2"); //@dev note that hardhat struggles with nested contracts. When we call a Safe to interact with Roles, only events from the Safe can be detected. const ZeroHash = "0x0000000000000000000000000000000000000000000000000000000000000000"; const SaltZero = "0x0000000000000000000000000000000000000000000000000000000000000000"; async function deployRolesV2(owner, avatar, target, proxied, chainId, chainConfig) { const [caller] = await hardhat_1.ethers.getSigners(); if (proxied) { const rolesMaster = new hardhat_1.ethers.Contract(chainConfig.ROLES_MASTER_COPY_ADDR, roles_v2_json_1.default, caller); const abiCoder = hardhat_1.ethers.AbiCoder.defaultAbiCoder(); const encoded = abiCoder.encode(["address", "address", "address"], [owner, avatar, target]); const initParams = await rolesMaster.setUp.populateTransaction(encoded); const safeModuleProxyFactory = new hardhat_1.ethers.Contract(chainConfig.SAFE_MODULE_PROXY_FACTORY_ADDR, safe_module_proxy_factory_v2_json_1.default, caller); const predictedRolesAddress = await (0, util_1.predictRolesModAddress)(caller, owner, avatar, target, "v2"); console.log(`predicted roles address: ${predictedRolesAddress}`); try { const deployModTx = await safeModuleProxyFactory.deployModule(chainConfig.ROLES_MASTER_COPY_ADDR, initParams.data, util_1.SALT); const txReceipt = await deployModTx.wait(); const txData = txReceipt.logs?.find((x) => x.fragment?.name === "ModuleProxyCreation"); const rolesModAddress = txData?.args?.proxy; if (rolesModAddress !== predictedRolesAddress) { throw new Error(`Roles mod address deployment unexpected, expected ${predictedRolesAddress}, actual: ${rolesModAddress}`); } console.info(colors_1.default.green(`✅ Roles was deployed via proxy factory to ${rolesModAddress}`)); return rolesModAddress; } catch (e) { console.error(e); throw new Error(`Roles mod address deployment failed: ${e}`); } } // const Permissions = await ethers.getContractFactory("Permissions"); // const permissions = await Permissions.deploy(); const salt = ZeroHash; const Packer = await hardhat_1.ethers.getContractFactory("Packer"); const packerLibraryAddress = await (0, EIP2470_1.deployViaFactory)(Packer.bytecode, salt, caller, "Packer"); const Integrity = await hardhat_1.ethers.getContractFactory("Integrity"); const integrityLibraryAddress = await (0, EIP2470_1.deployViaFactory)(Integrity.bytecode, salt, caller, "Integrity"); const Roles = await hardhat_1.ethers.getContractFactory("Roles", { libraries: { Integrity: integrityLibraryAddress, Packer: packerLibraryAddress, }, }); const roles = await Roles.deploy(owner, avatar, target); await roles.connect(caller).waitForDeployment(); // const rolesAddress = await deployRolesV2(owner, avatar, target, proxied); console.info("Modifier deployed to:", roles.address, "\n"); return roles.address; } //If the roles module is not already enabled on Safe, enable it async function enableRolesModifier(safeAddr, rolesAddr) { const [caller] = await hardhat_1.ethers.getSigners(); const signature = (0, util_1.getPreValidatedSignatures)(caller.address); //investment safe const invSafe = new hardhat_1.ethers.Contract(safeAddr, safe_master_copy_v2_json_1.default, caller); const enabled = await invSafe.isModuleEnabled(rolesAddr); if (!enabled) { const enable = await invSafe.enableModule.populateTransaction(rolesAddr); const enableTx = await invSafe.execTransaction(safeAddr, constants_1.tx.zeroValue, enable.data ?? "", constants_1.tx.operation, constants_1.tx.avatarTxGas, constants_1.tx.baseGas, constants_1.tx.gasPrice, constants_1.tx.gasToken, constants_1.tx.refundReceiver, signature); const txReceipt = await enableTx.wait(); const txData = txReceipt.logs?.find((x) => x.fragment?.name === "EnabledModule"); const moduleEnabledFromEvent = txData?.args?.module; console.info(colors_1.default.blue(`ℹ️ Roles modifier: ${moduleEnabledFromEvent} has been enabled on safe: ${safeAddr}`)); } else { console.info(`Roles modifier: ${rolesAddr} was already enabled on safe: ${safeAddr}`); } } // sets the address of the multisend contract async function setRolesMultisend(safeAddr, rolesAddr, chainConfig) { const [caller] = await hardhat_1.ethers.getSigners(); const roles = new hardhat_1.ethers.Contract(rolesAddr, roles_v2_json_1.default, caller); const multisendOnRecord = await roles.multisend(); //If no MS on record, submit a tx to write one on record if (multisendOnRecord === hardhat_1.ethers.constants.AddressZero) { const setMsPopTx = await roles.setMultisend.populateTransaction(chainConfig.MULTISEND_ADDR); const safe = new hardhat_1.ethers.Contract(safeAddr, safe_master_copy_v2_json_1.default, caller); const signature = (0, util_1.getPreValidatedSignatures)(caller.address); const setMsTx = await safe.execTransaction(rolesAddr, constants_1.tx.zeroValue, setMsPopTx.data, constants_1.tx.operation, constants_1.tx.avatarTxGas, constants_1.tx.baseGas, constants_1.tx.gasPrice, constants_1.tx.gasToken, constants_1.tx.refundReceiver, signature); await setMsTx.wait(); console.info(colors_1.default.blue(`ℹ️ Multisend has been set to: ${chainConfig.MULTISEND_ADDR}`)); } else { console.info(`Multisend has already been previously set to: ${multisendOnRecord}`); } } async function setRolesUnwrapper(safeAddr, rolesAddr, chainConfig) { const [caller] = await hardhat_1.ethers.getSigners(); const roles = new hardhat_1.ethers.Contract(rolesAddr, roles_v2_json_1.default, caller); const setMsPopTx = await roles.setTransactionUnwrapper.populateTransaction(chainConfig.MULTISEND_ADDR, chainConfig.MULTISEND_SELECTOR, chainConfig.DEFAULT_UNWRAPPER_ADDR); const safe = new hardhat_1.ethers.Contract(safeAddr, safe_master_copy_v2_json_1.default, caller); const signature = (0, util_1.getPreValidatedSignatures)(caller.address); const setMsTx = await safe.execTransaction(rolesAddr, constants_1.tx.zeroValue, setMsPopTx.data, constants_1.tx.operation, constants_1.tx.avatarTxGas, constants_1.tx.baseGas, constants_1.tx.gasPrice, constants_1.tx.gasToken, constants_1.tx.refundReceiver, signature); await setMsTx.wait(); console.info(colors_1.default.blue(`ℹ️ Transaction unwrapper has been set to: ${chainConfig.DEFAULT_UNWRAPPER_ADDR}`)); } // assign a role to a array of members addresses attached to a role id policy async function assignRoles(safeAddr, rolesAddr, memberAddrs, roleId, chainConfig) { const [caller] = await hardhat_1.ethers.getSigners(); // assign manager a role (becomes a member of role:manager_role_id) const roles = new hardhat_1.ethers.Contract(rolesAddr, roles_v2_json_1.default, caller); const signature = (0, util_1.getPreValidatedSignatures)(caller.address); const acSafe = new hardhat_1.ethers.Contract(safeAddr, safe_master_copy_v2_json_1.default, caller); const assignRolesPopTx = await Promise.all(memberAddrs.map(async (memberAddr) => { return await roles.assignRoles.populateTransaction(memberAddr, [roleId], [true]); })); const metaTxs = (0, util_1.createMultisendTx)(assignRolesPopTx, chainConfig.MULTISEND_ADDR); await acSafe.execTransaction(chainConfig.MULTISEND_ADDR, constants_1.tx.zeroValue, metaTxs.data, constants_1.SAFE_OPERATION_DELEGATECALL, constants_1.tx.avatarTxGas, constants_1.tx.baseGas, constants_1.tx.gasPrice, constants_1.tx.gasToken, constants_1.tx.refundReceiver, signature); console.info(colors_1.default.blue(`Role member: ${memberAddrs.toString()} has been assigned role id: ${roleId} (default)`)); } // this will deploy the entire system from scratch, WITHOUT any investment manager permissions const deployAccessControlSystemV2 = async (chainId, options, deployed) => { // get chain config for multichain deploy const chainConfig = (0, roles_chain_config_1.getChainConfig)(chainId, "v2"); //Deploy both safes const accessControlSafeAddr = deployed?.acSafeAddr || (await (0, deploy_safe_v2_1.deploySafeV2)(chainConfig, constants_1.SALTS.safes.accessControl)); const investmentSafeAddr = deployed?.invSafeAddr || (await (0, deploy_safe_v2_1.deploySafeV2)(chainConfig, constants_1.SALTS.safes.investment)); // //Deploy and enable a Roles modifier on the investment safe const invRolesAddr = deployed?.invRolesAddr || (await deployRolesV2(accessControlSafeAddr, investmentSafeAddr, investmentSafeAddr, options.proxied, chainId, chainConfig)); await enableRolesModifier(investmentSafeAddr, invRolesAddr); //Set the multisend address on roles so that manager can send multisend txs later on // await setRolesMultisend(accessControlSafeAddr, invRolesAddr); await setRolesUnwrapper(accessControlSafeAddr, invRolesAddr, chainConfig); //Deploy and enable a Roles modifier on the access control safe const acRolesAddr = deployed?.acRolesAddr || (await deployRolesV2(accessControlSafeAddr, accessControlSafeAddr, accessControlSafeAddr, options.proxied, chainId, chainConfig)); await enableRolesModifier(accessControlSafeAddr, acRolesAddr); // //Set the multisend address on roles so that manager can send multisend txs later on // await setRolesMultisend(accessControlSafeAddr, acRolesAddr); await setRolesUnwrapper(accessControlSafeAddr, acRolesAddr, chainConfig); //Grant an access controller role to Security EOA's await assignRoles(accessControlSafeAddr, acRolesAddr, options.securityEOAs, constants_1.SECURITY_ROLE_ID_V2, chainConfig); // Populate this role for Security so they can call whitelisting related functions on investment roles const [caller] = await hardhat_1.ethers.getSigners(); const accessControllerWhitelist = new scope_access_controller_v2_1.AccessControllerWhitelistV2(acRolesAddr, caller); await accessControllerWhitelist.execute(invRolesAddr, accessControlSafeAddr); //Grant a role to the investment managers EOAs //the idea would be that each strategy would be transacted on by 1 EOA await assignRoles(accessControlSafeAddr, invRolesAddr, options.managerEOAs, constants_1.MANAGER_ROLE_ID_V2, chainConfig); // // Add signers await (0, deploy_safe_v2_1.addSafeSigners)(investmentSafeAddr, options.sysAdminAddresses, chainConfig); await (0, deploy_safe_v2_1.addSafeSigners)(accessControlSafeAddr, options.sysAdminAddresses, chainConfig); // //Remove the deployer address as owner and rewrite signing threshold // await removeDeployerAsOwner(investmentSafeAddr, 0); // await removeDeployerAsOwner(accessControlSafeAddr, 0); return { acSafe: accessControlSafeAddr, invSafe: investmentSafeAddr, invRoles: invRolesAddr, acRoles: acRolesAddr, }; }; exports.deployAccessControlSystemV2 = deployAccessControlSystemV2;