diff --git a/deployment/common/changeset/transfer_to_mcms_with_timelock.go b/deployment/common/changeset/transfer_to_mcms_with_timelock.go index 1980dfaa378..afba1afa48b 100644 --- a/deployment/common/changeset/transfer_to_mcms_with_timelock.go +++ b/deployment/common/changeset/transfer_to_mcms_with_timelock.go @@ -211,3 +211,34 @@ func TransferToDeployer(e deployment.Environment, cfg TransferToDeployerConfig) e.Logger.Infof("deployer key accepted ownership tx %s", tx.Hash().Hex()) return deployment.ChangesetOutput{}, nil } + +var _ deployment.ChangeSet[RenounceTimelockDeployerConfig] = RenounceTimelockDeployer + +type RenounceTimelockDeployerConfig struct { + ChainSel uint64 +} + +// RenounceTimelockDeployer revokes the deployer key from administering the contract. +func RenounceTimelockDeployer(e deployment.Environment, cfg RenounceTimelockDeployerConfig) (deployment.ChangesetOutput, error) { + contracts, err := MaybeLoadMCMSWithTimelockState(e, []uint64{cfg.ChainSel}) + if err != nil { + return deployment.ChangesetOutput{}, err + } + tl := contracts[cfg.ChainSel].Timelock + if tl == nil { + return deployment.ChangesetOutput{}, fmt.Errorf("timelock not found on chain %d", cfg.ChainSel) + } + admin, err := tl.ADMINROLE(&bind.CallOpts{}) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to get admin role: %w", err) + } + tx, err := tl.RenounceRole(e.Chains[cfg.ChainSel].DeployerKey, admin, e.Chains[cfg.ChainSel].DeployerKey.From) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to revoke deployer key: %w", err) + } + if _, err := deployment.ConfirmIfNoError(e.Chains[cfg.ChainSel], tx, err); err != nil { + return deployment.ChangesetOutput{}, err + } + e.Logger.Infof("revoked deployer key from owning contract %s", tl.Address().Hex()) + return deployment.ChangesetOutput{}, nil +} diff --git a/deployment/common/changeset/transfer_to_mcms_with_timelock_test.go b/deployment/common/changeset/transfer_to_mcms_with_timelock_test.go index daf4309398f..49c46aab5f0 100644 --- a/deployment/common/changeset/transfer_to_mcms_with_timelock_test.go +++ b/deployment/common/changeset/transfer_to_mcms_with_timelock_test.go @@ -1,8 +1,10 @@ package changeset import ( + "math/big" "testing" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -78,3 +80,59 @@ func TestTransferToMCMSWithTimelock(t *testing.T) { require.NoError(t, err) require.Equal(t, e.Chains[chain1].DeployerKey.From, o) } + +func TestRenounceTimelockDeployer(t *testing.T) { + lggr := logger.TestLogger(t) + e := memory.NewMemoryEnvironment(t, lggr, 0, memory.MemoryEnvironmentConfig{ + Chains: 1, + Nodes: 1, + }) + chain1 := e.AllChainSelectors()[0] + e, err := ApplyChangesets(t, e, nil, []ChangesetApplication{ + { + Changeset: WrapChangeSet(DeployMCMSWithTimelock), + Config: map[uint64]types.MCMSWithTimelockConfig{ + chain1: proposalutils.SingleGroupTimelockConfig(t), + }, + }, + }) + require.NoError(t, err) + addrs, err := e.ExistingAddresses.AddressesForChain(chain1) + require.NoError(t, err) + + state, err := MaybeLoadMCMSWithTimelockChainState(e.Chains[chain1], addrs) + require.NoError(t, err) + + tl := state.Timelock + require.NotNil(t, tl) + + adminRole, err := tl.ADMINROLE(nil) + require.NoError(t, err) + + r, err := tl.GetRoleMemberCount(&bind.CallOpts{}, adminRole) + require.NoError(t, err) + require.Equal(t, int64(2), r.Int64()) + + // Revoke Deployer + e, err = ApplyChangesets(t, e, nil, []ChangesetApplication{ + { + Changeset: WrapChangeSet(RenounceTimelockDeployer), + Config: RenounceTimelockDeployerConfig{ + ChainSel: chain1, + }, + }, + }) + require.NoError(t, err) + + // Check that the deployer is no longer an admin + r, err = tl.GetRoleMemberCount(&bind.CallOpts{}, adminRole) + require.NoError(t, err) + require.Equal(t, int64(1), r.Int64()) + + // Retrieve the admin address + admin, err := tl.GetRoleMember(&bind.CallOpts{}, adminRole, big.NewInt(0)) + require.NoError(t, err) + + // Check that the admin is the timelock + require.Equal(t, tl.Address(), admin) +}