Skip to content

Commit

Permalink
feat(governance): script to add an owner to multisig (#13439)
Browse files Browse the repository at this point in the history
* script to add a multisig owner

* add cli task

* use correct nonce so we dont override existing tx

* fail early if no multisig address is passed

* move `all_networks` to governance

* switch for testnets

* try/actach errors when looping

* upgrade safe..glbal libs to latest
  • Loading branch information
clemsos committed Mar 14, 2024
1 parent e70f32d commit 9b30c21
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 32 deletions.
4 changes: 2 additions & 2 deletions governance/package.json
Expand Up @@ -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",
Expand Down
43 changes: 43 additions & 0 deletions 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)
}
}
80 changes: 80 additions & 0 deletions 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
10 changes: 10 additions & 0 deletions governance/tasks/safe.js
Expand Up @@ -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 })
})
51 changes: 21 additions & 30 deletions yarn.lock
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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:
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit 9b30c21

Please sign in to comment.