tenderly-wizard-v6
Version:
A tool for managing virtual testnets using Tenderly
123 lines (122 loc) • 6.43 kB
JavaScript
;
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);
}