Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ node_modules
audit_owners_mapper.py
data/Convex_EPS
data/badger/acl_roles_audit
data/badger/governance_audit
logs/
great_ape_safe/ape_api/helpers/balancer/pools.json
2 changes: 2 additions & 0 deletions helpers/addresses.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
"ops_multisig_old": "0x576cD258835C529B54722F84Bb7d4170aA932C64",
"treasury_ops_multisig": "0x042B32Ac6b453485e357938bdC38e0340d4b9276",
"treasury_vault_multisig": "0xD0A7A8B98957b9CD3cFB9c0425AbE44551158e9e",
"community_council_multisig": "0x1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a",
"dev_multisig_backup": "0x2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b",
"ibbtc_multisig": "0xB76782B51BFf9C27bA69C77027e20Abd92Bcf3a8",
"treasury_voter_multisig": "0xA9ed98B5Fb8428d68664f3C5027c62A10d45826b",
"payments_multisig": "0x30a9c1D258F6c2D23005e6450E72bDD42C541105",
Expand Down
2 changes: 2 additions & 0 deletions interfaces/badger/IController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ interface IController {

function setVault(address, address) external;

function setGovernance(address _governance) external;

function setRewards(address) external;

function want(address) external view returns (address);
Expand Down
15 changes: 15 additions & 0 deletions interfaces/badger/IInfraPermissions.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity >=0.7.0 <0.9.0;

interface IInfraPermissions {
function strategist() external view returns (address);

function governance() external view returns (address);

function owner() external view returns (address);

function manager() external view returns (address);

function admin() external view returns (address);

function guardian() external view returns (address);
}
179 changes: 179 additions & 0 deletions scripts/badger/dev_multisig_progression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
from great_ape_safe import GreatApeSafe
from helpers.addresses import r
from brownie import interface
from rich.console import Console

C = Console()

"""
The following set of scripts are meant to perform key actions to progress the Dev Multisig
into a more decentralized state. These actions were defined based on the observations obtained from the
governance and upgradeability access audit.
"""

# Actors involved
DEV = r.badger_wallets.dev_multisig
TECHOPS = r.badger_wallets.techops_multisig
TREASURY = r.badger_wallets.treasury_vault_multisig
COMMUNITY = r.badger_wallets.community_council_multisig
BACKUP = r.badger_wallets.dev_multisig_backup

# Contracts involved
ACL = [
r.GlobalAccessControl,
r.guardian,
r.keeperAccessControl,
r.rewardsLogger,
r.badger_wallets.badgertree,
]

TIMELOCK = r.governance_timelock

# Controllers and vaults not governed by the Timelock
CONTROLLERS = [
"bbveCVX-CVX-f",
"ibBTCCrv",
"restitution",
"bcrvBadger",
]
VAULTS = [
"bcrvRenBTC",
"bcrvIbBTC",
"bcvxCRV",
"bveCVX",
"bbveCVX_CVX_f",
"remBADGER",
"remDIGG",
"bcrvBadger",
"graviAURA",
"bauraBal",
"b80BADGER_20WBTC",
"b40WBTC_40DIGG_20graviAURA",
"bBB_a_USD",
"b33auraBAL_33graviAURA_33WETH",
"bB_stETH_STABLE",
"bB_rETH_STABLE",
]

# Constants
DEFAULT_ADMIN_ROLE = (
"0x0000000000000000000000000000000000000000000000000000000000000000"
)
SENTINEL_OWNERS = "0x0000000000000000000000000000000000000001"

FINAL_DEV_MULTISG_STATE = [
TREASURY,
TECHOPS,
COMMUNITY,
BACKUP,
]

FINAL_POLICY = 3


def step_1_1():
"""
- Transfer the DEFAULT_ADMIN_ROLE for GlobalAccessControl, Guardian, KeeperACL, RewardsLogger,
and BadgerTree from the Dev Multisig to the Badger TechOps multisig.
"""

dev = GreatApeSafe(DEV)

for acl_address in ACL:
acl = dev.contract(acl_address, Interface=interface.IAccessControl)
# Confirm that the Dev multisig holds the DEFAULT_ADMIN_ROLE (and nobody else does)
assert acl.getRoleMemberCount(DEFAULT_ADMIN_ROLE) == 1
assert acl.hasRole(DEFAULT_ADMIN_ROLE, DEV)
# Grant role to TechOps (Dev multisig keeps it to simplify things)
C.print(f"Granting admin role to TechOps on {acl_address}")
acl.grantRole(DEFAULT_ADMIN_ROLE, TECHOPS)
assert acl.getRoleMemberCount(DEFAULT_ADMIN_ROLE) == 2
assert acl.hasRole(DEFAULT_ADMIN_ROLE, TECHOPS)

dev.post_safe_tx()


def step_1_2():
"""
- Transfer governance of the remaining vaults and controllers, including remBADGER, from the Dev Multisig to
the Governance Timelock (for vaults not already governed by the Timelock).
"""

dev = GreatApeSafe(DEV)

# Confirm governance is held by the Dev Multisig and transfer it to the Timelock for each of the vaults
for vault_ID in VAULTS:
vault = dev.contract(r.sett_vaults[vault_ID], interface.ISettV4h)
assert vault.governance() == DEV
C.print(f"Transfering governance to Timelock on vault: {vault_ID}")
vault.setGovernance(TIMELOCK)
assert vault.governance() == TIMELOCK

# Confirm governance is held by the Dev Multisig and transfer it to the Timelock for each of the controllers
for controller_ID in CONTROLLERS:
controller = dev.contract(r.controllers[controller_ID], interface.IController)
assert controller.governance() == DEV
C.print(f"Transfering governance to Timelock on controller: {controller_ID}")
controller.setGovernance(TIMELOCK)
assert controller.governance() == TIMELOCK

dev.post_safe_tx()


def step_2():
"""
Replace Dev Multisig signers with the following multisigs and change its policy to 3/4:
- Treasury Vault
- Badger TechOps
- Community Council
- Security Backup (Third party security partner)
"""

dev = GreatApeSafe(DEV)
dev_multisig = interface.IGnosisSafe_v1_3_0(DEV, owner=dev.account)

dev_current = dev_multisig.getOwners()

unique_to_current = list(set(dev_current).difference(set(FINAL_DEV_MULTISG_STATE)))
unique_to_final = list(set(FINAL_DEV_MULTISG_STATE).difference(set(dev_current)))

# Swap out any unique addresses to the Dev Multisig
for i in range(len(unique_to_final)):
C.print(f"Swapping {unique_to_current[i]} for {unique_to_final[i]}...")

dev_multisig.swapOwner(
get_previous_owner(dev_multisig, unique_to_current[i]),
unique_to_current[i],
unique_to_final[i],
)

# Remove any outstanding owners
dev_current = dev_multisig.getOwners()
for owner in dev_current:
if owner not in FINAL_DEV_MULTISG_STATE:
C.print(f"Removing {owner}...")
dev_multisig.removeOwner(
get_previous_owner(dev_multisig, owner), owner, FINAL_POLICY
)

# Confirm all owners
for owner in dev_multisig.getOwners():
assert owner in FINAL_DEV_MULTISG_STATE
assert len(dev_multisig.getOwners()) == len(FINAL_DEV_MULTISG_STATE)
assert dev_multisig.getThreshold() == FINAL_POLICY

C.print(f"\nNew Owners at dev_multisig Multisig:")
C.print(f"[green]{dev_multisig.getOwners()}[/green]\n")
C.print(f"[green]{dev_multisig.getThreshold()}[/green]\n")

dev.post_safe_tx()


def get_previous_owner(dev, owner):
owners = dev.getOwners()
for i in range(len(owners)):
if owners[i] == owner:
if i == 0:
return SENTINEL_OWNERS
else:
return owners[i - 1]
Loading