From 356efab7586e824bca41c73861b01fbd78c4fdf2 Mon Sep 17 00:00:00 2001 From: Mikhail <16622558+mmv08@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:22:46 +0200 Subject: [PATCH] Readme updates and improvements to the deployment process (#678) This PR proposes README updates after https://github.com/safe-global/safe-deployments/issues/348, namely: 1. Add more detailed steps to add new deployments to the package 2. Grammar fixes 3. Describe the meaning of returned values 4. Remove the obsolete issue template for the new chain. I removed it because we unnecessarily make people open an issue and a PR simultaneously. We do not deploy the contracts ourselves, so I don't think we need an issue template. If the latter is not the case, I can look into switching the template to be more `safe-singleton-factory`-alike. 5. Remove the migration script I forgot to remove in my previous PR. --- .github/ISSUE_TEMPLATE/new_chain.yaml | 51 ---------- README.md | 84 ++++++++++------ src/jsonMigrationScript130.ts | 138 -------------------------- src/types.ts | 43 ++++++++ 4 files changed, 99 insertions(+), 217 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/new_chain.yaml delete mode 100644 src/jsonMigrationScript130.ts diff --git a/.github/ISSUE_TEMPLATE/new_chain.yaml b/.github/ISSUE_TEMPLATE/new_chain.yaml deleted file mode 100644 index 7a865f675..000000000 --- a/.github/ISSUE_TEMPLATE/new_chain.yaml +++ /dev/null @@ -1,51 +0,0 @@ -name: Add new chain -description: Template to provide information about the new chain -title: "[New chain]: " -labels: "[enhancement]" -body: - - type: markdown - attributes: - value: Please fill the following form - - type: input - id: chainid - attributes: - label: ChainId - description: Provide the chainId - validations: - required: true - - type: input - id: chainlist - attributes: - label: Chainlist - description: The chain should be listed on chainlist.org, provide the link. - validations: - required: true - - type: checkboxes - id: deployed - attributes: - label: Contract deployed - description: Confirm that the contracts are deployed on the chain, if not deploy with [safe-contracts](https://github.com/safe-global/safe-contracts) - options: - - label: I confirm that the contracts are deployed - required: true - - type: dropdown - id: versioning - attributes: - label: Version of contracts deployed - description: What version of contracts are deployed? NOTE:one issue per version and one pull request per version - multiple: false - options: - - v1.0.0 - - v1.1.0 - - v1.1.1 - - v1.2.0 - - v1.3.0 - - v1.4.1 - validations: - required: true - - type: textarea - id: relevant - attributes: - label: Relevant information - description: Add relevant information like blockexplorer, any annotation... - diff --git a/README.md b/README.md index 3c5991800..dc4653361 100644 --- a/README.md +++ b/README.md @@ -3,29 +3,34 @@ [![npm version](https://badge.fury.io/js/%40safe-global%2Fsafe-deployments.svg)](https://badge.fury.io/js/%40safe-global%2Fsafe-deployments) [![CI](https://github.com/safe-global/safe-deployments/actions/workflows/test.yml/badge.svg)](https://github.com/safe-global/safe-deployments/actions/workflows/test.yml) -This contract contains a collection of deployments of the contract of the [Safe contracts repository](https://github.com/safe-global/safe-contracts). +This contract contains a collection of deployments of the contract of the [Safe contracts repository](https://github.com/safe-global/safe-smart-account). -For each deployment the address on the different networks and the abi files are available. To get an overview of the available versions check the available [json assets](./src/assets/). +The addresses on the different networks and the abi files are available for each deployment. To get an overview of the available versions, check the available [json assets](./src/assets/). -To add additional deployments please follow the [deployment steps in the Safe contract repository](https://github.com/safe-global/safe-contracts#deployments). +## Adding additional deployments: + +1. Follow the [deployment steps in the Safe contract repository](https://github.com/safe-global/safe-smart-contracts#deployments). +2. Verify that the addresses match the expected address for each contract. You can find them under the "addresses" mapping in the respective JSON file in the [assets folder](./src/assets/). +3. Create a PR adding the new deployment. Example PR can be found [here](https://github.com/safe-global/safe-deployments/pull/676). ## Install + - npm - `npm i @safe-global/safe-deployments` - yarn - `yarn add @safe-global/safe-deployments` ## Usage -It is possible to directly use the json files in the [assets folder](./src/assets/) that contain the addresses and abi definitions. +It is possible to directly use the JSON files in the [assets folder](./src/assets/) that contain the addresses and abi definitions. -An alternative is to use the JavaScript library methods to query the correct deployment. The library supports different methods to get the deployment of a specific contract. +An alternative is using JavaScript library methods to query the correct deployment. The library supports different methods to get the deployment of a specific contract. -Each of the method takes an optional `DeploymentFilter` as a parameter. +Each of the methods takes an optional `DeploymentFilter` as a parameter. ```ts interface DeploymentFilter { - version?: string, - released?: boolean, // Defaults to true if no filter is specified - network?: string // Chain id of the network + version?: string; + released?: boolean; // Defaults to true if no filter is specified + network?: string; // Chain id of the network } ``` @@ -33,57 +38,80 @@ The method will return a `SingletonDeployment` object or `undefined` if no deplo ```ts interface SingletonDeployment { - defaultAddress: string, // Address the contract was deployed to by the Safe team - version: string, - abi: any[], - networkAddresses: Record, // Address of the contract by network - contractName: string, - released: boolean // A released version was audited and has a running bug bounty + // The default address of the deployment. + defaultAddress: string; + + // Indicates if the deployment is released. + released: boolean; + + // The name of the contract. + contractName: string; + + // The version of the deployment. + version: string; + + // The hash of the contract code. + codeHash: string; + + // A record of addresses, where the key is the address type and the value is the address. + addresses: Record; + + // A record of network addresses, where the key is the network identifier and the value is the address. + networkAddresses: Record; + + // The ABI (Application Binary Interface) of the contract. + abi: any[]; } ``` - Safe + ```ts -const safeSingleton = getSafeSingletonDeployment() +const safeSingleton = getSafeSingletonDeployment(); // Returns latest contract version, even if not finally released yet -const safeSingletonNightly = getSafeSingletonDeployment({ released: undefined }) +const safeSingletonNightly = getSafeSingletonDeployment({ released: undefined }); // Returns released contract version for specific network -const safeSingletonGörli = getSafeSingletonDeployment({ network: "5" }) +const safeSingletonGörli = getSafeSingletonDeployment({ network: '5' }); // Returns released contract version for specific version -const safeSingleton100 = getSafeSingletonDeployment({ version: "1.0.0" }) +const safeSingleton100 = getSafeSingletonDeployment({ version: '1.0.0' }); // Version with additional events used on L2 networks -const safeL2Singleton = getSafeL2SingletonDeployment() +const safeL2Singleton = getSafeL2SingletonDeployment(); ``` - Factories + ```ts -const proxyFactory = getProxyFactoryDeployment() +const proxyFactory = getProxyFactoryDeployment(); ``` - Libraries + ```ts -const multiSendLib = getMultiSendDeployment() +const multiSendLib = getMultiSendDeployment(); -const multiSendCallOnlyLib = getMultiSendCallOnlyDeployment() +const multiSendCallOnlyLib = getMultiSendCallOnlyDeployment(); -const createCallLib = getCreateCallDeployment() +const createCallLib = getCreateCallDeployment(); ``` - Handler + ```ts // Returns recommended handler -const fallbackHandler = getFallbackHandlerDeployment() +const fallbackHandler = getFallbackHandlerDeployment(); -const callbackHandler = getDefaultCallbackHandlerDeployment() +const callbackHandler = getDefaultCallbackHandlerDeployment(); -const compatHandler = getCompatibilityFallbackHandlerDeployment() +const compatHandler = getCompatibilityFallbackHandlerDeployment(); ``` + ## Release cycle -`safe-deployments` release cycle is once per month, except urgent issues that require immediate attention. + +`safe-deployments` release cycle is once per month, except for urgent issues that require immediate attention. ## Notes diff --git a/src/jsonMigrationScript130.ts b/src/jsonMigrationScript130.ts deleted file mode 100644 index 540d3a72f..000000000 --- a/src/jsonMigrationScript130.ts +++ /dev/null @@ -1,138 +0,0 @@ -import fs from 'fs'; -import { SingletonDeploymentJSON } from './types'; - -const fileList: string[] = [ - 'compatibility_fallback_handler.json', - 'create_call.json', - 'gnosis_safe_l2.json', - 'gnosis_safe.json', - 'multi_send.json', - 'multi_send_call_only.json', - 'proxy_factory.json', - 'sign_message_lib.json', - 'simulate_tx_accessor.json', -]; - -async function processFile(fileName: string) { - const file = fs.readFileSync(`./src/__tests__/assets/v1/v1.3.0/${fileName}`, 'utf8'); - const json = JSON.parse(file) as SingletonDeploymentJSON; - - const addresses = { - canonical: json.networkAddresses[1], - eip155: json.networkAddresses[10], - zksync: json.networkAddresses[324], - }; - - const newJson = { - ...json, - addresses, - networkAddresses: {} as Record, - }; - delete newJson.defaultAddress; - - for (const value of Object.values(addresses)) { - if (!value) { - throw new Error('Address not found'); - } - } - - for (const entry of Object.entries(json.networkAddresses)) { - const [chainId, previousAddress] = entry; - - const chainListUrl = `https://raw.githubusercontent.com/ethereum-lists/chains/master/_data/chains/eip155-${chainId}.json`; - - const chainData = (await fetch(chainListUrl) - .then((res) => res.json()) - .catch((e) => { - console.log(`Error fetching chain data for chain ${chainId}. Skipping...`, e); - return { rpc: [] }; - })) as { rpc: string[] }; - - const rpcs = chainData.rpc.filter((rpc) => !rpc.startsWith('ws') && !rpc.includes('INFURA_API_KEY')); - if (!rpcs.length) { - console.log(`Chain ${chainId} is missing in chain list. Skipping...`); - const previousAddressType = Object.keys(addresses).find((key) => addresses[key] === previousAddress); - if (!previousAddressType) { - throw new Error('Previous address type not found'); - } - newJson.networkAddresses[chainId] = previousAddressType; - continue; - } - - for (const rpc of rpcs) { - const networkAddresses: string[] = []; - for (const value of Object.entries(addresses)) { - const [addrType, addr] = value; - const deployedCode = await fetch(rpc, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - jsonrpc: '2.0', - method: 'eth_getCode', - params: [addr, 'latest'], - id: 1, - }), - }) - .then((res) => res.json()) - .then((json) => { - if ('result' in json) { - return json; - } - - throw new Error('Invalid response'); - }) - .catch((e) => { - console.log(`Error fetching code for ${addr} on chain ${chainId}. Skipping...`, e); - return { error: true }; - }); - - if (deployedCode.error) { - console.log(`RPC failed for chain ${chainId}. Skipping...`); - continue; - } - - if (deployedCode.result === '0x' || deployedCode.result === '') { - console.log(`Contract ${addr} not deployed. Skipping...`); - continue; - } else { - networkAddresses.push(addrType); - } - } - - if (networkAddresses.length === 0) { - console.log(`Contract ${fileName} not deployed on chain ${chainId}. Fallback to old json...`); - const previousAddressType = Object.keys(addresses).find((key) => addresses[key] === previousAddress); - if (!previousAddressType) { - throw new Error('Previous address type not found'); - } - newJson.networkAddresses[chainId] = previousAddressType; - continue; - } else if (networkAddresses.length === 1) { - newJson.networkAddresses[chainId] = networkAddresses[0]; - } else { - const previousAddressType = Object.keys(addresses).find((key) => addresses[key] === previousAddress); - if (!previousAddressType) { - throw new Error('Previous address type not found'); - } - - // sort the array so that the previous address type is first - newJson.networkAddresses[chainId] = [ - previousAddressType, - ...networkAddresses.filter((addr) => addr !== previousAddressType), - ]; - } - - break; - } - } - - fs.writeFileSync(`./src/assets/v1.3.0/${fileName}`, JSON.stringify(newJson, null, 2)); -} - -async function main() { - await Promise.all(fileList.map((fileName) => processFile(fileName))); -} - -main().catch(console.error); diff --git a/src/types.ts b/src/types.ts index 321cdead2..fbbc9ec49 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,28 +1,71 @@ enum AddressType { + // The original address the contract was deployed on. + // Starting with 1.4.1, the contracts are deployed with an EIP155-compatible transaction. CANONICAL = 'canonical', + + // An address that was deployed with a transaction compatible with the EIP155 standard. + // The type is only used for 1.3.0 deployments. EIP155 = 'eip155', + + // An address that is deployed to the ZkSync VM networks. ZKSYNC = 'zksync', } export interface SingletonDeploymentJSON { + // Indicates if the deployment is released. released: boolean; + + // The name of the contract. contractName: string; + + // The version of the deployment. version: string; + + // The hash of the contract code. codeHash: string; + + // A record of network addresses, where the key is the network identifier and the value is either a single address type or an array of address types. networkAddresses: Record; + + // A record of addresses, where the key is the address type and the value is the address. + // Possible addresses per version: + // 1.0.0: canonical + // 1.1.1: canonical + // 1.2.0: canonical + // 1.3.0: canonical, eip155, zksync + // 1.4.1: canonical, zksync addresses: Record; + + // The ABI (Application Binary Interface) of the contract. /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ abi: any[]; } +// This is the old type that only allows a single address for each network. +// It is still used for backwards compatibility. export interface SingletonDeployment { + // The default address of the deployment. defaultAddress: string; + + // Indicates if the deployment is released. released: boolean; + + // The name of the contract. contractName: string; + + // The version of the deployment. version: string; + + // The hash of the contract code. codeHash: string; + + // A record of addresses, where the key is the address type and the value is the address. addresses: Record; + + // A record of network addresses, where the key is the network identifier and the value is the address. networkAddresses: Record; + + // The ABI (Application Binary Interface) of the contract. /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ abi: any[]; }