@@ -6,11 +6,13 @@ import (
66 "crypto/rand"
77 "errors"
88 "fmt"
9+ "math/big"
910 "os"
1011 "slices"
1112 "strings"
1213 "time"
1314
15+ "github.com/ethereum/go-ethereum/accounts/abi/bind/v2"
1416 "github.com/ethereum/go-ethereum/common"
1517 gethtypes "github.com/ethereum/go-ethereum/core/types"
1618 "github.com/ethereum/go-ethereum/crypto"
@@ -32,6 +34,7 @@ import (
3234 "go.uber.org/zap/zapcore"
3335
3436 "github.com/smartcontractkit/chainlink-deployments-framework/chain"
37+ "github.com/smartcontractkit/chainlink-deployments-framework/datastore"
3538 cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
3639 cldf_chains "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/chains"
3740 cldf_config "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config"
@@ -563,40 +566,12 @@ func buildTimelockExecuteOperationV2Cmd(lggr logger.Logger, domain cldf_domain.D
563566 return fmt .Errorf ("failed to create TimelockExecutable: %w" , err )
564567 }
565568
566- // Get AddressBook
567- envdir := domain .EnvDir (cfgv2 .envStr )
568- ab , err := envdir .AddressBook ()
569+ executeOptions , err := timelockExecuteOptions (cmd .Context (), lggr , domain , cfgv2 )
569570 if err != nil {
570- return fmt .Errorf ("failed to load address book : %w" , err )
571+ return fmt .Errorf ("failed to get timelock execute options : %w" , err )
571572 }
572573
573- // Get Chain Contracts
574- contracts , err := ab .AddressesForChain (cfgv2 .chainSelector )
575- if err != nil {
576- return fmt .Errorf ("failed to get contracts for chain %d: %w" , cfgv2 .chainSelector , err )
577- }
578-
579- // Get CallProxy address
580- callProxyAddress := ""
581- for address , contract := range contracts {
582- if contract .Type == analyzer .CallProxy {
583- // TODO: this assumes there is only one CallProxy per chain.
584- // What happens if there are multiple? I think its safe to assume
585- // there is only one for now but that might not always be the case.
586- // Maybe we can do a check on the timelock to see if the found CallProxy
587- // has the executor role?
588- callProxyAddress = address
589- break
590- }
591- }
592-
593- // If there is no CallProxy, we don't need to pass it to the executor
594- opts := []mcms.Option {}
595- if callProxyAddress != "" {
596- opts = append (opts , mcms .WithCallProxy (callProxyAddress ))
597- }
598-
599- result , err := executable .Execute (context .Background (), index , opts ... )
574+ result , err := executable .Execute (cmd .Context (), index , executeOptions ... )
600575 if err != nil {
601576 return fmt .Errorf ("failed to execute operation %d: %w" , index , err )
602577 }
@@ -1267,37 +1242,9 @@ func timelockExecuteChainCommand(ctx context.Context, lggr logger.Logger, cfg *c
12671242 return fmt .Errorf ("failed to create TimelockExecutable: %w" , err )
12681243 }
12691244
1270- // Get AddressBook
1271- envdir := domain .EnvDir (cfg .envStr )
1272- ab , err := envdir .AddressBook ()
1245+ executeOptions , err := timelockExecuteOptions (ctx , lggr , domain , cfg )
12731246 if err != nil {
1274- return fmt .Errorf ("failed to load address book: %w" , err )
1275- }
1276-
1277- // Get Chain Contracts
1278- contracts , err := ab .AddressesForChain (cfg .chainSelector )
1279- if err != nil {
1280- return fmt .Errorf ("failed to get contracts for chain %d: %w" , cfg .chainSelector , err )
1281- }
1282-
1283- // Get CallProxy address
1284- callProxyAddress := ""
1285- for address , contract := range contracts {
1286- if contract .Type == analyzer .CallProxy {
1287- // TODO: this assumes there is only one CallProxy per chain.
1288- // What happens if there are multiple? I think its safe to assume
1289- // there is only one for now but that might not always be the case.
1290- // Maybe we can do a check on the timelock to see if the found CallProxy
1291- // has the executor role?
1292- callProxyAddress = address
1293- break
1294- }
1295- }
1296-
1297- // If there is no CallProxy, we don't need to pass it to the executor
1298- opts := []mcms.Option {}
1299- if callProxyAddress != "" {
1300- opts = append (opts , mcms .WithCallProxy (callProxyAddress ))
1247+ return fmt .Errorf ("failed to get timelock execute options: %w" , err )
13011248 }
13021249
13031250 for i := range cfg .timelockProposal .Operations {
@@ -1312,7 +1259,7 @@ func timelockExecuteChainCommand(ctx context.Context, lggr logger.Logger, cfg *c
13121259 return fmt .Errorf ("operation %d is not ready to be executed: %w" , i , err )
13131260 }
13141261
1315- result , err := executable .Execute (ctx , i , opts ... )
1262+ result , err := executable .Execute (ctx , i , executeOptions ... )
13161263 if err != nil {
13171264 return fmt .Errorf ("failed to execute operation %d: %w" , i , err )
13181265 }
@@ -1569,3 +1516,84 @@ func getProposalSigners(
15691516
15701517 return addresses , nil
15711518}
1519+
1520+ func timelockExecuteOptions (
1521+ ctx context.Context , lggr logger.Logger , _ cldf_domain.Domain , cfg * cfgv2 ,
1522+ ) ([]mcms.Option , error ) {
1523+ options := []mcms.Option {}
1524+
1525+ family , err := chainsel .GetSelectorFamily (cfg .chainSelector )
1526+ if err != nil {
1527+ return nil , fmt .Errorf ("failed to get selector family: %w" , err )
1528+ }
1529+ if family == chainsel .FamilyEVM {
1530+ err := addCallProxyOption (ctx , lggr , cfg , & options )
1531+ if err != nil {
1532+ return options , fmt .Errorf ("failed to add CallProxy option: %w" , err )
1533+ }
1534+ }
1535+
1536+ return options , nil
1537+ }
1538+
1539+ func addCallProxyOption (
1540+ ctx context.Context , lggr logger.Logger , cfg * cfgv2 , options * []mcms.Option ,
1541+ ) error {
1542+ timelockAddress , ok := cfg .timelockProposal .TimelockAddresses [types .ChainSelector (cfg .chainSelector )]
1543+ if ! ok {
1544+ return fmt .Errorf ("failed to find timelock address for chain selector %d" , cfg .chainSelector )
1545+ }
1546+
1547+ chain , ok := cfg .blockchains .EVMChains ()[cfg .chainSelector ]
1548+ if ! ok {
1549+ return fmt .Errorf ("failed to find evm chain for selector %d" , cfg .chainSelector )
1550+ }
1551+
1552+ timelockContract , err := bindings .NewRBACTimelock (common .HexToAddress (timelockAddress ), chain .Client )
1553+ if err != nil {
1554+ return fmt .Errorf ("failed to create timelock contract with address %v: %w" , timelockAddress , err )
1555+ }
1556+
1557+ callOpts := & bind.CallOpts {Context : ctx }
1558+
1559+ role , err := timelockContract .EXECUTORROLE (callOpts )
1560+ if err != nil {
1561+ return fmt .Errorf ("failed to get executor role from timelock contract: %w" , err )
1562+ }
1563+ memberCount , err := timelockContract .GetRoleMemberCount (callOpts , role )
1564+ if err != nil {
1565+ return fmt .Errorf ("failed to get executor member count from timelock contract: %w" , err )
1566+ }
1567+ for i := range memberCount .Int64 () {
1568+ executorAddress , ierr := timelockContract .GetRoleMember (callOpts , role , big .NewInt (i ))
1569+ if ierr != nil {
1570+ return fmt .Errorf ("failed to get executor address from timelock contract: %w" , ierr )
1571+ }
1572+
1573+ // search for executor address in the datastore
1574+ callProxyRefs := cfg .env .DataStore .Addresses ().Filter (
1575+ datastore .AddressRefByAddress (executorAddress .Hex ()),
1576+ datastore .AddressRefByChainSelector (cfg .chainSelector ),
1577+ datastore .AddressRefByType ("CallProxy" ))
1578+
1579+ if len (callProxyRefs ) > 0 {
1580+ * options = append (* options , mcms .WithCallProxy (executorAddress .Hex ()))
1581+ return nil
1582+ }
1583+
1584+ // if not found, search in the addressbook
1585+ addressesForChain , ierr := cfg .env .ExistingAddresses .AddressesForChain (cfg .chainSelector ) //nolint:staticcheck
1586+ if ierr != nil {
1587+ lggr .Infof ("unable to get addresses for chain %d in addressbook: %s" , cfg .chainSelector , ierr .Error ())
1588+ continue // ignore error; some domains don't use the addressbook anymore
1589+ }
1590+ for address , typeAndVersion := range addressesForChain {
1591+ if address == executorAddress .Hex () && typeAndVersion .Type == "CallProxy" {
1592+ * options = append (* options , mcms .WithCallProxy (executorAddress .Hex ()))
1593+ return nil
1594+ }
1595+ }
1596+ }
1597+
1598+ return fmt .Errorf ("failed to find call proxy contract for timelock %v" , timelockAddress )
1599+ }
0 commit comments