From baf0e151caa84b15fd1b68fcdf1adb0868318a60 Mon Sep 17 00:00:00 2001 From: Ryan Goulding Date: Mon, 27 Nov 2023 20:23:03 -0800 Subject: [PATCH] feat: allow configuration of Gnosis contractAddresses Provide our own types as gnosis does not define adequate types. Signed-off-by: Ryan Goulding --- package.json | 2 +- src/utils/crossChainHelper.ts | 13 ++++----- src/utils/gnosis.ts | 51 +++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src/utils/gnosis.ts diff --git a/package.json b/package.json index b4a8087..732c115 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@layerzerolabs/ua-utils", - "version": "0.0.17", + "version": "0.0.21", "repository": "https://github.com/LayerZero-Labs/ua-utils.git", "license": "MIT", "main": "dist/index.js", diff --git a/src/utils/crossChainHelper.ts b/src/utils/crossChainHelper.ts index 7918656..2a40843 100644 --- a/src/utils/crossChainHelper.ts +++ b/src/utils/crossChainHelper.ts @@ -6,7 +6,8 @@ import Safe from "@gnosis.pm/safe-core-sdk"; import { LZ_APP_ABI } from "../constants/abi"; import { LZ_ENDPOINTS } from "../constants/endpoints"; import { MainnetEndpointId, TestnetEndpointId, SandboxEndpointId } from "@layerzerolabs/lz-definitions"; -import { promptToProceed, arrayToCsv, getConfig } from "./helpers"; +import { toContractNetworksString, getSafeConfigs, SafeConfigs } from './gnosis' +import { promptToProceed, arrayToCsv } from "./helpers"; const path = require("path"); const fs = require("fs"); import { writeFile } from "fs/promises"; @@ -174,7 +175,7 @@ export async function executeTransactions(hre: any, taskArgs: any, transactionBy if (taskArgs.n) { await promptToProceed("Would you like to Submit to gnosis?", taskArgs.noPrompt); - const gnosisConfig = getConfig(taskArgs.gnosisConfigPath); + const gnosisConfig = getSafeConfigs(taskArgs.gnosisConfigPath); await Promise.all( transactionBynetwork.map(async ({ network, transactions }) => { const transactionToCommit = transactions.filter((transaction: any) => transaction.needChange); @@ -239,14 +240,14 @@ export async function executeTransactions(hre: any, taskArgs: any, transactionBy } -export const executeGnosisTransactions = async (hre: any, network: string, gnosisConfig: any, transactions: Transaction[]) => { +export const executeGnosisTransactions = async (hre: any, network: string, gnosisConfig: SafeConfigs, transactions: Transaction[]) => { const signer = await getConnectedWallet(hre, network, 0); if (!gnosisConfig[network]) { throw Error(`Gnosis for ${network} not found or not supported`); } - const { safeAddress, url } = gnosisConfig[network]; - console.log(`safeAddress[${safeAddress}] url[${url}]`); + const { safeAddress, url, contractNetworks } = gnosisConfig[network]; + console.log(`safeAddress[${safeAddress}] url[${url}] ${toContractNetworksString(contractNetworks)}`); const safeService = new SafeServiceClient(url); const ethAdapter = new EthersAdapter({ @@ -254,7 +255,7 @@ export const executeGnosisTransactions = async (hre: any, network: string, gnosi signerOrProvider: signer, }); - const safeSdk: Safe = await Safe.create({ ethAdapter, safeAddress }); + const safeSdk: Safe = await Safe.create({ ethAdapter, safeAddress, ...(!!contractNetworks && contractNetworks)}); const gnosisTransactions = transactions.map((tx) => ({ to: tx.contractAddress, data: tx.calldata!, value: "0" })); const nonce = await safeService.getNextNonce(safeAddress); const safeTransaction = await safeSdk.createTransaction(gnosisTransactions, { nonce }); diff --git a/src/utils/gnosis.ts b/src/utils/gnosis.ts new file mode 100644 index 0000000..e561139 --- /dev/null +++ b/src/utils/gnosis.ts @@ -0,0 +1,51 @@ +import { readFileSync } from 'fs' + +const GNOSIS_SAFE_FILE_ENCODING = 'utf-8' + +/** + * Gnosis Safe configuration for a specific network. + */ +type SafeConfig = { + safeAddress: string + url: string + contractNetworks?: ContractNetworks +} + +/** + * Contract addresses for each network. + */ +type ContractNetworks = { + [chainListId: number]: { + multiSendAddress: string + safeMasterCopyAddress: string + safeProxyFactoryAddress: string + } +} + +/** + * Converts a ContractNetworks object to a string. + * @param {ContractNetworks} contractNetworks The ContractNetworks object to convert. + */ +export const toContractNetworksString = (contractNetworks?: ContractNetworks): string => { + if (contractNetworks === undefined) { + return '' + } + return Object.entries(contractNetworks).reduce((accumulator, [chainListId, config]) => { + return accumulator + `contractNetworks[chainListId=${chainListId}, multiSendAddress=${config.multiSendAddress}, safeMasterCopyAddress=${config.safeMasterCopyAddress}, safeProxyFactoryAddress=${config.safeProxyFactoryAddress}]`; + }, ''); +} + +/** + * Gnosis Safe configuration per network. + */ +export interface SafeConfigs { + [chainName: string]: SafeConfig +} + +/** + * Reads the safe config file and returns the parsed SafeConfigs. + * @param {string} fileName The name of the safe config file. + */ +export const getSafeConfigs = (fileName: string): SafeConfigs => { + return JSON.parse(readFileSync(fileName, GNOSIS_SAFE_FILE_ENCODING)) as SafeConfigs +} \ No newline at end of file