Skip to content

Add changeset to add bidirectional lanes #17015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 3, 2025
Merged
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
246 changes: 246 additions & 0 deletions deployment/ccip/changeset/v1_6/cs_update_bidirectional_lanes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package v1_6

import (
"fmt"
"math/big"

"github.com/smartcontractkit/mcms"

"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_6_0/fee_quoter"
)

// UpdateBidirectionalLanesChangeset enables or disables multiple bidirectional lanes on CCIP.
// It batches all lane updates into a single MCMS proposal.
var UpdateBidirectionalLanesChangeset = deployment.CreateChangeSet(updateBidirectionalLanesLogic, updateBidirectionalLanesPrecondition)

// BidirectionalLaneDefinition indicates two chains that we want to connect.
type BidirectionalLaneDefinition struct {
// IsDisabled indicates if the lane should be disabled.
// We use IsDisabled instead of IsEnabled because enabling a lane should be the default action.
IsDisabled bool
Chains [2]ChainDefinition
}

// laneDefinition defines a lane between source and destination.
type laneDefinition struct {
// Source defines the source chain.
Source ChainDefinition
// Dest defines the destination chain.
Dest ChainDefinition
}

// UpdateBidirectionalLanesConfig is a configuration struct for UpdateBidirectionalLanesChangeset.
type UpdateBidirectionalLanesConfig struct {
// MCMSConfig defines the MCMS configuration for the changeset.
MCMSConfig *proposalutils.TimelockConfig
// Lanes describes the lanes that we want to create.
Lanes []BidirectionalLaneDefinition
// TestRouter indicates if we want to enable these lanes on the test router.
TestRouter bool
}

type UpdateBidirectionalLanesChangesetConfigs struct {
UpdateFeeQuoterDestsConfig UpdateFeeQuoterDestsConfig
UpdateFeeQuoterPricesConfig UpdateFeeQuoterPricesConfig
UpdateOnRampDestsConfig UpdateOnRampDestsConfig
UpdateOffRampSourcesConfig UpdateOffRampSourcesConfig
UpdateRouterRampsConfig UpdateRouterRampsConfig
}

func (c UpdateBidirectionalLanesConfig) BuildConfigs() UpdateBidirectionalLanesChangesetConfigs {
onRampUpdatesByChain := make(map[uint64]map[uint64]OnRampDestinationUpdate)
offRampUpdatesByChain := make(map[uint64]map[uint64]OffRampSourceUpdate)
routerUpdatesByChain := make(map[uint64]RouterUpdates)
feeQuoterDestUpdatesByChain := make(map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig)
feeQuoterPriceUpdatesByChain := make(map[uint64]FeeQuoterPriceUpdatePerSource)

for _, lane := range c.Lanes {
isEnabled := !lane.IsDisabled
chainA := lane.Chains[0]
chainB := lane.Chains[1]

laneAToB := laneDefinition{
Source: chainA,
Dest: chainB,
}
laneBToA := laneDefinition{
Source: chainB,
Dest: chainA,
}

for _, laneDef := range []laneDefinition{laneAToB, laneBToA} {
// Setting the destination on the on ramp
if onRampUpdatesByChain[laneDef.Source.Selector] == nil {
onRampUpdatesByChain[laneDef.Source.Selector] = make(map[uint64]OnRampDestinationUpdate)
}
onRampUpdatesByChain[laneDef.Source.Selector][laneDef.Dest.Selector] = OnRampDestinationUpdate{
IsEnabled: isEnabled,
TestRouter: c.TestRouter,
AllowListEnabled: laneDef.Dest.AllowListEnabled,
}

// Setting the source on the off ramp
if offRampUpdatesByChain[laneDef.Dest.Selector] == nil {
offRampUpdatesByChain[laneDef.Dest.Selector] = make(map[uint64]OffRampSourceUpdate)
}
offRampUpdatesByChain[laneDef.Dest.Selector][laneDef.Source.Selector] = OffRampSourceUpdate{
IsEnabled: isEnabled,
TestRouter: c.TestRouter,
IsRMNVerificationDisabled: laneDef.Source.RMNVerificationDisabled,
}

// Setting the on ramp on the source router
routerUpdatesOnSource := routerUpdatesByChain[laneDef.Source.Selector]
if routerUpdatesByChain[laneDef.Source.Selector].OnRampUpdates == nil {
routerUpdatesOnSource.OnRampUpdates = make(map[uint64]bool)
}
routerUpdatesOnSource.OnRampUpdates[laneDef.Dest.Selector] = isEnabled
routerUpdatesByChain[laneDef.Source.Selector] = routerUpdatesOnSource

// Setting the off ramp on the dest router
routerUpdatesOnDest := routerUpdatesByChain[laneDef.Dest.Selector]
if routerUpdatesByChain[laneDef.Dest.Selector].OffRampUpdates == nil {
routerUpdatesOnDest.OffRampUpdates = make(map[uint64]bool)
}
routerUpdatesOnDest.OffRampUpdates[laneDef.Source.Selector] = isEnabled
routerUpdatesByChain[laneDef.Dest.Selector] = routerUpdatesOnDest

// Setting the fee quoter destination on the source chain
if feeQuoterDestUpdatesByChain[laneDef.Source.Selector] == nil {
feeQuoterDestUpdatesByChain[laneDef.Source.Selector] = make(map[uint64]fee_quoter.FeeQuoterDestChainConfig)
}
feeQuoterDestUpdatesByChain[laneDef.Source.Selector][laneDef.Dest.Selector] = laneDef.Dest.FeeQuoterDestChainConfig

// Setting the destination gas prices on the source chain
feeQuoterPriceUpdatesOnSource := feeQuoterPriceUpdatesByChain[laneDef.Source.Selector]
if feeQuoterPriceUpdatesOnSource.GasPrices == nil {
feeQuoterPriceUpdatesOnSource.GasPrices = make(map[uint64]*big.Int)
}
feeQuoterPriceUpdatesOnSource.GasPrices[laneDef.Dest.Selector] = laneDef.Dest.GasPrice
feeQuoterPriceUpdatesByChain[laneDef.Source.Selector] = feeQuoterPriceUpdatesOnSource
}
}

routerMCMSConfig := c.MCMSConfig
if c.TestRouter {
routerMCMSConfig = nil // Test router is never owned by MCMS
}

return UpdateBidirectionalLanesChangesetConfigs{
UpdateFeeQuoterDestsConfig: UpdateFeeQuoterDestsConfig{
MCMS: c.MCMSConfig,
UpdatesByChain: feeQuoterDestUpdatesByChain,
},
UpdateFeeQuoterPricesConfig: UpdateFeeQuoterPricesConfig{
MCMS: c.MCMSConfig,
PricesByChain: feeQuoterPriceUpdatesByChain,
},
UpdateOnRampDestsConfig: UpdateOnRampDestsConfig{
MCMS: c.MCMSConfig,
UpdatesByChain: onRampUpdatesByChain,
},
UpdateOffRampSourcesConfig: UpdateOffRampSourcesConfig{
MCMS: c.MCMSConfig,
UpdatesByChain: offRampUpdatesByChain,
},
UpdateRouterRampsConfig: UpdateRouterRampsConfig{
TestRouter: c.TestRouter,
MCMS: routerMCMSConfig,
UpdatesByChain: routerUpdatesByChain,
},
}
}

func updateBidirectionalLanesPrecondition(e deployment.Environment, c UpdateBidirectionalLanesConfig) error {
configs := c.BuildConfigs()
state, err := changeset.LoadOnchainState(e)
if err != nil {
return fmt.Errorf("failed to load onchain state: %w", err)
}

err = configs.UpdateFeeQuoterDestsConfig.Validate(e)
if err != nil {
return fmt.Errorf("failed to validate UpdateFeeQuoterDestsConfig: %w", err)
}

err = configs.UpdateFeeQuoterPricesConfig.Validate(e)
if err != nil {
return fmt.Errorf("failed to validate UpdateFeeQuoterPricesConfig: %w", err)
}

err = configs.UpdateOnRampDestsConfig.Validate(e)
if err != nil {
return fmt.Errorf("failed to validate UpdateOnRampDestsConfig: %w", err)
}

err = configs.UpdateOffRampSourcesConfig.Validate(e, state)
if err != nil {
return fmt.Errorf("failed to validate UpdateOffRampSourcesConfig: %w", err)
}

err = configs.UpdateRouterRampsConfig.Validate(e, state)
if err != nil {
return fmt.Errorf("failed to validate UpdateRouterRampsConfig: %w", err)
}

return nil
}

func updateBidirectionalLanesLogic(e deployment.Environment, c UpdateBidirectionalLanesConfig) (deployment.ChangesetOutput, error) {
proposals := make([]mcms.TimelockProposal, 0)
configs := c.BuildConfigs()

out, err := UpdateFeeQuoterDestsChangeset(e, configs.UpdateFeeQuoterDestsConfig)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to run UpdateFeeQuoterDestsChangeset: %w", err)
}
proposals = append(proposals, out.MCMSTimelockProposals...)
e.Logger.Info("Destination configs updated on FeeQuoters")

out, err = UpdateFeeQuoterPricesChangeset(e, configs.UpdateFeeQuoterPricesConfig)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to run UpdateFeeQuoterPricesChangeset: %w", err)
}
proposals = append(proposals, out.MCMSTimelockProposals...)
e.Logger.Info("Gas prices updated on FeeQuoters")

out, err = UpdateOnRampsDestsChangeset(e, configs.UpdateOnRampDestsConfig)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to run UpdateOnRampDestsChangeset: %w", err)
}
proposals = append(proposals, out.MCMSTimelockProposals...)
e.Logger.Info("Destination configs updated on OnRamps")

out, err = UpdateOffRampSourcesChangeset(e, configs.UpdateOffRampSourcesConfig)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to run UpdateOffRampSourcesChangeset: %w", err)
}
proposals = append(proposals, out.MCMSTimelockProposals...)
e.Logger.Info("Source configs updated on OffRamps")

out, err = UpdateRouterRampsChangeset(e, configs.UpdateRouterRampsConfig)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to run UpdateRouterRampsChangeset: %w", err)
}
proposals = append(proposals, out.MCMSTimelockProposals...)
e.Logger.Info("Ramps updated on Routers")

state, err := changeset.LoadOnchainState(e)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to load onchain state: %w", err)
}
proposal, err := proposalutils.AggregateProposals(e, state.EVMMCMSStateByChain(), proposals, nil, "Update multiple bidirectional lanes", c.MCMSConfig)
if err != nil {
return deployment.ChangesetOutput{}, fmt.Errorf("failed to aggregate proposals: %w", err)
}
if proposal == nil {
return deployment.ChangesetOutput{}, nil
}

return deployment.ChangesetOutput{
MCMSTimelockProposals: []mcms.TimelockProposal{*proposal},
}, nil
}
Loading
Loading