From 0e8e794bff33ac99e5d117989f9c3a6dc27bd123 Mon Sep 17 00:00:00 2001 From: Nelson Taveras <4562733+nvtaveras@users.noreply.github.com> Date: Wed, 31 Jul 2024 23:51:48 -0400 Subject: [PATCH 1/7] feat: add get-time-lock-id script --- package-lock.json | 159 ++++++++++++++++++++++++++++++++++ package.json | 2 + src/utils/get-time-lock-id.ts | 78 +++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 src/utils/get-time-lock-id.ts diff --git a/package-lock.json b/package-lock.json index 98646cc..c63fce7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@types/eslint__js": "^8.42.3", "eslint": "^9.7.0", "rimraf": "^6.0.1", + "ts-node": "^10.9.2", "typescript": "^5.5.3", "typescript-eslint": "^8.0.0-alpha.51" } @@ -129,6 +130,18 @@ "node": ">=4" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@discordjs/builders": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.8.2.tgz", @@ -614,6 +627,31 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@js-sdsl/ordered-map": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", @@ -833,6 +871,30 @@ "node": ">=18.0.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -1349,6 +1411,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", @@ -1434,6 +1508,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1710,6 +1790,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1779,6 +1865,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3185,6 +3280,12 @@ "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -4377,6 +4478,49 @@ "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -4512,6 +4656,12 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4744,6 +4894,15 @@ "node": ">=12" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index f498244..a39a1fb 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "deploy:via:tf": "terraform -chdir=infra apply", "gcp-build": "npm run build", "generate:env": "terraform -chdir=infra apply -target=local_file.env_file", + "get-time-lock-id": "ts-node src/utils/get-time-lock-id.ts", "logs": "./get-logs.sh", "logs:url": "./get-logs-url.sh", "prestart": "npm run build", @@ -38,6 +39,7 @@ "@types/eslint__js": "^8.42.3", "eslint": "^9.7.0", "rimraf": "^6.0.1", + "ts-node": "^10.9.2", "typescript": "^5.5.3", "typescript-eslint": "^8.0.0-alpha.51" } diff --git a/src/utils/get-time-lock-id.ts b/src/utils/get-time-lock-id.ts new file mode 100644 index 0000000..72bbc19 --- /dev/null +++ b/src/utils/get-time-lock-id.ts @@ -0,0 +1,78 @@ +import { + createPublicClient, + encodeAbiParameters, + parseAbiParameters, + http, + decodeEventLog, + TransactionReceipt, + PublicClient, + keccak256, +} from "viem"; +import { mainnet } from "viem/chains"; +import GovernorABI from "../governor-abi"; + +async function getTransactionReceiptAndDecodeLogs( + client: PublicClient, + txHash: `0x${string}`, +) { + try { + const receipt: TransactionReceipt = await client.getTransactionReceipt({ + hash: txHash, + }); + + for (const log of receipt.logs) { + try { + const decodedLog = decodeEventLog({ + abi: GovernorABI, + data: log.data, + topics: log.topics, + }); + + if (decodedLog.eventName !== "ProposalCreated") { + continue; + } + + console.log("Decoded event log", decodedLog); + + // @ts-ignore + const { targets, values, calldatas, description } = decodedLog.args; + const descriptionHash = keccak256(Buffer.from(description)); + + const proposalId = keccak256( + encodeAbiParameters( + parseAbiParameters( + "address[], uint256[], bytes[], uint256, bytes32", + ), + // _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); + [targets, values, calldatas, 0n, descriptionHash], + ), + ); + + console.log("Proposal timeLockId:", proposalId.toString()); + } catch (error) { + console.error("Error decoding log:", error); + } + } + } catch (error) { + console.error("Error fetching transaction receipt:", error); + } +} + +async function main() { + if (process.argv.length !== 4) { + console.error("Usage: npm run get-time-lock-id -- "); + process.exit(1); + } + + const RPC_URL = process.argv[2]; + const TX_HASH = process.argv[3] as `0x${string}`; + + const client = createPublicClient({ + chain: mainnet, + transport: http(RPC_URL), + }); + + await getTransactionReceiptAndDecodeLogs(client, TX_HASH); +} + +main().then(() => {}); From f5df43a26568f225d13213b2949b7844127af31a Mon Sep 17 00:00:00 2001 From: Nelson Taveras <4562733+nvtaveras@users.noreply.github.com> Date: Wed, 31 Jul 2024 23:56:46 -0400 Subject: [PATCH 2/7] chore: linter --- src/utils/get-time-lock-id.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/get-time-lock-id.ts b/src/utils/get-time-lock-id.ts index 72bbc19..aacb06a 100644 --- a/src/utils/get-time-lock-id.ts +++ b/src/utils/get-time-lock-id.ts @@ -34,7 +34,6 @@ async function getTransactionReceiptAndDecodeLogs( console.log("Decoded event log", decodedLog); - // @ts-ignore const { targets, values, calldatas, description } = decodedLog.args; const descriptionHash = keccak256(Buffer.from(description)); @@ -75,4 +74,6 @@ async function main() { await getTransactionReceiptAndDecodeLogs(client, TX_HASH); } -main().then(() => {}); +main().catch((error: unknown) => { + console.error("Error while getting time-lock id", error); +}); From fda4892ae4e3fa445e572717d147231aa8af38ad Mon Sep 17 00:00:00 2001 From: Nelson Taveras <4562733+nvtaveras@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:17:36 -0400 Subject: [PATCH 3/7] chore: remove ts-node and script from package.json --- package-lock.json | 159 ---------------------------------------------- package.json | 2 - 2 files changed, 161 deletions(-) diff --git a/package-lock.json b/package-lock.json index c63fce7..98646cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,6 @@ "@types/eslint__js": "^8.42.3", "eslint": "^9.7.0", "rimraf": "^6.0.1", - "ts-node": "^10.9.2", "typescript": "^5.5.3", "typescript-eslint": "^8.0.0-alpha.51" } @@ -130,18 +129,6 @@ "node": ">=4" } }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@discordjs/builders": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.8.2.tgz", @@ -627,31 +614,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, "node_modules/@js-sdsl/ordered-map": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", @@ -871,30 +833,6 @@ "node": ">=18.0.0" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -1411,18 +1349,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", @@ -1508,12 +1434,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1790,12 +1710,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1865,15 +1779,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3280,12 +3185,6 @@ "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -4478,49 +4377,6 @@ "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -4656,12 +4512,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -4894,15 +4744,6 @@ "node": ">=12" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index a39a1fb..f498244 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "deploy:via:tf": "terraform -chdir=infra apply", "gcp-build": "npm run build", "generate:env": "terraform -chdir=infra apply -target=local_file.env_file", - "get-time-lock-id": "ts-node src/utils/get-time-lock-id.ts", "logs": "./get-logs.sh", "logs:url": "./get-logs-url.sh", "prestart": "npm run build", @@ -39,7 +38,6 @@ "@types/eslint__js": "^8.42.3", "eslint": "^9.7.0", "rimraf": "^6.0.1", - "ts-node": "^10.9.2", "typescript": "^5.5.3", "typescript-eslint": "^8.0.0-alpha.51" } From a3e6d1c9420ca9cd464af0ff2164d33c4a884cdc Mon Sep 17 00:00:00 2001 From: Nelson Taveras <4562733+nvtaveras@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:13:18 -0400 Subject: [PATCH 4/7] feat: include time-lock-id in notification msg --- src/index.ts | 14 ++++- src/parse-transaction-receipts.ts | 3 + src/send-discord-notification.ts | 5 ++ src/send-telegram-notification.ts | 2 + src/types.ts | 1 - src/utils/get-time-lock-id.ts | 95 ++++++------------------------- 6 files changed, 39 insertions(+), 81 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4074d1b..d3b0fd2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,8 +34,18 @@ export const watchdogNotifier: HttpFunction = async ( for (const parsedEvent of parsedEvents) { switch (parsedEvent.event.eventName) { case EventType.ProposalCreated: - await sendDiscordNotification(parsedEvent.event, parsedEvent.txHash); - await sendTelegramNotification(parsedEvent.event, parsedEvent.txHash); + await sendDiscordNotification( + parsedEvent.event, + // eslint-disable-next-line + parsedEvent.timeLockId!, + parsedEvent.txHash, + ); + await sendTelegramNotification( + parsedEvent.event, + // eslint-disable-next-line + parsedEvent.timeLockId!, + parsedEvent.txHash, + ); break; case EventType.MedianUpdated: // Acts a health check/heartbeat for the service, as it's a frequently emitted event diff --git a/src/parse-transaction-receipts.ts b/src/parse-transaction-receipts.ts index d869a2b..9e2f0ae 100644 --- a/src/parse-transaction-receipts.ts +++ b/src/parse-transaction-receipts.ts @@ -12,6 +12,7 @@ import isHealthCheckEvent from "./utils/is-health-check-event.js"; import isProposalCreatedEvent from "./utils/is-proposal-created-event.js"; import isTransactionReceipt from "./utils/is-transaction-receipt.js"; import SortedOraclesABI from "./sorted-oracles-abi.js"; +import getProposalTimeLockId from "./utils/get-time-lock-id.js"; /** * Parse request body containing raw transaction receipts @@ -21,6 +22,7 @@ export default function parseTransactionReceipts( ): { block?: number; event: ProposalCreatedEvent | HealthCheckEvent; + timeLockId?: string; txHash: string; }[] { const result = []; @@ -72,6 +74,7 @@ export default function parseTransactionReceipts( result.push({ event, + timeLockId: getProposalTimeLockId(event), txHash: log.transactionHash, }); break; diff --git a/src/send-discord-notification.ts b/src/send-discord-notification.ts index ee7a234..39cb3b8 100644 --- a/src/send-discord-notification.ts +++ b/src/send-discord-notification.ts @@ -5,6 +5,7 @@ import type { ProposalCreatedEvent } from "./types"; export default async function sendDiscordNotification( event: ProposalCreatedEvent, + timeLockId: string, txHash: string, ) { const { title, description } = JSON.parse(event.args.description) as { @@ -28,6 +29,10 @@ export default async function sendDiscordNotification( name: "Transaction", value: `https://celoscan.io/tx/${txHash}`, }) + .addFields({ + name: "Time Lock ID", + value: timeLockId, + }) .setColor(0xa6e5f6); const discordWebhookClient = new WebhookClient({ diff --git a/src/send-telegram-notification.ts b/src/send-telegram-notification.ts index bd38043..a92fd01 100644 --- a/src/send-telegram-notification.ts +++ b/src/send-telegram-notification.ts @@ -4,6 +4,7 @@ import { ProposalCreatedEvent } from "./types"; export default async function sendTelegramNotification( event: ProposalCreatedEvent, + timeLockId: string, txHash: string, ) { const botToken = await getSecret(config.TELEGRAM_BOT_TOKEN_SECRET_ID); @@ -19,6 +20,7 @@ export default async function sendTelegramNotification( Proposer: `https://celoscan.io/address/${event.args.proposer}`, Event: event.eventName, Transaction: `https://celoscan.io/tx/${txHash}`, + "Time Lock ID": timeLockId, Description: description, }; diff --git a/src/types.ts b/src/types.ts index 47390d4..0c5934e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -50,7 +50,6 @@ export interface ProposalCreatedEvent { export interface HealthCheckEvent { eventName: EventType.MedianUpdated; - block: number; args: { token: `0x${string}`; value: bigint; diff --git a/src/utils/get-time-lock-id.ts b/src/utils/get-time-lock-id.ts index aacb06a..5954f86 100644 --- a/src/utils/get-time-lock-id.ts +++ b/src/utils/get-time-lock-id.ts @@ -1,79 +1,18 @@ -import { - createPublicClient, - encodeAbiParameters, - parseAbiParameters, - http, - decodeEventLog, - TransactionReceipt, - PublicClient, - keccak256, -} from "viem"; -import { mainnet } from "viem/chains"; -import GovernorABI from "../governor-abi"; - -async function getTransactionReceiptAndDecodeLogs( - client: PublicClient, - txHash: `0x${string}`, -) { - try { - const receipt: TransactionReceipt = await client.getTransactionReceipt({ - hash: txHash, - }); - - for (const log of receipt.logs) { - try { - const decodedLog = decodeEventLog({ - abi: GovernorABI, - data: log.data, - topics: log.topics, - }); - - if (decodedLog.eventName !== "ProposalCreated") { - continue; - } - - console.log("Decoded event log", decodedLog); - - const { targets, values, calldatas, description } = decodedLog.args; - const descriptionHash = keccak256(Buffer.from(description)); - - const proposalId = keccak256( - encodeAbiParameters( - parseAbiParameters( - "address[], uint256[], bytes[], uint256, bytes32", - ), - // _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); - [targets, values, calldatas, 0n, descriptionHash], - ), - ); - - console.log("Proposal timeLockId:", proposalId.toString()); - } catch (error) { - console.error("Error decoding log:", error); - } - } - } catch (error) { - console.error("Error fetching transaction receipt:", error); - } +import { encodeAbiParameters, parseAbiParameters, keccak256 } from "viem"; + +import { ProposalCreatedEvent } from "../types"; + +export default function getProposalTimeLockId( + event: ProposalCreatedEvent, +): string { + const { targets, values, calldatas, description } = event.args; + const descriptionHash = keccak256(Buffer.from(description)); + + return keccak256( + encodeAbiParameters( + parseAbiParameters("address[], uint256[], bytes[], uint256, bytes32"), + // _timelockIds[proposalId] = _timelock.hashOperationBatch(targets, values, calldatas, 0, descriptionHash); + [targets, values, calldatas, 0n, descriptionHash], + ), + ); } - -async function main() { - if (process.argv.length !== 4) { - console.error("Usage: npm run get-time-lock-id -- "); - process.exit(1); - } - - const RPC_URL = process.argv[2]; - const TX_HASH = process.argv[3] as `0x${string}`; - - const client = createPublicClient({ - chain: mainnet, - transport: http(RPC_URL), - }); - - await getTransactionReceiptAndDecodeLogs(client, TX_HASH); -} - -main().catch((error: unknown) => { - console.error("Error while getting time-lock id", error); -}); From 6156e399a2b71aeded4e051316ffbc519771d393 Mon Sep 17 00:00:00 2001 From: Philip Paetz Date: Tue, 6 Aug 2024 10:34:45 +0200 Subject: [PATCH 5/7] fix: removed eslint-disable comment via assert statement --- src/index.ts | 20 +++++++++++--------- src/parse-transaction-receipts.ts | 9 ++++++--- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/index.ts b/src/index.ts index d3b0fd2..41518d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,16 +1,14 @@ -import assert from "assert/strict"; - -// Types import type { HttpFunction, Request, Response, } from "@google-cloud/functions-framework"; -import { EventType } from "./types.js"; +import assert from "assert/strict"; import parseTransactionReceipts from "./parse-transaction-receipts"; import sendDiscordNotification from "./send-discord-notification"; import sendTelegramNotification from "./send-telegram-notification"; -import { isFromQuicknode, hasAuthToken } from "./validate-request-origin"; +import { EventType } from "./types.js"; +import { hasAuthToken, isFromQuicknode } from "./validate-request-origin"; export const watchdogNotifier: HttpFunction = async ( req: Request, @@ -34,23 +32,27 @@ export const watchdogNotifier: HttpFunction = async ( for (const parsedEvent of parsedEvents) { switch (parsedEvent.event.eventName) { case EventType.ProposalCreated: + assert(parsedEvent.timeLockId, "Time lock ID is missing"); + await sendDiscordNotification( parsedEvent.event, - // eslint-disable-next-line - parsedEvent.timeLockId!, + parsedEvent.timeLockId, parsedEvent.txHash, ); + await sendTelegramNotification( parsedEvent.event, - // eslint-disable-next-line - parsedEvent.timeLockId!, + parsedEvent.timeLockId, parsedEvent.txHash, ); + break; + case EventType.MedianUpdated: // Acts a health check/heartbeat for the service, as it's a frequently emitted event console.info("[HealthCheck]: Block", parsedEvent.block); break; + default: assert( false, diff --git a/src/parse-transaction-receipts.ts b/src/parse-transaction-receipts.ts index 9e2f0ae..d2f39c6 100644 --- a/src/parse-transaction-receipts.ts +++ b/src/parse-transaction-receipts.ts @@ -5,14 +5,14 @@ import { decodeEventLog } from "viem"; // Internal import GovernorABI from "./governor-abi.js"; +import SortedOraclesABI from "./sorted-oracles-abi.js"; import { EventType, HealthCheckEvent, ProposalCreatedEvent } from "./types.js"; -import hasLogs from "./utils/has-logs.js"; import getEventByTopic from "./utils/get-event-by-topic.js"; +import getProposalTimeLockId from "./utils/get-time-lock-id.js"; +import hasLogs from "./utils/has-logs.js"; import isHealthCheckEvent from "./utils/is-health-check-event.js"; import isProposalCreatedEvent from "./utils/is-proposal-created-event.js"; import isTransactionReceipt from "./utils/is-transaction-receipt.js"; -import SortedOraclesABI from "./sorted-oracles-abi.js"; -import getProposalTimeLockId from "./utils/get-time-lock-id.js"; /** * Parse request body containing raw transaction receipts @@ -60,6 +60,7 @@ export default function parseTransactionReceipts( // It can happen that a single transaction fires multiple events, // some of which we are not interested in continue; + case EventType.ProposalCreated: { const event = decodeEventLog({ abi: GovernorABI, @@ -79,6 +80,7 @@ export default function parseTransactionReceipts( }); break; } + case EventType.MedianUpdated: { const event = decodeEventLog({ abi: SortedOraclesABI, @@ -98,6 +100,7 @@ export default function parseTransactionReceipts( }); break; } + default: assert( false, From 8bfd8924cc58b9f29786927364bef3260ca7deec Mon Sep 17 00:00:00 2001 From: Philip Paetz Date: Tue, 6 Aug 2024 10:42:02 +0200 Subject: [PATCH 6/7] chore: "Time Lock" => "Timelock" that's how openzeppelin spells it everywhere :) --- src/index.ts | 6 +++--- src/parse-transaction-receipts.ts | 6 +++--- src/send-discord-notification.ts | 6 +++--- src/send-telegram-notification.ts | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/index.ts b/src/index.ts index 41518d2..3c7b19b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,17 +32,17 @@ export const watchdogNotifier: HttpFunction = async ( for (const parsedEvent of parsedEvents) { switch (parsedEvent.event.eventName) { case EventType.ProposalCreated: - assert(parsedEvent.timeLockId, "Time lock ID is missing"); + assert(parsedEvent.timelockId, "Timelock ID is missing"); await sendDiscordNotification( parsedEvent.event, - parsedEvent.timeLockId, + parsedEvent.timelockId, parsedEvent.txHash, ); await sendTelegramNotification( parsedEvent.event, - parsedEvent.timeLockId, + parsedEvent.timelockId, parsedEvent.txHash, ); diff --git a/src/parse-transaction-receipts.ts b/src/parse-transaction-receipts.ts index d2f39c6..24a2e3e 100644 --- a/src/parse-transaction-receipts.ts +++ b/src/parse-transaction-receipts.ts @@ -8,7 +8,7 @@ import GovernorABI from "./governor-abi.js"; import SortedOraclesABI from "./sorted-oracles-abi.js"; import { EventType, HealthCheckEvent, ProposalCreatedEvent } from "./types.js"; import getEventByTopic from "./utils/get-event-by-topic.js"; -import getProposalTimeLockId from "./utils/get-time-lock-id.js"; +import getProposaltimelockId from "./utils/get-time-lock-id.js"; import hasLogs from "./utils/has-logs.js"; import isHealthCheckEvent from "./utils/is-health-check-event.js"; import isProposalCreatedEvent from "./utils/is-proposal-created-event.js"; @@ -22,7 +22,7 @@ export default function parseTransactionReceipts( ): { block?: number; event: ProposalCreatedEvent | HealthCheckEvent; - timeLockId?: string; + timelockId?: string; txHash: string; }[] { const result = []; @@ -75,7 +75,7 @@ export default function parseTransactionReceipts( result.push({ event, - timeLockId: getProposalTimeLockId(event), + timelockId: getProposaltimelockId(event), txHash: log.transactionHash, }); break; diff --git a/src/send-discord-notification.ts b/src/send-discord-notification.ts index 39cb3b8..5336483 100644 --- a/src/send-discord-notification.ts +++ b/src/send-discord-notification.ts @@ -5,7 +5,7 @@ import type { ProposalCreatedEvent } from "./types"; export default async function sendDiscordNotification( event: ProposalCreatedEvent, - timeLockId: string, + timelockId: string, txHash: string, ) { const { title, description } = JSON.parse(event.args.description) as { @@ -30,8 +30,8 @@ export default async function sendDiscordNotification( value: `https://celoscan.io/tx/${txHash}`, }) .addFields({ - name: "Time Lock ID", - value: timeLockId, + name: "Timelock ID", + value: timelockId, }) .setColor(0xa6e5f6); diff --git a/src/send-telegram-notification.ts b/src/send-telegram-notification.ts index a92fd01..f54a9b1 100644 --- a/src/send-telegram-notification.ts +++ b/src/send-telegram-notification.ts @@ -4,7 +4,7 @@ import { ProposalCreatedEvent } from "./types"; export default async function sendTelegramNotification( event: ProposalCreatedEvent, - timeLockId: string, + timelockId: string, txHash: string, ) { const botToken = await getSecret(config.TELEGRAM_BOT_TOKEN_SECRET_ID); @@ -20,7 +20,7 @@ export default async function sendTelegramNotification( Proposer: `https://celoscan.io/address/${event.args.proposer}`, Event: event.eventName, Transaction: `https://celoscan.io/tx/${txHash}`, - "Time Lock ID": timeLockId, + "Timelock ID": timelockId, Description: description, }; From 929b275cac7fe76c4bbc3efd9f1565809d6242f2 Mon Sep 17 00:00:00 2001 From: Philip Paetz Date: Tue, 6 Aug 2024 10:56:53 +0200 Subject: [PATCH 7/7] chore: add timelock operation id explainer --- src/utils/get-time-lock-id.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/utils/get-time-lock-id.ts b/src/utils/get-time-lock-id.ts index 5954f86..67e730f 100644 --- a/src/utils/get-time-lock-id.ts +++ b/src/utils/get-time-lock-id.ts @@ -1,7 +1,16 @@ -import { encodeAbiParameters, parseAbiParameters, keccak256 } from "viem"; +import { encodeAbiParameters, keccak256, parseAbiParameters } from "viem"; import { ProposalCreatedEvent } from "../types"; +/** + * Given a ProposalCreatedEvent, calculate the corresponding timelock operation ID. + * Governance Watchdogs need the timelock operation ID to veto queued proposals. + * + * The governor proposal ID and the timelock operation ID are not the same, which can + * be confusing. They use different hashing mechanisms to calculate their respective IDs: + * - Timelock Controller Operation IDs: https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/58fa0f81c4036f1a3b616fdffad2fd27e5d5ce21/contracts/governance/TimelockControllerUpgradeable.sol#L218 + * - Governor Proposal IDs: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/0a25c1940ca220686588c4af3ec526f725fe2582/contracts/governance/Governor.sol#L139 + */ export default function getProposalTimeLockId( event: ProposalCreatedEvent, ): string {