diff --git a/.github/workflows/dispatch-draft-release.yml b/.github/workflows/dispatch-draft-release.yml new file mode 100644 index 0000000..de180c9 --- /dev/null +++ b/.github/workflows/dispatch-draft-release.yml @@ -0,0 +1,12 @@ +name: Draft release + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +on: + workflow_dispatch: + +jobs: + draft-release: + uses: bgd-labs/github-workflows/.github/workflows/draft-release.yml@main diff --git a/.github/workflows/merge-main.yml b/.github/workflows/merge-main.yml new file mode 100644 index 0000000..e9be163 --- /dev/null +++ b/.github/workflows/merge-main.yml @@ -0,0 +1,19 @@ +name: Head branch workflow + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +on: + pull_request: + push: + branches: + - main + +jobs: + release: + uses: bgd-labs/github-workflows/.github/workflows/release.yml@main + release-node: + uses: bgd-labs/github-workflows/.github/workflows/release-node.yml@main + secrets: + NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} diff --git a/main.ts b/main.ts index 7b8d3c8..ea0af6a 100644 --- a/main.ts +++ b/main.ts @@ -46,8 +46,7 @@ type CommonOptions = { enterArtifactPath: boolean; artifactPath?: string; // - pool?: string; - aclManagerAddress?: string; + pool: string; userAddress?: string; keepAlive?: boolean | string; }; @@ -58,6 +57,8 @@ interface SharedQuestion { // shared type: "string" | "list" | "confirm" | "fuzzypath" | "number"; default?: string | number | boolean; + // additional property for option in --help which requires static options + staticChoices?: string[] | number[]; choices?: string[] | number[] | ((args: Options) => string[]); when?: | ((args: Options) => boolean | undefined) @@ -200,7 +201,8 @@ const questions: { [key: string]: InquirerQuestion | YargsQuestion } = { pool: { type: "list", message: "Select the target pool", - inquirerOnly: true, + describe: "Select the target pool", + staticChoices: Object.keys(allConfigs), choices: (args) => { return Object.keys(allConfigs).filter( (key) => (allConfigs as any)[key].CHAIN_ID === Number(args.networkId) @@ -212,17 +214,6 @@ const questions: { [key: string]: InquirerQuestion | YargsQuestion } = { Number(args.networkId) !== ChainId.mainnet ), }, - aclManagerAddress: { - message: "Enter ACL manager address of the target pool", - describe: "ACL manager address", - type: "string", - when: (args) => - !!( - (args.artifactPath || args.proposalId || args.payloadAddress) && - !args.pool && - Number(args.networkId) !== ChainId.mainnet - ), - }, userAddress: { message: "Enter an address you want to fund with 1000 native currency on the fork?", @@ -269,11 +260,19 @@ function getOptions(options: { previous, [ name, - { choices, default: _default, demandOption, describe, type, coerce }, + { + staticChoices, + choices, + default: _default, + demandOption, + describe, + type, + coerce, + }, ] ) => { previous[name] = { - choices, + choices: staticChoices || choices || undefined, default: _default, demandOption, describe, @@ -337,9 +336,6 @@ function getName(options: Options) { keepAlive: argv.keepAlive === true || argv.keepAlive === "true", })); const fork = forkIdToForkParams({ forkId }); - const aclManagerAddress = argv.pool - ? (allConfigs as any)[argv.pool].ACL_MANAGER - : argv.aclManagerAddress; if (argv.proposalId) { await passAndExecuteProposal({ @@ -360,7 +356,7 @@ function getName(options: Options) { await executeL2Payload({ payloadAddress: argv.payloadAddress, provider: fork.provider, - aclManagerAddress, + pool: argv.pool, }); } } else if (argv.artifactPath) { @@ -381,7 +377,7 @@ function getName(options: Options) { await executeL2Payload({ payloadAddress, provider: fork.provider, - aclManagerAddress, + pool: argv.pool, }); } } diff --git a/package.json b/package.json index baa04a3..1dfb6d0 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "scripts": { "start": "npm run build && node dist/index.js", "build": "ncc build ./main.ts -o dist --minify", - "publish:local": "npm run build && npm pack && npm i -g bgd-labs-aave-tenderly-cli-0.0.8.tgz && rm bgd-labs-aave-tenderly-cli-0.0.8.tgz" + "publish:local": "npm run build && npm pack && npm i -g bgd-labs-aave-tenderly-cli-0.0.8.tgz && rm bgd-labs-aave-tenderly-cli-0.0.8.tgz", + "ci:publish": "npm run build && npm publish --access=public" }, "repository": { "type": "git", diff --git a/src/l2Gov.ts b/src/l2Gov.ts index 444df38..d663408 100644 --- a/src/l2Gov.ts +++ b/src/l2Gov.ts @@ -1,12 +1,29 @@ import { providers, BigNumber, utils, Contract } from "ethers"; -import { keccak256, toUtf8Bytes, defaultAbiCoder } from "ethers/lib/utils"; +import { + keccak256, + toUtf8Bytes, + defaultAbiCoder, + formatBytes32String, +} from "ethers/lib/utils"; +import * as allConfigs from "@bgd-labs/aave-address-book"; + interface DefaultInterface { provider: providers.StaticJsonRpcProvider; } interface ExecuteL2Payload extends DefaultInterface { - aclManagerAddress: string; payloadAddress: string; + pool: string; +} + +function getPoolAdminSlot() { + const slot = "0x2"; + const role = formatBytes32String("POOL_ADMIN"); + const encodedSlot = defaultAbiCoder.encode( + ["bytes32", "uint256"], + [role, slot] + ); + return keccak256(encodedSlot); } function getACLRoleAddressSlot(_role: string, address: string) { @@ -27,38 +44,43 @@ function getACLRoleAddressSlot(_role: string, address: string) { } export async function executeL2Payload({ - aclManagerAddress, payloadAddress, provider, + pool, }: ExecuteL2Payload) { + const config = allConfigs[pool as keyof typeof allConfigs]; + const aclManagerAddress = (config as typeof allConfigs.AaveV3Optimism) + .ACL_MANAGER; + const isV2 = !aclManagerAddress; try { - const listingAdminSlot = getACLRoleAddressSlot( - "ASSET_LISTING_ADMIN", - payloadAddress - ); - await provider.send("tenderly_setStorageAt", [ - aclManagerAddress, - listingAdminSlot, - utils.hexZeroPad(BigNumber.from(1).toHexString(), 32), - ]); - console.log("added role ASSET_LISTING_ADMIN"); + if (aclManagerAddress) { + const listingAdminSlot = getACLRoleAddressSlot( + "ASSET_LISTING_ADMIN", + payloadAddress + ); + await provider.send("tenderly_setStorageAt", [ + aclManagerAddress, + listingAdminSlot, + utils.hexZeroPad(BigNumber.from(1).toHexString(), 32), + ]); + console.log("added role ASSET_LISTING_ADMIN"); - const riskAdminSlot = getACLRoleAddressSlot("RISK_ADMIN", payloadAddress); - await provider.send("tenderly_setStorageAt", [ - aclManagerAddress, - riskAdminSlot, - utils.hexZeroPad(BigNumber.from(1).toHexString(), 32), - ]); - console.log("added role RISK_ADMIN"); - - const poolAdminSlot = getACLRoleAddressSlot("POOL_ADMIN", payloadAddress); - await provider.send("tenderly_setStorageAt", [ - aclManagerAddress, - poolAdminSlot, - utils.hexZeroPad(BigNumber.from(1).toHexString(), 32), - ]); - console.log("added role POOL_ADMIN"); + const riskAdminSlot = getACLRoleAddressSlot("RISK_ADMIN", payloadAddress); + await provider.send("tenderly_setStorageAt", [ + aclManagerAddress, + riskAdminSlot, + utils.hexZeroPad(BigNumber.from(1).toHexString(), 32), + ]); + console.log("added role RISK_ADMIN"); + const poolAdminSlot = getACLRoleAddressSlot("POOL_ADMIN", payloadAddress); + await provider.send("tenderly_setStorageAt", [ + aclManagerAddress, + poolAdminSlot, + utils.hexZeroPad(BigNumber.from(1).toHexString(), 32), + ]); + console.log("added role POOL_ADMIN"); + } const payload = new Contract( payloadAddress, [ @@ -84,10 +106,24 @@ export async function executeL2Payload({ // therefore we check if we can fetch owner and set the owner as signer try { const owner = await payload.owner(); + if (isV2) { + console.log("### WARNING ###"); + console.log("Cannot simulate l2 proposals with owner guard"); + } const payloadWithOwner = payload.connect(provider.getSigner(owner)); await payloadWithOwner.execute(); } catch (e) { - await payload.execute(); + if (isV2) { + const adminSlot = getPoolAdminSlot(); + await provider.send("tenderly_setStorageAt", [ + config.POOL_ADDRESSES_PROVIDER, + adminSlot, + utils.hexZeroPad(payloadAddress, 32), + ]); + await payload.execute(); + } else { + await payload.execute(); + } } console.log("executed payload");