From 5620c6a82ad0d906e9bafcb39954539cdc43f44e Mon Sep 17 00:00:00 2001 From: travis Date: Thu, 17 Nov 2022 12:06:28 -0800 Subject: [PATCH] more scripts --- .gitignore | 6 +- README.md | 4 + SAMPLE.env | 6 +- flattened.sol | 5 +- lib/forge-std | 2 +- lib/openzeppelin-contracts | 2 +- lib/solmate | 2 +- node-scripts/logger.ts | 34 ++++ node-scripts/package-lock.json | 186 +++++++++++++++++++ node-scripts/package.json | 15 ++ node-scripts/validate-msig-add-validators.ts | 88 +++++++++ script/DepositDataToCalldata.s.sol | 2 +- 12 files changed, 344 insertions(+), 8 deletions(-) create mode 100644 node-scripts/logger.ts create mode 100644 node-scripts/package-lock.json create mode 100644 node-scripts/package.json create mode 100644 node-scripts/validate-msig-add-validators.ts diff --git a/.gitignore b/.gitignore index d6f32ee..8ca1e3f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,8 @@ out/ .env broadcast/ .github -.VSCodeCounter \ No newline at end of file +.VSCodeCounter +deposits/ +deposits/*.json +node-scripts/node_modules +bin \ No newline at end of file diff --git a/README.md b/README.md index 85cfd3e..4d9d15e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ If you need to fork mainnet, single test contract Verbosely test a single contract while forking mainnet or ```source .env && forge test --fork-url $MAINNET_RPC_URL -m test_frxETHMinter_submitAndDepositRegular -vvvvv``` for single test verbosity level 5 + +### Other Scipts +tsx validate-msig-add-validators.ts + ### Slither 1) Install [slither](https://github.com/crytic/slither#how-to-install) 2) Slither a single contract diff --git a/SAMPLE.env b/SAMPLE.env index c7d5b16..148a697 100644 --- a/SAMPLE.env +++ b/SAMPLE.env @@ -39,4 +39,8 @@ VALIDATOR_GOERLI_SIG1="" VALIDATOR_GOERLI_DDROOT1="" VALIDATOR_GOERLI_PUBKEY2="" VALIDATOR_GOERLI_SIG2="" -VALIDATOR_GOERLI_DDROOT2="" \ No newline at end of file +VALIDATOR_GOERLI_DDROOT2="" + +# Deposit Data +# ================================= +DEPOSIT_DATA_PATH="" \ No newline at end of file diff --git a/flattened.sol b/flattened.sol index 332f955..4b55ea3 100644 --- a/flattened.sol +++ b/flattened.sol @@ -1,5 +1,6 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.8.0; + // ==================================================================== // | ______ _______ | diff --git a/lib/forge-std b/lib/forge-std index cb69e9c..2a2ce36 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit cb69e9c07fbd002819c8c6c8db3caeab76b90d6b +Subproject commit 2a2ce3692b8c1523b29de3ec9d961ee9fbbc43a6 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 561d106..8d908fe 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 561d1061fc568f04c7a65853538e834a889751e8 +Subproject commit 8d908fe2c20503b05f888dd9f702e3fa6fa65840 diff --git a/lib/solmate b/lib/solmate index bff24e8..62e0943 160000 --- a/lib/solmate +++ b/lib/solmate @@ -1 +1 @@ -Subproject commit bff24e835192470ed38bf15dbed6084c2d723ace +Subproject commit 62e0943c013a66b2720255e2651450928f4eed7a diff --git a/node-scripts/logger.ts b/node-scripts/logger.ts new file mode 100644 index 0000000..58d5551 --- /dev/null +++ b/node-scripts/logger.ts @@ -0,0 +1,34 @@ +/* eslint-disable no-console */ +import chalk from 'chalk'; + +export interface ILogger { + info(...args: any[]): void; + warn(...args: any[]): void; + error(...args: any[]): void; +} + +const logInternal = (method: 'info' | 'warn' | 'error', symbol: string, ...args: any[]) => { + const now = new Date(); + const timestamp = chalk.gray(`[${now.toLocaleTimeString()}]`); + + console[method](timestamp, symbol, ...args); +}; + +const spacer = ' '; +const frax = `${spacer}\u00A4${spacer}`; +const square = `${spacer}\u25A0${spacer}`; +const triangle = `${spacer}\u25B2${spacer}`; + +export const logger: ILogger = { + info(...args: any[]) { + logInternal('info', chalk.green(square), ...args); + }, + + warn(...args: any[]) { + logInternal('warn', chalk.yellow(triangle), ...args); + }, + + error(...args: any[]) { + logInternal('error', chalk.bgRedBright.whiteBright(frax), ...args); + }, +}; \ No newline at end of file diff --git a/node-scripts/package-lock.json b/node-scripts/package-lock.json new file mode 100644 index 0000000..a7db23a --- /dev/null +++ b/node-scripts/package-lock.json @@ -0,0 +1,186 @@ +{ + "name": "frxeth-node-scripts", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "frxeth-node-scripts", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.1.3", + "chalk": "^5.1.2" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz", + "integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + } + }, + "dependencies": { + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.1.3.tgz", + "integrity": "sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "chalk": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + } + } +} diff --git a/node-scripts/package.json b/node-scripts/package.json new file mode 100644 index 0000000..f381b44 --- /dev/null +++ b/node-scripts/package.json @@ -0,0 +1,15 @@ +{ + "name": "frxeth-node-scripts", + "version": "1.0.0", + "description": "Node scripts for frxETH", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.1.3", + "chalk": "^5.1.2" + } +} diff --git a/node-scripts/validate-msig-add-validators.ts b/node-scripts/validate-msig-add-validators.ts new file mode 100644 index 0000000..1016825 --- /dev/null +++ b/node-scripts/validate-msig-add-validators.ts @@ -0,0 +1,88 @@ +import { logger } from './logger'; +import Axios from 'axios'; + +(async () => { + logger.info('[Start] Validating MSIG validators to add data...'); + + const { validators } = await Axios.get<{ validators: { publicKey: string; statusCode: string }[] }>( + 'https://api.frax.finance/v2/frxeth/validators', + ).then((r) => r.data); + + logger.info(`Got ${validators.length} validators from the API`); + + const { results } = await Axios.get<{ + results: { + type: string; + transaction?: { id: string; executionInfo: { nonce: number }; txInfo: { methodName: string; to: { value: string } } }; + }[]; + }>('https://safe-client.safe.global/v1/chains/1/safes/0x8306300ffd616049FD7e4b0354a64Da835c1A81C/transactions/queued').then( + (r) => r.data, + ); + + const addValidatorTransactions = results.filter((f) => f.transaction?.txInfo.methodName === 'addValidators'); + + logger.info( + `Got ${results.filter((f) => f.type === 'TRANSACTION').length} transactions in the MSIG queue, ${ + addValidatorTransactions.length + } of which are addValidators`, + ); + + const publicKeysToAdd: { key: string; nonce: number }[] = []; + for (const tx of addValidatorTransactions) { + if (tx.transaction) { + const { txData } = await Axios.get<{ + txData: { dataDecoded: { method: string; parameters: { name: string; value: [string, string, string[]] }[] } }; + }>(`https://safe-client.safe.global/v1/chains/1/transactions/${tx.transaction?.id}`).then((r) => r.data); + + const validatorParams = txData.dataDecoded.parameters[0]; + + if (tx.transaction.txInfo.to.value !== '0xbAFA44EFE7901E04E39Dad13167D089C559c1138') { + logger.error( + `[#${tx.transaction.executionInfo.nonce}] Wrong to_address of ${tx.transaction.txInfo.to.value}, should be 0xbAFA44EFE7901E04E39Dad13167D089C559c1138`, + ); + continue; + } + + if (txData.dataDecoded.method === 'addValidators' && validatorParams.name === 'validatorArray') { + const txKeysToAdd = validatorParams.value.map((x) => ({ key: x[0], nonce: tx.transaction?.executionInfo.nonce ?? -1 })); + publicKeysToAdd.push(...txKeysToAdd); + logger.info(`For tx #${tx.transaction.executionInfo.nonce}, we got ${txKeysToAdd.length} validator public keys to add`); + } + } + } + + logger.info(`Got a total of ${publicKeysToAdd.length} public keys to add`); + + const uniquePublicKeys = [...new Set(publicKeysToAdd.map((x) => x.key))]; + + if (uniquePublicKeys.length !== publicKeysToAdd.length) { + logger.error('Duplicate keys found in enqueued transactions'); + return; + } else { + logger.info(`There are no duplicate keys in the enqueued transactions`); + } + + const keyStatuses = publicKeysToAdd.map((x) => ({ + key: x.key, + status: validators.find((f) => f.publicKey === x.key)?.statusCode, + nonce: x.nonce, + })); + + const keysWithIssues = keyStatuses.filter((f) => f.status !== 'uninitialized'); + + if (keysWithIssues.length > 0) { + for (const item of keysWithIssues) { + if (!item.status) { + logger.error(`[#${item.nonce}] ${item.key} is missing from the API`); + } else { + logger.error(`[#${item.nonce}] ${item.key} has status ${item.status}`); + } + } + + logger.error(`Got ${keysWithIssues.length} total keys with issues`); + } else { + logger.info(`All ${keyStatuses.length} keys are good to go`); + } + + logger.info('[End] Validated MSIG validators to add data'); +})(); \ No newline at end of file diff --git a/script/DepositDataToCalldata.s.sol b/script/DepositDataToCalldata.s.sol index 0aeac0d..ffa4818 100644 --- a/script/DepositDataToCalldata.s.sol +++ b/script/DepositDataToCalldata.s.sol @@ -18,7 +18,7 @@ pragma solidity ^0.8.0; 4. Use the final log output as data in a transaction to the frxETHMinter /////////////////////////////////////////////////////////////////////////////////////////*/ -import { stdJson } from "forge-std/stdJson.sol"; +import { stdJson } from "forge-std/StdJson.sol"; import { Script } from "forge-std/Script.sol"; import { Test } from "forge-std/Test.sol"; import { frxETHMinter, OperatorRegistry } from "../src/frxETHMinter.sol";