Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(governance) refactored the script to check multisig signers and settings #13488

Merged
merged 1 commit into from Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 4 additions & 5 deletions governance/scripts/multisig/_helpers.js
Expand Up @@ -18,11 +18,10 @@ const getProvider = async (chainId) => {

// custom services URL for network not supported by Safe
const safeServiceURLs = {
42220: 'http://mainnet-tx-svc.celo-safe-prod.celo-networks-dev.org/',
// mumbai isnt supported by Safe Global, you need to run Safe infrastructure locally
80001: 'http://localhost:8000/cgw/',
// for some reasons, zkevm throws in lib
1101: 'https://safe-transaction-zkevm.safe.global',
324: 'https://safe-transaction-zksync.safe.global/api',
1101: 'https://safe-transaction-zkevm.safe.global/api',
534352: 'https://transaction.safe.scroll.xyz/api',
59144: 'https://safe.linea.build/api',
}

// get safeAddress directly from unlock if needed
Expand Down
1 change: 0 additions & 1 deletion governance/scripts/multisig/addOwner.js
@@ -1,5 +1,4 @@
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')
Expand Down
54 changes: 42 additions & 12 deletions governance/scripts/multisig/info.js
Expand Up @@ -5,8 +5,25 @@ const { networks } = require('@unlock-protocol/networks')

const SafeApiKit = require('@safe-global/api-kit').default

const getMultigiInfo = async (chainId, multisig) => {
const prodSigners = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we get these from mainnet directly ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about that, but then realized it was going to be weird to check something against itself...

'0x9d3ea9e9adde71141f4534dB3b9B80dF3D03Ee5f', // cc
'0x4Ce2DD8373ECe0d7baAA16E559A5817CC875b16a', // jg
'0x4011d09a86D0acA8377a4A8baD691F1ACeeCd672', // nf
'0xcFd35259E3A468E7bDF84a95bCddAc0B614A9212', // aa
'0xccb5D94FbfBFDc4953Ca8a114f88773C2fF98e80', // sm
'0x246A13358Fb27523642D86367a51C2aEB137Ac6C', // cr
].sort()

const devSigners = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem maybe use Sepolia as reference ?

'0x4Ce2DD8373ECe0d7baAA16E559A5817CC875b16a', // jg
'0x246A13358Fb27523642D86367a51C2aEB137Ac6C', // cr
'0x9d3ea9e9adde71141f4534dB3b9B80dF3D03Ee5f', // cc
].sort()

const getMultiSigInfo = async (chainId, multisig) => {
const errors = []
const { isTestNetwork } = networks[chainId]
const expectedSigners = isTestNetwork ? devSigners : prodSigners
const provider = await getProvider(chainId)
// get Safe service
const safeService = new SafeApiKit({
Expand All @@ -26,24 +43,37 @@ const getMultigiInfo = async (chainId, multisig) => {
// queued: false,
// })

const safe = new ethers.Contract(multisig, multisigABI, provider)
const owners = await safe.getOwners()
const policy = await safe.getThreshold()
if (!multisig) {
errors.push('Missing multisig')
} else {
if (policy == 1) {
errors.push('❌ Single policy owner!')
const safe = new ethers.Contract(multisig, multisigABI, provider)
const owners = await safe.getOwners()
const policy = await safe.getThreshold()

if (isTestNetwork && policy < 2) {
errors.push('❌ Policy below 2!')
}
if (!isTestNetwork && policy < 4) {
errors.push(
`❌ Unexpected policy: ${policy}/${owners.length} for 4/${expectedSigners.length} expected`
)
}
if (policy <= 2 || policy / BigInt(owners.length) > 1.5) {
errors.push(`Low policy owner (${policy}/${owners.length})`)

let extraSigners = owners.filter((x) => !expectedSigners.includes(x))
if (extraSigners.length > 0) {
errors.push(`❌ Extra signers: ${[...extraSigners].sort()}`)
}

let missingSigners = expectedSigners.filter((x) => !owners.includes(x))
if (missingSigners.length > 0) {
errors.push(`❌ Missing signers: ${missingSigners}`)
}
}
return errors
}

const log = (name, chainId, msg) =>
console.log(`[${name} (${chainId})]: ${msg}`)
const log = (name, chainId, multisig, msg) =>
console.log(`[${name} (${chainId})]: ${multisig} ${msg}`)

async function main() {
for (let chainId in networks) {
Expand All @@ -52,11 +82,11 @@ async function main() {
const { multisig, name } = networks[chainId]

try {
errors = await getMultigiInfo(chainId, multisig)
errors = await getMultiSigInfo(chainId, multisig)
} catch (error) {
errors = [`Couldn't fetch multisig info: ${error.message}`]
}
errors.forEach((error) => log(name, chainId, error))
errors.forEach((error) => log(name, chainId, multisig, error))
}
}

Expand Down