diff --git a/scripts/configChangePayloads/createSetQuorumSignatures.ts b/scripts/configChangePayloads/createSetQuorumSignatures.ts new file mode 100644 index 0000000..865ee3a --- /dev/null +++ b/scripts/configChangePayloads/createSetQuorumSignatures.ts @@ -0,0 +1,129 @@ +import { GcpKmsSigner } from 'ethers-gcp-kms-signer' +import { ethers } from 'ethers' +import { parse } from 'ts-command-line-args' +import { GcpKmsKey, parseKmsKeyIds } from './kms' +import { getChainIdForNetwork } from '@layerzerolabs/lz-definitions' + +import path from 'path' +import fs from 'fs' + +const PATH = path.join(__dirname) +const FILE_PATH = `${PATH}/quorum-change-payloads.json` + +/** + * This script creates signature payloads to be submitted by an Admin of the DVN contract + * that will change the quorum of the DVN contract + */ + +const args = parse({ + environment: { + alias: 'e', + type: String, + defaultValue: 'mainnet', + description: 'environment', + }, + chainNames: { + alias: 'c', + type: String, + description: 'comma separated list of chain names', + }, + oldQuorum: { + type: Number, + description: + 'old quorum, which is number of signatures required for change to happen', + }, + newQuorum: { + type: Number, + description: 'new quorum', + }, +}) + +const setQuorumFunctionSig = 'function setQuorum(uint64 _quorum)' +const EXPIRATION = Date.now() + 7 * 24 * 60 * 60 * 1000 // 1 week expiration from now + +const iface = new ethers.utils.Interface([setQuorumFunctionSig]) + +const getCallData = (newQuorum: number) => { + return iface.encodeFunctionData('setQuorum', [newQuorum]) +} + +const hashCallData = (target: string, callData: string, vId: string) => { + return ethers.utils.keccak256( + ethers.utils.solidityPack( + ['uint32', 'address', 'uint', 'bytes'], + [vId, target, EXPIRATION, callData], + ), + ) +} + +interface Signature { + signature: string + address: string +} + +const main = async () => { + const { environment, chainNames, oldQuorum, newQuorum } = args + const dvnAddresses = require(`./data/dvn-addresses-${environment}.json`) + const keyIds = require(`./data/kms-keyids-${environment}.json`) + const signers = await Promise.all( + keyIds.map(async (credentials: GcpKmsKey) => { + return new GcpKmsSigner(credentials) + }), + ) + const availableChainNames = chainNames.split(',') + + const results: { [chainName: string]: { [sendVersion: string]: any } } = {} + await Promise.all( + availableChainNames.map(async (chainName) => { + results[chainName] = results[chainName] || {} + const vId = getChainIdForNetwork(chainName, environment, '2') + const callData = getCallData(newQuorum) + const hash = hashCallData(dvnAddresses[chainName], callData, vId) + // sign + const signatures = await Promise.all( + signers.map(async (signer) => ({ + signature: await signer.signMessage( + ethers.utils.arrayify(hash), + ), + address: await signer.getAddress(), + })), + ) + + signatures.sort((a: Signature, b: Signature) => + a.address.localeCompare(b.address), + ) + const signaturesForQuorum = signatures.slice(0, oldQuorum) + const signaturePayload = ethers.utils.solidityPack( + signaturesForQuorum.map(() => 'bytes'), + signaturesForQuorum.map((s: Signature) => s.signature), + ) + + results[chainName] = { + args: { + target: dvnAddresses[chainName], + signatures: signaturePayload, + callData, + expiration: EXPIRATION, + vid: vId, + }, + info: { + signatures, + hashCallData: hash, + oldQuorum, + newQuorum, + }, + } + }), + ) + fs.writeFileSync(FILE_PATH, JSON.stringify(results)) + console.log(`Results written to: ${FILE_PATH}`) +} + +main() + .then(() => { + process.exit(0) + }) + .catch((err: any) => { + console.error(err) + process.exit(1) + }) diff --git a/scripts/configChangePayloads/data/dvn-addresses-testnet.json b/scripts/configChangePayloads/data/dvn-addresses-testnet.json new file mode 100644 index 0000000..0eaf7b8 --- /dev/null +++ b/scripts/configChangePayloads/data/dvn-addresses-testnet.json @@ -0,0 +1,4 @@ +{ + "bsc": "0xfb5257b22111ed53df225bd71bce8a6e27311170", + "avalanche": "0xd28fa1395a45bf2d30af5afded0c7487b156f705" +} diff --git a/scripts/configChangePayloads/data/kms-keyids-testnet.json b/scripts/configChangePayloads/data/kms-keyids-testnet.json new file mode 100644 index 0000000..024b91c --- /dev/null +++ b/scripts/configChangePayloads/data/kms-keyids-testnet.json @@ -0,0 +1,16 @@ +[ + { + "projectId": "gasolina-gcp-test", + "locationId": "global", + "keyRingId": "gasolinaKeyRing", + "keyId": "gasolinaKey1", + "keyVersion": "1" + }, + { + "projectId": "gasolina-gcp-test", + "locationId": "global", + "keyRingId": "gasolinaKeyRing", + "keyId": "gasolinaKey2", + "keyVersion": "1" + } +] diff --git a/scripts/configChangePayloads/kms.ts b/scripts/configChangePayloads/kms.ts new file mode 100644 index 0000000..6f923b1 --- /dev/null +++ b/scripts/configChangePayloads/kms.ts @@ -0,0 +1,18 @@ +/** + * Defines a GCP KMS Key. + */ +export interface GcpKmsKey { + projectId: string + locationId: string + keyRingId: string + keyId: string + keyVersion: string +} + +/** + * Utility to parse GCP KMS Keys from a file. + * @param {string} input + */ +export const parseKmsKeyIds = (input: string): GcpKmsKey[] => { + return JSON.parse(input) as GcpKmsKey[] +} diff --git a/scripts/configChangePayloads/package.json b/scripts/configChangePayloads/package.json new file mode 100644 index 0000000..2c99dc9 --- /dev/null +++ b/scripts/configChangePayloads/package.json @@ -0,0 +1,17 @@ +{ + "name": "@layerzerolabs/gasolina-gcp-scripts", + "version": "1.0.0", + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/providers": "^5.7.0", + "@typechain/ethers-v5": "^10.2.0", + "ethers": "^5.7.2", + "@layerzerolabs/lz-definitions": "^1.5.70", + "ethers-gcp-kms-signer": "^1.1.6", + "typechain": "^8.1.0" + }, + "devDependencies": { + "@types/node": "^20.16.10" + } +} diff --git a/scripts/configChangePayloads/quorum-change-payloads.json b/scripts/configChangePayloads/quorum-change-payloads.json new file mode 100644 index 0000000..f7886ee --- /dev/null +++ b/scripts/configChangePayloads/quorum-change-payloads.json @@ -0,0 +1,50 @@ +{ + "bsc": { + "args": { + "target": "0xfb5257b22111ed53df225bd71bce8a6e27311170", + "signatures": "0x3c5c5887993007e2042395d7224847444a4685c310282f63cb13d0431bb5f4b4320baf12485d4e967fa24503d8b90d4f593b28dce8f65f9bcfd497b115b38f691bf8cc39482552e0d198609bafa42cd4786dc85039f99f5ad24ddb64e5c61a3f563b26b509808e35659edbf310d5244ce8fb1914f3a2531b1fd5512d1817a20e6a1b", + "callData": "0x8585c9450000000000000000000000000000000000000000000000000000000000000001", + "expiration": 1734735312437, + "vid": "10102" + }, + "info": { + "signatures": [ + { + "signature": "0x3c5c5887993007e2042395d7224847444a4685c310282f63cb13d0431bb5f4b4320baf12485d4e967fa24503d8b90d4f593b28dce8f65f9bcfd497b115b38f691b", + "address": "0x6a73853254eadca287346cc69d32b5a24b18a063" + }, + { + "signature": "0xf8cc39482552e0d198609bafa42cd4786dc85039f99f5ad24ddb64e5c61a3f563b26b509808e35659edbf310d5244ce8fb1914f3a2531b1fd5512d1817a20e6a1b", + "address": "0x85e4857b7f15bbbbbc72d933a6357d3c22a0bbc7" + } + ], + "hashCallData": "0x35b891a625434c32d1c0106cd7d21188d58e979e61ab584717e57962d6f3778c", + "oldQuorum": 2, + "newQuorum": 1 + } + }, + "avalanche": { + "args": { + "target": "0xd28fa1395a45bf2d30af5afded0c7487b156f705", + "signatures": "0xae2f47ccf90b256b2942330bb74c74d59b41873d04732c7ea9997e916b88d63d7a6d7bf723d62d46b038f7b48f640403e3b78fc5ed61b059064e0895fc4260e91c5f8de2df9354743bcb0742ba3c9f7d5bced41d523c55ce44dabc61c5a5073bb175bf31a9a19e46be7d5262b092031bfdc4e432a24d8659811ebc01500e9d4e801c", + "callData": "0x8585c9450000000000000000000000000000000000000000000000000000000000000001", + "expiration": 1734735312437, + "vid": "10106" + }, + "info": { + "signatures": [ + { + "signature": "0xae2f47ccf90b256b2942330bb74c74d59b41873d04732c7ea9997e916b88d63d7a6d7bf723d62d46b038f7b48f640403e3b78fc5ed61b059064e0895fc4260e91c", + "address": "0x6a73853254eadca287346cc69d32b5a24b18a063" + }, + { + "signature": "0x5f8de2df9354743bcb0742ba3c9f7d5bced41d523c55ce44dabc61c5a5073bb175bf31a9a19e46be7d5262b092031bfdc4e432a24d8659811ebc01500e9d4e801c", + "address": "0x85e4857b7f15bbbbbc72d933a6357d3c22a0bbc7" + } + ], + "hashCallData": "0x87bbba75ae24aeddbd1fc7a376f991e8ff9f18e85aae98446cd666806ba706ab", + "oldQuorum": 2, + "newQuorum": 1 + } + } +}