Skip to content

Commit 325b4fd

Browse files
Connect EVM with EVM: Add support for skipping FQ v2 config if already configured during migration (#21665)
* Connect EVM with EVM: Add support for skipping FQ v2 config if already configured during migration * Exclude updating 1.6 FQ if the destCfg is already present --------- Co-authored-by: simsonraj <simsonraj.easvarasakthi@smartcontract.com>
1 parent d351043 commit 325b4fd

2 files changed

Lines changed: 196 additions & 1 deletion

File tree

deployment/ccip/changeset/v1_6/cs_update_bidirectional_lanes.go

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/Masterminds/semver/v3"
99

10+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1011
"github.com/ethereum/go-ethereum/common"
1112

1213
fqv2ops "github.com/smartcontractkit/chainlink-ccip/chains/evm/deployment/v2_0_0/operations/fee_quoter"
@@ -251,7 +252,14 @@ func UpdateLanesLogic(e cldf.Environment, mcmsConfig *proposalutils.TimelockConf
251252
v2FeeQuoterChains[chainSel] = struct{}{}
252253
continue
253254
}
254-
v1FeeQuoterDestsUpdates[chainSel] = update
255+
filtered, err := FilterOutExistingDestChainConfigs(e, update.Address, chainSel, update.CallInput)
256+
if err != nil {
257+
return cldf.ChangesetOutput{}, err
258+
}
259+
if len(filtered) > 0 {
260+
update.CallInput = filtered
261+
v1FeeQuoterDestsUpdates[chainSel] = update
262+
}
255263
}
256264
for chainSel, update := range feeQuoterPricesInput.UpdatesByChain {
257265
version, ok := feeQuoterVersionsByChain[chainSel]
@@ -308,6 +316,10 @@ func UpdateLanesLogic(e cldf.Environment, mcmsConfig *proposalutils.TimelockConf
308316
if err != nil {
309317
return cldf.ChangesetOutput{}, fmt.Errorf("failed to convert v1.6 fee quoter destination updates for chain %d: %w", chainSel, err)
310318
}
319+
destCfgs, err = FilterOutExistingDestChainConfigs(e, dests.Address, chainSel, destCfgs)
320+
if err != nil {
321+
return cldf.ChangesetOutput{}, err
322+
}
311323
fqUpdate.DestChainConfigs = destCfgs
312324
}
313325
if prices, ok := feeQuoterPricesInput.UpdatesByChain[chainSel]; ok {
@@ -349,6 +361,75 @@ func UpdateLanesLogic(e cldf.Environment, mcmsConfig *proposalutils.TimelockConf
349361
return output, nil
350362
}
351363

364+
// destChainConfigType constrains the types accepted by FilterOutExistingDestChainConfigs.
365+
type destChainConfigType interface {
366+
fee_quoter.FeeQuoterDestChainConfigArgs | fqv2ops.DestChainConfigArgs
367+
}
368+
369+
// FilterOutExistingDestChainConfigs removes destination chain configs where the destination
370+
// is already enabled on-chain. It automatically selects the correct FeeQuoter binding
371+
// based on the concrete config type.
372+
func FilterOutExistingDestChainConfigs[T destChainConfigType](
373+
e cldf.Environment,
374+
fqAddr common.Address,
375+
chainSel uint64,
376+
destCfgs []T,
377+
) ([]T, error) {
378+
if len(destCfgs) == 0 {
379+
return destCfgs, nil
380+
}
381+
382+
var isDestEnabled func(T) (uint64, bool, error)
383+
384+
switch any(destCfgs[0]).(type) {
385+
case fee_quoter.FeeQuoterDestChainConfigArgs:
386+
fq, err := fee_quoter.NewFeeQuoter(fqAddr, e.BlockChains.EVMChains()[chainSel].Client)
387+
if err != nil {
388+
return nil, fmt.Errorf("failed to bind FeeQuoter on chain %d: %w", chainSel, err)
389+
}
390+
isDestEnabled = func(cfg T) (uint64, bool, error) {
391+
destSel := any(cfg).(fee_quoter.FeeQuoterDestChainConfigArgs).DestChainSelector
392+
onChain, err := fq.GetDestChainConfig(&bind.CallOpts{Context: e.GetContext()}, destSel)
393+
if err != nil {
394+
return destSel, false, err
395+
}
396+
return destSel, onChain.IsEnabled, nil
397+
}
398+
case fqv2ops.DestChainConfigArgs:
399+
fq, err := fqv2ops.NewFeeQuoterContract(fqAddr, e.BlockChains.EVMChains()[chainSel].Client)
400+
if err != nil {
401+
return nil, fmt.Errorf("failed to bind v2 FeeQuoter on chain %d: %w", chainSel, err)
402+
}
403+
isDestEnabled = func(cfg T) (uint64, bool, error) {
404+
destSel := any(cfg).(fqv2ops.DestChainConfigArgs).DestChainSelector
405+
onChain, err := fq.GetDestChainConfig(&bind.CallOpts{Context: e.GetContext()}, destSel)
406+
if err != nil {
407+
return destSel, false, err
408+
}
409+
return destSel, onChain.IsEnabled, nil
410+
}
411+
}
412+
413+
filtered := make([]T, 0, len(destCfgs))
414+
for _, destCfg := range destCfgs {
415+
destSel, enabled, err := isDestEnabled(destCfg)
416+
if err != nil {
417+
return nil, fmt.Errorf("failed to query existing dest chain config on chain %d for dest %d: %w",
418+
chainSel, destSel, err)
419+
}
420+
if enabled {
421+
e.Logger.Infow("skipping dest chain config already present on FeeQuoter",
422+
"sourceChain", chainSel,
423+
"destChain", destSel,
424+
)
425+
continue
426+
}
427+
filtered = append(filtered, destCfg)
428+
}
429+
430+
return filtered, nil
431+
}
432+
352433
func ConvertV16FeeQuoterDestUpdatesToV2(in []fee_quoter.FeeQuoterDestChainConfigArgs) ([]fqv2ops.DestChainConfigArgs, error) {
353434
out := make([]fqv2ops.DestChainConfigArgs, 0, len(in))
354435
for _, cfg := range in {

deployment/ccip/changeset/v1_6/cs_update_bidirectional_lanes_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,120 @@ func TestUpdateBidirectionalLanesChangesetWithV2FeeQuoter(t *testing.T) {
543543
}
544544
}
545545

546+
func TestFilterOutExistingDestChainConfigs(t *testing.T) {
547+
t.Parallel()
548+
549+
deployedEnvironment, _ := testhelpers.NewMemoryEnvironment(t, func(testCfg *testhelpers.TestConfigs) {
550+
testCfg.Chains = 2
551+
})
552+
e := deployedEnvironment.Env
553+
554+
state, err := stateview.LoadOnchainState(e)
555+
require.NoError(t, err, "must load onchain state")
556+
557+
selectors := e.BlockChains.ListChainSelectors(cldf_chain.WithFamily(chain_selectors.FamilyEVM))
558+
require.Len(t, selectors, 2, "must have 2 chains")
559+
560+
chainSel := selectors[0]
561+
otherChainSel := selectors[1]
562+
evmChain := e.BlockChains.EVMChains()[chainSel]
563+
564+
parsedABI, err := abi.JSON(strings.NewReader(fqv2ops.FeeQuoterABI))
565+
require.NoError(t, err, "must parse v2 FeeQuoter ABI")
566+
567+
// Deploy a v2 FeeQuoter, then configure one destination (otherChainSel) after deployment.
568+
// Constructor-time dest config with sparse fields reverts, so we apply it post-deploy.
569+
fqV2Addr, tx, _, err := bind.DeployContract(
570+
evmChain.DeployerKey,
571+
parsedABI,
572+
common.FromHex(fqv2ops.FeeQuoterBin),
573+
evmChain.Client,
574+
fqv2ops.StaticConfig{
575+
MaxFeeJuelsPerMsg: big.NewInt(1e18),
576+
LinkToken: state.Chains[chainSel].LinkToken.Address(),
577+
},
578+
[]common.Address{evmChain.DeployerKey.From},
579+
[]fqv2ops.TokenTransferFeeConfigArgs{},
580+
[]fqv2ops.DestChainConfigArgs{},
581+
)
582+
require.NoError(t, err, "must deploy v2 FeeQuoter")
583+
584+
_, err = evmChain.Confirm(tx)
585+
require.NoError(t, err, "must confirm v2 FeeQuoter deployment")
586+
587+
fqV2, err := fqv2ops.NewFeeQuoterContract(fqV2Addr, evmChain.Client)
588+
require.NoError(t, err, "must bind v2 FeeQuoter")
589+
590+
// Convert a default v1.6 config to v2 format and apply it so otherChainSel is already enabled
591+
v2Cfgs, err := v1_6.ConvertV16FeeQuoterDestUpdatesToV2([]fee_quoter.FeeQuoterDestChainConfigArgs{
592+
{
593+
DestChainSelector: otherChainSel,
594+
DestChainConfig: v1_6.DefaultFeeQuoterDestChainConfig(true),
595+
},
596+
})
597+
require.NoError(t, err, "must convert v1.6 dest config to v2")
598+
599+
applyTx, err := fqV2.ApplyDestChainConfigUpdates(evmChain.DeployerKey, v2Cfgs)
600+
require.NoError(t, err, "must apply dest chain config")
601+
_, err = evmChain.Confirm(applyTx)
602+
require.NoError(t, err, "must confirm dest chain config tx")
603+
604+
unconfiguredChainSel := uint64(999)
605+
606+
// Call FilterOutExistingDestChainConfigs with both destinations
607+
input := []fqv2ops.DestChainConfigArgs{
608+
{
609+
DestChainSelector: otherChainSel,
610+
DestChainConfig: fqv2ops.DestChainConfig{IsEnabled: true, MaxDataBytes: 50_000},
611+
},
612+
{
613+
DestChainSelector: unconfiguredChainSel,
614+
DestChainConfig: fqv2ops.DestChainConfig{IsEnabled: true, MaxDataBytes: 60_000},
615+
},
616+
}
617+
618+
filtered, err := v1_6.FilterOutExistingDestChainConfigs(e, fqV2Addr, chainSel, input)
619+
require.NoError(t, err, "FilterOutExistingDestChainConfigs must not error")
620+
621+
// Only the unconfigured chain should remain
622+
require.Len(t, filtered, 1, "must filter out the already-enabled destination")
623+
assert.Equal(t, unconfiguredChainSel, filtered[0].DestChainSelector,
624+
"remaining entry must be the unconfigured chain")
625+
assert.Equal(t, uint32(60_000), filtered[0].DestChainConfig.MaxDataBytes,
626+
"remaining entry must preserve original config")
627+
628+
// Configure otherChainSel on the v1.6 FeeQuoter so it's already enabled
629+
fqV16 := state.Chains[chainSel].FeeQuoter
630+
applyTxV16, err := fqV16.ApplyDestChainConfigUpdates(evmChain.DeployerKey, []fee_quoter.FeeQuoterDestChainConfigArgs{
631+
{
632+
DestChainSelector: otherChainSel,
633+
DestChainConfig: v1_6.DefaultFeeQuoterDestChainConfig(true),
634+
},
635+
})
636+
require.NoError(t, err, "must apply v1.6 dest chain config")
637+
_, err = evmChain.Confirm(applyTxV16)
638+
require.NoError(t, err, "must confirm v1.6 dest chain config tx")
639+
640+
// Call FilterOutExistingDestChainConfigs with v1.6 types
641+
v16Input := []fee_quoter.FeeQuoterDestChainConfigArgs{
642+
{
643+
DestChainSelector: otherChainSel,
644+
DestChainConfig: v1_6.DefaultFeeQuoterDestChainConfig(true),
645+
},
646+
{
647+
DestChainSelector: unconfiguredChainSel,
648+
DestChainConfig: v1_6.DefaultFeeQuoterDestChainConfig(true),
649+
},
650+
}
651+
652+
filteredV16, err := v1_6.FilterOutExistingDestChainConfigs(e, fqV16.Address(), chainSel, v16Input)
653+
require.NoError(t, err, "FilterOutExistingDestChainConfigs must not error")
654+
655+
require.Len(t, filteredV16, 1, "must filter out the already-enabled destination")
656+
assert.Equal(t, unconfiguredChainSel, filteredV16[0].DestChainSelector,
657+
"remaining entry must be the unconfigured chain")
658+
}
659+
546660
func TestUpdateBidirectionalLanesChangesetWithV2FeeQuoterWithMCMS(t *testing.T) {
547661
t.Parallel()
548662

0 commit comments

Comments
 (0)