diff --git a/governance/package.json b/governance/package.json index 68b3674271d..31f4bb0694a 100644 --- a/governance/package.json +++ b/governance/package.json @@ -13,8 +13,8 @@ "@nomiclabs/hardhat-ethers": "2.2.3", "@openzeppelin/hardhat-upgrades": "3.0.2", "@openzeppelin/upgrades-core": "1.32.5", - "@safe-global/api-kit": "2.0.0", - "@safe-global/protocol-kit": "2.0.0", + "@safe-global/api-kit": "2.2.0", + "@safe-global/protocol-kit": "3.0.1", "@tenderly/hardhat-tenderly": "2.2.2", "@unlock-protocol/contracts": "workspace:./packages/contracts", "@unlock-protocol/eslint-config": "workspace:./packages/eslint-config", diff --git a/governance/scripts/all_networks b/governance/scripts/all_networks new file mode 100755 index 00000000000..4882bca377d --- /dev/null +++ b/governance/scripts/all_networks @@ -0,0 +1,43 @@ +#!/usr/bin/env node +/** + * + * Just a small util to run a hardhat script for all networks + * + * Usage: scripts/all_networks run scripts/etc... + * Usage: scripts/all_networks unlock:info + */ + +const allNetworks = require('@unlock-protocol/networks') +const { execSync } = require('child_process') + +// if any network is present this array, only these will be executed +const only = [] + +// these networks will be excluded from run +const toExclude = ['localhost', 'default', 'networks'] +const excludeTestnets = true + +const networks = only.length + ? only + : Object.keys(allNetworks).filter( + (n) => + !toExclude.includes(n) && + (excludeTestnets ? !allNetworks[n].isTestNetwork : true) + ) + +console.log(`Running task for the following networks: ${networks.toString()}`) + +for (let i = 0; i < networks.length; i++) { + const cmd = `yarn hardhat` + const args = ['--network', networks[i], ...process.argv.slice(2)] + + console.log(cmd, args) + try { + execSync(`${cmd} ${args.join(' ')}`, { + stdio: 'inherit', + }) + } catch (error) { + console.log(`Network ${networks[i]} failed`) + console.log(error.message) + } +} diff --git a/governance/scripts/multisig/addOwner.js b/governance/scripts/multisig/addOwner.js new file mode 100644 index 00000000000..b67d4f1c42d --- /dev/null +++ b/governance/scripts/multisig/addOwner.js @@ -0,0 +1,80 @@ +const { ethers } = require('hardhat') + +const { getNetwork } = require('@unlock-protocol/hardhat-helpers') +const Safe = require('@safe-global/protocol-kit').default +const { EthersAdapter } = require('@safe-global/protocol-kit') +const SafeApiKit = require('@safe-global/api-kit').default + +async function main({ newOwner, safeAddress, threshold } = {}) { + const { id, multisig, name } = await getNetwork() + let [signer] = await ethers.getSigners() + + if (!safeAddress) { + safeAddress = multisig + } + + if (!safeAddress) { + throw new Error(`Missing multisig address for ${name} [${id}].`) + } + + // default to ccarfi.eth + console.log(`Adding signer ${newOwner} on chain on ${id}: + - multisig: ${safeAddress} + - signer: ${signer.address} + `) + + // Use Safe v1+ with SDK + const ethAdapter = new EthersAdapter({ + ethers, + signerOrProvider: signer, + }) + + const safeSdk = await Safe.create({ ethAdapter, safeAddress }) + const safeService = new SafeApiKit({ + chainId: id, + }) + + // get nonce so we make sure we dont erase current pending tx + const nonce = await safeService.getNextNonce(safeAddress) + const safeTransaction = await safeSdk.createAddOwnerTx( + { + ownerAddress: newOwner, + // threshold + }, + { nonce } + ) + + // Get the transaction hash of the safeTransaction + const safeTransactionHash = await safeSdk.getTransactionHash(safeTransaction) + console.log(`submitting tx with hash : ${safeTransactionHash} ...`) + + // get signature + const senderSignature = await safeSdk.signTransactionHash(safeTransactionHash) + + // Propose the transaction + const txParams = { + safeAddress, + safeTransactionData: safeTransaction.data, + safeTxHash: safeTransactionHash, + senderAddress: signer.address, + senderSignature: senderSignature.data, + } + + await safeService.proposeTransaction(txParams) + const { nonce: actualNonce } = await safeService.getTransaction( + safeTransactionHash + ) + console.log(`tx submitted - nonce: [${actualNonce}].`) +} + +// execute as standalone +if (require.main === module) { + main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) +} + +module.exports = main diff --git a/governance/tasks/safe.js b/governance/tasks/safe.js index efd9c2c3ba6..6bc9e22fa21 100644 --- a/governance/tasks/safe.js +++ b/governance/tasks/safe.js @@ -89,3 +89,13 @@ task('safe:submit', 'Submit to multisig from a proposal file') const submitTx = require('../scripts/multisig/submitTx') await submitTx({ safeAddress, tx: calls }) }) + +task('safe:add-owner', 'Submit a new owner to a multisig') + .addParam('newOwner', 'The address of the new safe owner') + .addOptionalParam('safeAddress', 'the address of the multisig contract') + .addOptionalParam('threshold', 'new threshold for the multisig contract') + .setAction(async ({ newOwner, safeAddress }) => { + // eslint-disable-next-line global-require + const addOwner = require('../scripts/multisig/addOwner') + await addOwner({ safeAddress, newOwner }) + }) diff --git a/yarn.lock b/yarn.lock index 65ce52916c6..bf2da856304 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10799,7 +10799,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": version: 1.3.3 resolution: "@noble/hashes@npm:1.3.3" checksum: 10/1025ddde4d24630e95c0818e63d2d54ee131b980fe113312d17ed7468bc18f54486ac86c907685759f8a7e13c2f9b9e83ec7b67d1cc20836f36b5e4a65bb102d @@ -13446,31 +13446,31 @@ __metadata: languageName: node linkType: hard -"@safe-global/api-kit@npm:2.0.0": - version: 2.0.0 - resolution: "@safe-global/api-kit@npm:2.0.0" +"@safe-global/api-kit@npm:2.2.0": + version: 2.2.0 + resolution: "@safe-global/api-kit@npm:2.2.0" dependencies: - "@safe-global/protocol-kit": "npm:^2.0.0" - "@safe-global/safe-core-sdk-types": "npm:^3.0.0" + "@safe-global/protocol-kit": "npm:^3.0.1" + "@safe-global/safe-core-sdk-types": "npm:^4.0.1" ethers: "npm:^6.7.1" node-fetch: "npm:^2.7.0" - checksum: 10/6a16aaa49954382ef308d736fb6193a37eb389f6e0d12f5784a86964f11a46892ddf9180b94e993dcb92e89b79073a574e64b5d93c2ac4bba05aca137c0d087b + checksum: 10/5e65e2346775f07c8ea9c509b0b2e2eb3d4062d20f894bebab5eaa8bef96e57b5bd77dd7fabf9ada5f058ff1b4e794143fdc0f97be532b30fd0f253a73133ba2 languageName: node linkType: hard -"@safe-global/protocol-kit@npm:2.0.0, @safe-global/protocol-kit@npm:^2.0.0": - version: 2.0.0 - resolution: "@safe-global/protocol-kit@npm:2.0.0" +"@safe-global/protocol-kit@npm:3.0.1, @safe-global/protocol-kit@npm:^3.0.1": + version: 3.0.1 + resolution: "@safe-global/protocol-kit@npm:3.0.1" dependencies: - "@noble/hashes": "npm:^1.3.2" - "@safe-global/safe-deployments": "npm:^1.28.0" + "@noble/hashes": "npm:^1.3.3" + "@safe-global/safe-deployments": "npm:^1.33.0" ethereumjs-util: "npm:^7.1.5" ethers: "npm:^6.7.1" semver: "npm:^7.5.4" web3: "npm:^1.10.3" web3-core: "npm:^1.10.3" web3-utils: "npm:^1.10.3" - checksum: 10/d284447f7046bcc56c0a1e3ebb25d3e995df0e0cdc78dc8529628abc1b635e98c15edeb09d3b2b9d0da1751065af90c52ddd769601d71e9e3df3f99aa31ec239 + checksum: 10/cbf620e4b2a48db988d2acc36b7857c1601d6958fe9afe0d8f46ffdc30e411d5f95f0bcd21abd917b1800888a78225f6f98eea53b9cbaade919f8cb0e0602ebb languageName: node linkType: hard @@ -13487,15 +13487,15 @@ __metadata: languageName: node linkType: hard -"@safe-global/safe-core-sdk-types@npm:^3.0.0": - version: 3.0.1 - resolution: "@safe-global/safe-core-sdk-types@npm:3.0.1" +"@safe-global/safe-core-sdk-types@npm:^4.0.1": + version: 4.0.1 + resolution: "@safe-global/safe-core-sdk-types@npm:4.0.1" dependencies: - "@safe-global/safe-deployments": "npm:^1.28.0" + "@safe-global/safe-deployments": "npm:^1.33.0" ethers: "npm:^6.7.1" web3-core: "npm:^1.10.3" web3-utils: "npm:^1.10.3" - checksum: 10/afbe289dbcb99841fdb864f29ddad8e15ad9f75a66c2e3d1ff40f82e93f0665ef4eaaa6dc0cbbaf7bbdd2176f88d6c14eab1cb159a0ad3984c8931a3f02a1739 + checksum: 10/fc68538ab0bfa17a00c33fe3eb2ad80d150947edb2f345060c5794f3b753001f368653af87960e707d92addec1a37a30376463481f8d7a8aa23319ae5b3d9484 languageName: node linkType: hard @@ -13526,7 +13526,7 @@ __metadata: languageName: node linkType: hard -"@safe-global/safe-deployments@npm:^1.20.2": +"@safe-global/safe-deployments@npm:^1.20.2, @safe-global/safe-deployments@npm:^1.33.0": version: 1.33.0 resolution: "@safe-global/safe-deployments@npm:1.33.0" dependencies: @@ -13544,15 +13544,6 @@ __metadata: languageName: node linkType: hard -"@safe-global/safe-deployments@npm:^1.28.0": - version: 1.30.0 - resolution: "@safe-global/safe-deployments@npm:1.30.0" - dependencies: - semver: "npm:^7.3.7" - checksum: 10/0d8eca465d9e9b2ecf4f0d1cb95fd8d4ae0c0104b5f9f0cba71e07a0268122badc40ab23863d867368a9e0f5d81f284de61359c306ad3f88b63d7e5771efd46a - languageName: node - linkType: hard - "@safe-global/safe-ethers-lib@npm:1.9.3": version: 1.9.3 resolution: "@safe-global/safe-ethers-lib@npm:1.9.3" @@ -19911,8 +19902,8 @@ __metadata: "@nomiclabs/hardhat-ethers": "npm:2.2.3" "@openzeppelin/hardhat-upgrades": "npm:3.0.2" "@openzeppelin/upgrades-core": "npm:1.32.5" - "@safe-global/api-kit": "npm:2.0.0" - "@safe-global/protocol-kit": "npm:2.0.0" + "@safe-global/api-kit": "npm:2.2.0" + "@safe-global/protocol-kit": "npm:3.0.1" "@tenderly/hardhat-tenderly": "npm:2.2.2" "@unlock-protocol/contracts": "workspace:./packages/contracts" "@unlock-protocol/eslint-config": "workspace:./packages/eslint-config"