Skip to content

Commit f128632

Browse files
committed
fix: re add operation.go
1 parent 2d3f746 commit f128632

2 files changed

Lines changed: 144 additions & 26 deletions

File tree

mcms/evm/grant-role/operation.go

Lines changed: 122 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,139 @@
11
package evmgrantrole
22

33
import (
4+
"errors"
5+
"fmt"
6+
"math/big"
7+
48
"github.com/Masterminds/semver/v3"
59
"github.com/ethereum/go-ethereum/accounts/abi/bind"
610
"github.com/ethereum/go-ethereum/common"
711
"github.com/ethereum/go-ethereum/core/types"
8-
12+
cldf_evm "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm"
13+
opscontract "github.com/smartcontractkit/chainlink-deployments-framework/chain/evm/operations2/contract"
14+
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
915
mcmscontracts "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/contracts/mcms"
10-
"github.com/smartcontractkit/mcms/sdk/evm/bindings"
16+
cldfproposalutils "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalutils"
17+
"github.com/smartcontractkit/chainlink-deployments-framework/operations"
18+
mcmssdk "github.com/smartcontractkit/mcms/sdk"
19+
mcmsevm "github.com/smartcontractkit/mcms/sdk/evm"
20+
mcmsbindings "github.com/smartcontractkit/mcms/sdk/evm/bindings"
1121

12-
evmops "github.com/smartcontractkit/cld-changesets/legacy/mcms/oputils"
22+
"github.com/smartcontractkit/cld-changesets/mcms/evm/internal/gasboost"
1323
)
1424

15-
// OpGrantRoleInput is the input to OpGrantRole.
16-
type OpGrantRoleInput struct {
17-
Account common.Address `json:"account"`
18-
RoleID [32]byte `json:"roleID"`
25+
type GrantRoleTarget struct {
26+
Timelock common.Address `json:"timelock"`
27+
Role mcmssdk.TimelockRole `json:"role"`
28+
Address common.Address `json:"address"`
29+
}
30+
31+
type OpEVMGrantRoleInput struct {
32+
Target GrantRoleTarget `json:"target"`
33+
NoSend bool `json:"noSend"`
34+
GasPrice uint64 `json:"gasPrice"`
35+
GasLimit uint64 `json:"gasLimit"`
1936
}
2037

21-
// OpGrantRole grants the given role to the given account on the EVM Timelock contract.
22-
// TODO: refactor to use mcms lib
23-
var OpGrantRole = evmops.NewEVMCallOperation(
38+
func (in OpEVMGrantRoleInput) GasBoostValues() (gasLimit, gasPrice uint64) {
39+
return in.GasLimit, in.GasPrice
40+
}
41+
42+
func (in OpEVMGrantRoleInput) WithGasBoost(gasLimit, gasPrice uint64) OpEVMGrantRoleInput {
43+
in.GasLimit = gasLimit
44+
in.GasPrice = gasPrice
45+
46+
return in
47+
}
48+
49+
var OpEVMGrantRole = operations.NewOperation(
2450
"evm-timelock-grant-role",
2551
semver.MustParse("1.0.0"),
26-
"Grants the specified role to the given account on the EVM Timelock contract",
27-
bindings.RBACTimelockABI,
28-
mcmscontracts.RBACTimelock,
29-
bindings.NewRBACTimelock,
30-
func(timelock *bindings.RBACTimelock, opts *bind.TransactOpts, input OpGrantRoleInput) (*types.Transaction, error) {
31-
return timelock.GrantRole(opts, input.RoleID, input.Account)
52+
"Grants an RBACTimelock role to one EVM address via the MCMS SDK timelock configurer",
53+
func(b operations.Bundle, deps cldf_evm.Chain, in OpEVMGrantRoleInput) (opscontract.WriteOutput, error) {
54+
if !in.NoSend && deps.DeployerKey == nil {
55+
return opscontract.WriteOutput{}, fmt.Errorf("missing deployer key for chain %d", deps.Selector)
56+
}
57+
58+
var opts *bind.TransactOpts
59+
if in.NoSend {
60+
opts = cldf.SimTransactOpts()
61+
} else {
62+
opts = gasboost.CloneTransactOptsWithGas(deps.DeployerKey, in.GasLimit, in.GasPrice)
63+
}
64+
if opts == nil {
65+
return opscontract.WriteOutput{}, fmt.Errorf("failed to build transact opts for chain %d", deps.Selector)
66+
}
67+
opts.Context = b.GetContext()
68+
69+
configurer := mcmsevm.NewTimelockConfigurer(deps.Client, opts)
70+
res, err := configurer.GrantRole(b.GetContext(), in.Target.Timelock.Hex(), in.Target.Role, in.Target.Address.Hex())
71+
if err != nil {
72+
return opscontract.WriteOutput{}, fmt.Errorf("failed to grant role %s to %s on %s: %w",
73+
in.Target.Role.String(), in.Target.Address.Hex(), in.Target.Timelock.Hex(), err)
74+
}
75+
76+
tx, err := rawTransaction(res.RawData)
77+
if err != nil {
78+
return opscontract.WriteOutput{}, err
79+
}
80+
81+
out := writeOutputFromGrant(deps.Selector, in.Target.Timelock, tx)
82+
if in.NoSend {
83+
return out, nil
84+
}
85+
86+
if _, err = cldf.ConfirmIfNoErrorWithABI(deps, tx, mcmsbindings.RBACTimelockABI, nil); err != nil {
87+
return opscontract.WriteOutput{}, fmt.Errorf("failed to confirm grant role tx against %s: %w", in.Target.Timelock.Hex(), err)
88+
}
89+
b.Logger.Infow("GrantRole tx confirmed", "txHash", tx.Hash().Hex(), "timelock", in.Target.Timelock.Hex())
90+
91+
out.ExecInfo = &opscontract.ExecInfo{Hash: tx.Hash().Hex()}
92+
93+
return out, nil
3294
},
3395
)
96+
97+
func writeOutputFromGrant(chainSelector uint64, timelock common.Address, tx *types.Transaction) opscontract.WriteOutput {
98+
return opscontract.WriteOutput{
99+
ChainSelector: chainSelector,
100+
Tx: mcmsevm.NewTransaction(
101+
timelock,
102+
tx.Data(),
103+
big.NewInt(0),
104+
string(mcmscontracts.RBACTimelock),
105+
[]string{},
106+
),
107+
}
108+
}
109+
110+
func rawTransaction(raw any) (*types.Transaction, error) {
111+
switch tx := raw.(type) {
112+
case *types.Transaction:
113+
return tx, nil
114+
default:
115+
return nil, fmt.Errorf("unexpected raw data type %T from GrantRole", raw)
116+
}
117+
}
118+
119+
func retryGrantRoleWithGasBoost(cfg *cldfproposalutils.GasBoostConfig) operations.ExecuteOption[OpEVMGrantRoleInput, cldf_evm.Chain] {
120+
if cfg == nil {
121+
return operations.WithRetry[OpEVMGrantRoleInput, cldf_evm.Chain]()
122+
}
123+
124+
return gasboost.RetryWithGasBoost[OpEVMGrantRoleInput](cfg)
125+
}
126+
127+
func validateGrantRoleTarget(target GrantRoleTarget) error {
128+
if target.Timelock == (common.Address{}) {
129+
return errors.New("timelock address must not be zero")
130+
}
131+
if !target.Role.Valid() {
132+
return errors.New("role is unsupported")
133+
}
134+
if target.Address == (common.Address{}) {
135+
return errors.New("address must not be zero")
136+
}
137+
138+
return nil
139+
}

mcms/evm/grant-role/operation_test.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,28 @@ import (
1313
"github.com/smartcontractkit/chainlink-deployments-framework/engine/test/runtime"
1414
"github.com/smartcontractkit/chainlink-deployments-framework/operations"
1515
"github.com/smartcontractkit/chainlink-deployments-framework/pkg/logger"
16+
mcmssdk "github.com/smartcontractkit/mcms/sdk"
1617
mcmsevmsdk "github.com/smartcontractkit/mcms/sdk/evm"
1718

18-
"github.com/smartcontractkit/cld-changesets/internal/mcmsrole"
19-
evmops "github.com/smartcontractkit/cld-changesets/legacy/mcms/oputils"
2019
timelockops "github.com/smartcontractkit/cld-changesets/mcms/evm/deploy/v1_0_0/operations/rbac_timelock"
2120
evmgrantrole "github.com/smartcontractkit/cld-changesets/mcms/evm/grant-role"
2221
)
2322

23+
func TestOpEVMGrantRoleInputGasOverridable(t *testing.T) {
24+
t.Parallel()
25+
26+
in := evmgrantrole.OpEVMGrantRoleInput{GasLimit: 100, GasPrice: 200}
27+
gotLimit, gotPrice := in.GasBoostValues()
28+
require.Equal(t, uint64(100), gotLimit)
29+
require.Equal(t, uint64(200), gotPrice)
30+
31+
boosted := in.WithGasBoost(500, 600)
32+
require.Equal(t, uint64(500), boosted.GasLimit)
33+
require.Equal(t, uint64(600), boosted.GasPrice)
34+
}
35+
2436
// TestOpGrantRole deploys a timelock with the deployer as admin and grants the
25-
// executor role to a fresh address, asserting it shows up in the inspector.
37+
// executor role to a fresh address via the MCMS SDK timelock configurer.
2638
func TestOpGrantRole(t *testing.T) {
2739
t.Parallel()
2840

@@ -53,16 +65,16 @@ func TestOpGrantRole(t *testing.T) {
5365
timelockAddr := common.HexToAddress(tlReport.Output.Address)
5466

5567
grantee := common.HexToAddress("0x00000000000000000000000000000000000000aa")
56-
_, err = operations.ExecuteOperation(bundle, evmgrantrole.OpGrantRole, chain,
57-
evmops.EVMCallInput[evmgrantrole.OpGrantRoleInput]{
58-
ChainSelector: sel,
59-
Address: timelockAddr,
60-
CallInput: evmgrantrole.OpGrantRoleInput{
61-
Account: grantee,
62-
RoleID: [32]byte(mcmsrole.ExecutorRole.ID),
68+
report, err := operations.ExecuteOperation(bundle, evmgrantrole.OpEVMGrantRole, chain,
69+
evmgrantrole.OpEVMGrantRoleInput{
70+
Target: evmgrantrole.GrantRoleTarget{
71+
Timelock: timelockAddr,
72+
Role: mcmssdk.TimelockRoleExecutor,
73+
Address: grantee,
6374
},
6475
})
6576
require.NoError(t, err)
77+
require.True(t, report.Output.Executed())
6678

6779
executors, err := mcmsevmsdk.NewTimelockInspector(chain.Client).GetExecutors(t.Context(), timelockAddr.Hex())
6880
require.NoError(t, err)

0 commit comments

Comments
 (0)