diff --git a/deployment/ccip/changeset/cs_add_lane.go b/deployment/ccip/changeset/cs_add_lane.go deleted file mode 100644 index 0bd03b56559..00000000000 --- a/deployment/ccip/changeset/cs_add_lane.go +++ /dev/null @@ -1,227 +0,0 @@ -package changeset - -import ( - "encoding/hex" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" - - "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ccipevm" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" -) - -var _ deployment.ChangeSet[AddLanesConfig] = AddLanes - -type InitialPrices struct { - LinkPrice *big.Int // USD to the power of 18 (e18) per LINK - WethPrice *big.Int // USD to the power of 18 (e18) per WETH - GasPrice *big.Int // uint224 packed gas price in USD (112 for exec // 112 for da) -} - -func (p InitialPrices) Validate() error { - if p.LinkPrice == nil { - return fmt.Errorf("missing link price") - } - if p.WethPrice == nil { - return fmt.Errorf("missing weth price") - } - if p.GasPrice == nil { - return fmt.Errorf("missing gas price") - } - return nil -} - -type LaneConfig struct { - SourceSelector uint64 - DestSelector uint64 - InitialPricesBySource InitialPrices - FeeQuoterDestChain fee_quoter.FeeQuoterDestChainConfig - TestRouter bool -} - -type AddLanesConfig struct { - LaneConfigs []LaneConfig -} - -func (c AddLanesConfig) Validate() error { - for _, pair := range c.LaneConfigs { - if pair.SourceSelector == pair.DestSelector { - return fmt.Errorf("cannot add lane to the same chain") - } - if err := pair.InitialPricesBySource.Validate(); err != nil { - return fmt.Errorf("error in validating initial prices for chain %d : %w", pair.SourceSelector, err) - } - // TODO: add more FeeQuoterDestChainConfigArgs validation - if pair.FeeQuoterDestChain == (fee_quoter.FeeQuoterDestChainConfig{}) { - return fmt.Errorf("missing fee quoter dest chain config") - } - } - return nil -} - -// AddLanes adds lanes between chains. -// AddLanes is run while the contracts are still owned by the deployer. -// This is useful to test the initial deployment to enable lanes between chains. -// If the testrouter is enabled, the lanes can be used to send messages between chains with testrouter. -// On successful verification with testrouter, the lanes can be enabled with the main router with different addLane ChangeSet. -func AddLanes(e deployment.Environment, cfg AddLanesConfig) (deployment.ChangesetOutput, error) { - if err := cfg.Validate(); err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("invalid AddLanesConfig: %w", err) - } - newAddresses := deployment.NewMemoryAddressBook() - err := addLanes(e, cfg) - if err != nil { - e.Logger.Errorw("Failed to add lanes", "err", err) - return deployment.ChangesetOutput{}, err - } - return deployment.ChangesetOutput{ - Proposals: []timelock.MCMSWithTimelockProposal{}, - AddressBook: newAddresses, - JobSpecs: nil, - }, nil -} - -var DefaultInitialPrices = InitialPrices{ - LinkPrice: deployment.E18Mult(20), - WethPrice: deployment.E18Mult(4000), - GasPrice: ToPackedFee(big.NewInt(8e14), big.NewInt(0)), -} - -func addLanes(e deployment.Environment, cfg AddLanesConfig) error { - state, err := LoadOnchainState(e) - if err != nil { - return fmt.Errorf("failed to load onchain state: %w", err) - } - for _, laneCfg := range cfg.LaneConfigs { - e.Logger.Infow("Enabling lane with test router", "from", laneCfg.SourceSelector, "to", laneCfg.DestSelector) - if err := addLane(e, state, laneCfg, laneCfg.TestRouter); err != nil { - return err - } - } - return nil -} - -func addLane(e deployment.Environment, state CCIPOnChainState, config LaneConfig, isTestRouter bool) error { - // TODO: Batch - var fromRouter *router.Router - var toRouter *router.Router - from := config.SourceSelector - to := config.DestSelector - feeQuoterDestChainConfig := config.FeeQuoterDestChain - initialPrices := config.InitialPricesBySource - if isTestRouter { - fromRouter = state.Chains[from].TestRouter - toRouter = state.Chains[to].TestRouter - } else { - fromRouter = state.Chains[from].Router - toRouter = state.Chains[to].Router - } - tx, err := fromRouter.ApplyRampUpdates(e.Chains[from].DeployerKey, []router.RouterOnRamp{ - { - DestChainSelector: to, - OnRamp: state.Chains[from].OnRamp.Address(), - }, - }, []router.RouterOffRamp{}, []router.RouterOffRamp{}) - if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { - return err - } - tx, err = state.Chains[from].OnRamp.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, - []onramp.OnRampDestChainConfigArgs{ - { - DestChainSelector: to, - Router: fromRouter.Address(), - }, - }) - if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { - return err - } - - _, err = state.Chains[from].FeeQuoter.UpdatePrices( - e.Chains[from].DeployerKey, fee_quoter.InternalPriceUpdates{ - TokenPriceUpdates: []fee_quoter.InternalTokenPriceUpdate{ - { - SourceToken: state.Chains[from].LinkToken.Address(), - UsdPerToken: initialPrices.LinkPrice, - }, - { - SourceToken: state.Chains[from].Weth9.Address(), - UsdPerToken: initialPrices.WethPrice, - }, - }, - GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ - { - DestChainSelector: to, - UsdPerUnitGas: initialPrices.GasPrice, - }, - }}) - if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { - return err - } - - // Enable dest in fee quoter - tx, err = state.Chains[from].FeeQuoter.ApplyDestChainConfigUpdates(e.Chains[from].DeployerKey, - []fee_quoter.FeeQuoterDestChainConfigArgs{ - { - DestChainSelector: to, - DestChainConfig: feeQuoterDestChainConfig, - }, - }) - if _, err := deployment.ConfirmIfNoError(e.Chains[from], tx, err); err != nil { - return err - } - - tx, err = state.Chains[to].OffRamp.ApplySourceChainConfigUpdates(e.Chains[to].DeployerKey, - []offramp.OffRampSourceChainConfigArgs{ - { - Router: toRouter.Address(), - SourceChainSelector: from, - IsEnabled: true, - OnRamp: common.LeftPadBytes(state.Chains[from].OnRamp.Address().Bytes(), 32), - }, - }) - if _, err := deployment.ConfirmIfNoError(e.Chains[to], tx, err); err != nil { - return err - } - tx, err = toRouter.ApplyRampUpdates(e.Chains[to].DeployerKey, []router.RouterOnRamp{}, []router.RouterOffRamp{}, []router.RouterOffRamp{ - { - SourceChainSelector: from, - OffRamp: state.Chains[to].OffRamp.Address(), - }, - }) - _, err = deployment.ConfirmIfNoError(e.Chains[to], tx, err) - return err -} - -func DefaultFeeQuoterDestChainConfig() fee_quoter.FeeQuoterDestChainConfig { - // https://github.com/smartcontractkit/ccip/blob/c4856b64bd766f1ddbaf5d13b42d3c4b12efde3a/contracts/src/v0.8/ccip/libraries/Internal.sol#L337-L337 - /* - ```Solidity - // bytes4(keccak256("CCIP ChainFamilySelector EVM")) - bytes4 public constant CHAIN_FAMILY_SELECTOR_EVM = 0x2812d52c; - ``` - */ - evmFamilySelector, _ := hex.DecodeString("2812d52c") - return fee_quoter.FeeQuoterDestChainConfig{ - IsEnabled: true, - MaxNumberOfTokensPerMsg: 10, - MaxDataBytes: 256, - MaxPerMsgGasLimit: 3_000_000, - DestGasOverhead: ccipevm.DestGasOverhead, - DefaultTokenFeeUSDCents: 1, - DestGasPerPayloadByte: ccipevm.CalldataGasPerByte, - DestDataAvailabilityOverheadGas: 100, - DestGasPerDataAvailabilityByte: 100, - DestDataAvailabilityMultiplierBps: 1, - DefaultTokenDestGasOverhead: 125_000, - DefaultTxGasLimit: 200_000, - GasMultiplierWeiPerEth: 11e17, // Gas multiplier in wei per eth is scaled by 1e18, so 11e17 is 1.1 = 110% - NetworkFeeUSDCents: 1, - ChainFamilySelector: [4]byte(evmFamilySelector), - } -} diff --git a/deployment/ccip/changeset/cs_add_lane_test.go b/deployment/ccip/changeset/cs_add_lane_test.go index 5c324c975ef..e793c1866d9 100644 --- a/deployment/ccip/changeset/cs_add_lane_test.go +++ b/deployment/ccip/changeset/cs_add_lane_test.go @@ -1,17 +1,16 @@ package changeset import ( + "math/big" "testing" - "time" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - commonutils "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" - "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" + commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" ) @@ -25,14 +24,79 @@ func TestAddLanesWithTestRouter(t *testing.T) { selectors := e.Env.AllChainSelectors() chain1, chain2 := selectors[0], selectors[1] - _, err = AddLanes(e.Env, AddLanesConfig{ - LaneConfigs: []LaneConfig{ - { - SourceSelector: chain1, - DestSelector: chain2, - InitialPricesBySource: DefaultInitialPrices, - FeeQuoterDestChain: DefaultFeeQuoterDestChainConfig(), - TestRouter: true, + stateChain1 := state.Chains[chain1] + e.Env, err = commoncs.ApplyChangesets(t, e.Env, e.TimelockContracts(t), []commoncs.ChangesetApplication{ + { + Changeset: commoncs.WrapChangeSet(UpdateOnRampsDests), + Config: UpdateOnRampDestsConfig{ + UpdatesByChain: map[uint64]map[uint64]OnRampDestinationUpdate{ + chain1: { + chain2: { + IsEnabled: true, + TestRouter: true, + AllowListEnabled: false, + }, + }, + }, + }, + }, + { + Changeset: commoncs.WrapChangeSet(UpdateFeeQuoterPricesCS), + Config: UpdateFeeQuoterPricesConfig{ + PricesByChain: map[uint64]FeeQuoterPriceUpdatePerSource{ + chain1: { + TokenPrices: map[common.Address]*big.Int{ + stateChain1.LinkToken.Address(): DefaultLinkPrice, + stateChain1.Weth9.Address(): DefaultWethPrice, + }, + GasPrices: map[uint64]*big.Int{ + chain2: DefaultGasPrice, + }, + }, + }, + }, + }, + { + Changeset: commoncs.WrapChangeSet(UpdateFeeQuoterDests), + Config: UpdateFeeQuoterDestsConfig{ + UpdatesByChain: map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig{ + chain1: { + chain2: DefaultFeeQuoterDestChainConfig(), + }, + }, + }, + }, + { + Changeset: commoncs.WrapChangeSet(UpdateOffRampSources), + Config: UpdateOffRampSourcesConfig{ + UpdatesByChain: map[uint64]map[uint64]OffRampSourceUpdate{ + chain2: { + chain1: { + IsEnabled: true, + TestRouter: true, + }, + }, + }, + }, + }, + { + Changeset: commoncs.WrapChangeSet(UpdateRouterRamps), + Config: UpdateRouterRampsConfig{ + TestRouter: true, + UpdatesByChain: map[uint64]RouterUpdates{ + // onRamp update on source chain + chain1: { + OnRampUpdates: map[uint64]bool{ + chain2: true, + }, + }, + // offramp update on dest chain + chain2: { + OffRampUpdates: map[uint64]bool{ + chain1: true, + }, + }, + }, }, }, }) @@ -58,142 +122,3 @@ func TestAddLanesWithTestRouter(t *testing.T) { }] = []uint64{msgSentEvent.SequenceNumber} ConfirmExecWithSeqNrsForAll(t, e.Env, state, expectedSeqNumExec, startBlocks) } - -// TestAddLane covers the workflow of adding a lane between two chains and enabling it. -// It also covers the case where the onRamp is disabled on the OffRamp contract initially and then enabled. -func TestAddLane(t *testing.T) { - t.Skip("This test is flaky and needs to be fixed: reverted," + - "error reason: 0x07da6ee6 InsufficientFeeTokenAmount: Replace time.sleep() with polling") - - t.Parallel() - // We add more chains to the chainlink nodes than the number of chains where CCIP is deployed. - e := NewMemoryEnvironment(t) - // Here we have CR + nodes set up, but no CCIP contracts deployed. - state, err := LoadOnchainState(e.Env) - require.NoError(t, err) - - selectors := e.Env.AllChainSelectors() - chain1, chain2 := selectors[0], selectors[1] - - // We expect no lanes available on any chain. - for _, sel := range []uint64{chain1, chain2} { - chain := state.Chains[sel] - offRamps, err := chain.Router.GetOffRamps(nil) - require.NoError(t, err) - require.Len(t, offRamps, 0) - } - - replayBlocks, err := LatestBlocksByChain(testcontext.Get(t), e.Env.Chains) - require.NoError(t, err) - - // Add one lane from chain1 to chain 2 and send traffic. - require.NoError(t, AddLaneWithDefaultPricesAndFeeQuoterConfig(e.Env, state, chain1, chain2, false)) - - ReplayLogs(t, e.Env.Offchain, replayBlocks) - time.Sleep(30 * time.Second) - // disable the onRamp initially on OffRamp - disableRampTx, err := state.Chains[chain2].OffRamp.ApplySourceChainConfigUpdates(e.Env.Chains[chain2].DeployerKey, []offramp.OffRampSourceChainConfigArgs{ - { - Router: state.Chains[chain2].Router.Address(), - SourceChainSelector: chain1, - IsEnabled: false, - OnRamp: common.LeftPadBytes(state.Chains[chain1].OnRamp.Address().Bytes(), 32), - }, - }) - _, err = deployment.ConfirmIfNoError(e.Env.Chains[chain2], disableRampTx, err) - require.NoError(t, err) - - for _, sel := range []uint64{chain1, chain2} { - chain := state.Chains[sel] - offRamps, err := chain.Router.GetOffRamps(nil) - require.NoError(t, err) - if sel == chain2 { - require.Len(t, offRamps, 1) - srcCfg, err := chain.OffRamp.GetSourceChainConfig(nil, chain1) - require.NoError(t, err) - require.Equal(t, common.LeftPadBytes(state.Chains[chain1].OnRamp.Address().Bytes(), 32), srcCfg.OnRamp) - require.False(t, srcCfg.IsEnabled) - } else { - require.Len(t, offRamps, 0) - } - } - - latesthdr, err := e.Env.Chains[chain2].Client.HeaderByNumber(testcontext.Get(t), nil) - require.NoError(t, err) - startBlock := latesthdr.Number.Uint64() - // Send traffic on the first lane and it should not be processed by the plugin as onRamp is disabled - // we will check this by confirming that the message is not executed by the end of the test - msgSentEvent1 := TestSendRequest(t, e.Env, state, chain1, chain2, false, router.ClientEVM2AnyMessage{ - Receiver: common.LeftPadBytes(state.Chains[chain2].Receiver.Address().Bytes(), 32), - Data: []byte("hello world"), - TokenAmounts: nil, - FeeToken: common.HexToAddress("0x0"), - ExtraArgs: nil, - }) - require.Equal(t, uint64(1), msgSentEvent1.SequenceNumber) - - // Add another lane - require.NoError(t, AddLaneWithDefaultPricesAndFeeQuoterConfig(e.Env, state, chain2, chain1, false)) - - // Send traffic on the second lane and it should succeed - latesthdr, err = e.Env.Chains[chain1].Client.HeaderByNumber(testcontext.Get(t), nil) - require.NoError(t, err) - startBlock2 := latesthdr.Number.Uint64() - msgSentEvent2 := TestSendRequest(t, e.Env, state, chain2, chain1, false, router.ClientEVM2AnyMessage{ - Receiver: common.LeftPadBytes(state.Chains[chain2].Receiver.Address().Bytes(), 32), - Data: []byte("hello world"), - TokenAmounts: nil, - FeeToken: common.HexToAddress("0x0"), - ExtraArgs: nil, - }) - require.Equal(t, uint64(1), msgSentEvent2.SequenceNumber) - require.NoError(t, - commonutils.JustError( - ConfirmExecWithSeqNrs( - t, - e.Env.Chains[chain2], - e.Env.Chains[chain1], - state.Chains[chain1].OffRamp, - &startBlock2, - []uint64{msgSentEvent2.SequenceNumber}, - ), - ), - ) - - // now check for the previous message from chain 1 to chain 2 that it has not been executed till now as the onRamp was disabled - ConfirmNoExecConsistentlyWithSeqNr(t, e.Env.Chains[chain1], e.Env.Chains[chain2], state.Chains[chain2].OffRamp, msgSentEvent1.SequenceNumber, 30*time.Second) - - // enable the onRamp on OffRamp - enableRampTx, err := state.Chains[chain2].OffRamp.ApplySourceChainConfigUpdates(e.Env.Chains[chain2].DeployerKey, []offramp.OffRampSourceChainConfigArgs{ - { - Router: state.Chains[chain2].Router.Address(), - SourceChainSelector: chain1, - IsEnabled: true, - OnRamp: common.LeftPadBytes(state.Chains[chain1].OnRamp.Address().Bytes(), 32), - }, - }) - _, err = deployment.ConfirmIfNoError(e.Env.Chains[chain2], enableRampTx, err) - require.NoError(t, err) - - srcCfg, err := state.Chains[chain2].OffRamp.GetSourceChainConfig(nil, chain1) - require.NoError(t, err) - require.Equal(t, common.LeftPadBytes(state.Chains[chain1].OnRamp.Address().Bytes(), 32), srcCfg.OnRamp) - require.True(t, srcCfg.IsEnabled) - - // we need the replay here otherwise plugin is not able to locate the message - ReplayLogs(t, e.Env.Offchain, replayBlocks) - time.Sleep(30 * time.Second) - // Now that the onRamp is enabled, the request should be processed - require.NoError(t, - commonutils.JustError( - ConfirmExecWithSeqNrs( - t, - e.Env.Chains[chain1], - e.Env.Chains[chain2], - state.Chains[chain2].OffRamp, - &startBlock, - []uint64{msgSentEvent1.SequenceNumber}, - ), - ), - ) -} diff --git a/deployment/ccip/changeset/cs_ccip_home.go b/deployment/ccip/changeset/cs_ccip_home.go index a2abad5101f..0c82afee261 100644 --- a/deployment/ccip/changeset/cs_ccip_home.go +++ b/deployment/ccip/changeset/cs_ccip_home.go @@ -34,7 +34,7 @@ import ( var ( _ deployment.ChangeSet[AddDonAndSetCandidateChangesetConfig] = AddDonAndSetCandidateChangeset - _ deployment.ChangeSet[PromoteAllCandidatesChangesetConfig] = PromoteAllCandidatesChangeset + _ deployment.ChangeSet[PromoteCandidatesChangesetConfig] = PromoteAllCandidatesChangeset _ deployment.ChangeSet[SetCandidateChangesetConfig] = SetCandidateChangeset _ deployment.ChangeSet[RevokeCandidateChangesetConfig] = RevokeCandidateChangeset _ deployment.ChangeSet[UpdateChainConfigConfig] = UpdateChainConfig @@ -107,7 +107,7 @@ func DefaultOCRParams( } } -type PromoteAllCandidatesChangesetConfig struct { +type PromoteCandidatesChangesetConfig struct { HomeChainSelector uint64 // RemoteChainSelectors is the chain selector of the DONs that we want to promote the candidate config of. @@ -121,7 +121,7 @@ type PromoteAllCandidatesChangesetConfig struct { MCMS *MCMSConfig } -func (p PromoteAllCandidatesChangesetConfig) Validate(e deployment.Environment) ([]uint32, error) { +func (p PromoteCandidatesChangesetConfig) Validate(e deployment.Environment) ([]uint32, error) { state, err := LoadOnchainState(e) if err != nil { return nil, err @@ -201,7 +201,7 @@ func (p PromoteAllCandidatesChangesetConfig) Validate(e deployment.Environment) // At that point you can call the RemoveDON changeset to remove the DON entirely from the capability registry. func PromoteAllCandidatesChangeset( e deployment.Environment, - cfg PromoteAllCandidatesChangesetConfig, + cfg PromoteCandidatesChangesetConfig, ) (deployment.ChangesetOutput, error) { donIDs, err := cfg.Validate(e) if err != nil { diff --git a/deployment/ccip/changeset/cs_ccip_home_test.go b/deployment/ccip/changeset/cs_ccip_home_test.go index 6b4683ae12c..dae32557f8b 100644 --- a/deployment/ccip/changeset/cs_ccip_home_test.go +++ b/deployment/ccip/changeset/cs_ccip_home_test.go @@ -88,7 +88,7 @@ func Test_PromoteCandidate(t *testing.T) { }, []commonchangeset.ChangesetApplication{ { Changeset: commonchangeset.WrapChangeSet(PromoteAllCandidatesChangeset), - Config: PromoteAllCandidatesChangesetConfig{ + Config: PromoteCandidatesChangesetConfig{ HomeChainSelector: tenv.HomeChainSel, RemoteChainSelectors: []uint64{dest}, MCMS: mcmsConfig, diff --git a/deployment/ccip/changeset/cs_chain_contracts.go b/deployment/ccip/changeset/cs_chain_contracts.go index e97772793b0..f85814f1768 100644 --- a/deployment/ccip/changeset/cs_chain_contracts.go +++ b/deployment/ccip/changeset/cs_chain_contracts.go @@ -4,35 +4,220 @@ import ( "bytes" "context" "encoding/hex" + "errors" "fmt" "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/smartcontractkit/ccip-owner-contracts/pkg/gethwrappers" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/mcms" "github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ccipevm" cctypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/nonce_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/offramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router" ) var ( - _ deployment.ChangeSet[UpdateOnRampDestsConfig] = UpdateOnRampsDests - _ deployment.ChangeSet[UpdateOffRampSourcesConfig] = UpdateOffRampSources - _ deployment.ChangeSet[UpdateRouterRampsConfig] = UpdateRouterRamps - _ deployment.ChangeSet[UpdateFeeQuoterDestsConfig] = UpdateFeeQuoterDests - _ deployment.ChangeSet[SetOCR3OffRampConfig] = SetOCR3OffRamp + _ deployment.ChangeSet[UpdateOnRampDestsConfig] = UpdateOnRampsDests + _ deployment.ChangeSet[UpdateOffRampSourcesConfig] = UpdateOffRampSources + _ deployment.ChangeSet[UpdateRouterRampsConfig] = UpdateRouterRamps + _ deployment.ChangeSet[UpdateFeeQuoterDestsConfig] = UpdateFeeQuoterDests + _ deployment.ChangeSet[SetOCR3OffRampConfig] = SetOCR3OffRamp + _ deployment.ChangeSet[UpdateFeeQuoterPricesConfig] = UpdateFeeQuoterPricesCS + _ deployment.ChangeSet[UpdateNonceManagerConfig] = UpdateNonceManagersCS ) +type UpdateNonceManagerConfig struct { + UpdatesByChain map[uint64]NonceManagerUpdate // source -> dest -> update + MCMS *MCMSConfig +} + +type NonceManagerUpdate struct { + AddedAuthCallers []common.Address + RemovedAuthCallers []common.Address + PreviousRampsArgs []PreviousRampCfg +} + +type PreviousRampCfg struct { + RemoteChainSelector uint64 + OverrideExisting bool + EnableOnRamp bool + EnableOffRamp bool +} + +func (cfg UpdateNonceManagerConfig) Validate(e deployment.Environment) error { + state, err := LoadOnchainState(e) + if err != nil { + return err + } + for sourceSel, update := range cfg.UpdatesByChain { + sourceChainState, ok := state.Chains[sourceSel] + if !ok { + return fmt.Errorf("chain %d not found in onchain state", sourceSel) + } + if sourceChainState.NonceManager == nil { + return fmt.Errorf("missing nonce manager for chain %d", sourceSel) + } + sourceChain, ok := e.Chains[sourceSel] + if !ok { + return fmt.Errorf("missing chain %d in environment", sourceSel) + } + if err := commoncs.ValidateOwnership(e.GetContext(), cfg.MCMS != nil, sourceChain.DeployerKey.From, sourceChainState.Timelock.Address(), sourceChainState.OnRamp); err != nil { + return fmt.Errorf("chain %s: %w", sourceChain.String(), err) + } + for _, prevRamp := range update.PreviousRampsArgs { + if prevRamp.RemoteChainSelector == sourceSel { + return errors.New("source and dest chain cannot be the same") + } + if _, ok := state.Chains[prevRamp.RemoteChainSelector]; !ok { + return fmt.Errorf("dest chain %d not found in onchain state for chain %d", prevRamp.RemoteChainSelector, sourceSel) + } + if !prevRamp.EnableOnRamp && !prevRamp.EnableOffRamp { + return errors.New("must specify either onramp or offramp") + } + if prevRamp.EnableOnRamp { + if prevOnRamp := state.Chains[sourceSel].EVM2EVMOnRamp; prevOnRamp == nil { + return fmt.Errorf("no previous onramp for source chain %d", sourceSel) + } else if prevOnRamp[prevRamp.RemoteChainSelector] == nil { + return fmt.Errorf("no previous onramp for source chain %d and dest chain %d", sourceSel, prevRamp.RemoteChainSelector) + } + } + if prevRamp.EnableOffRamp { + if prevOffRamp := state.Chains[sourceSel].EVM2EVMOffRamp; prevOffRamp == nil { + return fmt.Errorf("missing previous offramps for chain %d", sourceSel) + } else if prevOffRamp[prevRamp.RemoteChainSelector] == nil { + return fmt.Errorf("no previous offramp for source chain %d and dest chain %d", prevRamp.RemoteChainSelector, sourceSel) + } + } + } + } + return nil +} + +func UpdateNonceManagersCS(e deployment.Environment, cfg UpdateNonceManagerConfig) (deployment.ChangesetOutput, error) { + if err := cfg.Validate(e); err != nil { + return deployment.ChangesetOutput{}, err + } + s, err := LoadOnchainState(e) + if err != nil { + return deployment.ChangesetOutput{}, err + } + var batches []timelock.BatchChainOperation + timelocks := make(map[uint64]common.Address) + proposers := make(map[uint64]*gethwrappers.ManyChainMultiSig) + for chainSel, updates := range cfg.UpdatesByChain { + txOpts := e.Chains[chainSel].DeployerKey + if cfg.MCMS != nil { + txOpts = deployment.SimTransactOpts() + } + nm := s.Chains[chainSel].NonceManager + var authTx, prevRampsTx *types.Transaction + if len(updates.AddedAuthCallers) > 0 || len(updates.RemovedAuthCallers) > 0 { + authTx, err = nm.ApplyAuthorizedCallerUpdates(txOpts, nonce_manager.AuthorizedCallersAuthorizedCallerArgs{ + AddedCallers: updates.AddedAuthCallers, + RemovedCallers: updates.RemovedAuthCallers, + }) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("error updating authorized callers for chain %s: %w", e.Chains[chainSel].String(), err) + } + } + if len(updates.PreviousRampsArgs) > 0 { + previousRampsArgs := make([]nonce_manager.NonceManagerPreviousRampsArgs, 0) + for _, prevRamp := range updates.PreviousRampsArgs { + var onRamp, offRamp common.Address + if prevRamp.EnableOnRamp { + onRamp = s.Chains[chainSel].EVM2EVMOnRamp[prevRamp.RemoteChainSelector].Address() + } + if prevRamp.EnableOffRamp { + offRamp = s.Chains[chainSel].EVM2EVMOffRamp[prevRamp.RemoteChainSelector].Address() + } + previousRampsArgs = append(previousRampsArgs, nonce_manager.NonceManagerPreviousRampsArgs{ + RemoteChainSelector: prevRamp.RemoteChainSelector, + OverrideExistingRamps: prevRamp.OverrideExisting, + PrevRamps: nonce_manager.NonceManagerPreviousRamps{ + PrevOnRamp: onRamp, + PrevOffRamp: offRamp, + }, + }) + } + prevRampsTx, err = nm.ApplyPreviousRampsUpdates(txOpts, previousRampsArgs) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("error updating previous ramps for chain %s: %w", e.Chains[chainSel].String(), err) + } + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("error updating previous ramps for chain %s: %w", e.Chains[chainSel].String(), err) + } + } + if cfg.MCMS == nil { + if authTx != nil { + if _, err := deployment.ConfirmIfNoError(e.Chains[chainSel], authTx, err); err != nil { + return deployment.ChangesetOutput{}, err + } + } + if prevRampsTx != nil { + if _, err := deployment.ConfirmIfNoError(e.Chains[chainSel], prevRampsTx, err); err != nil { + return deployment.ChangesetOutput{}, err + } + } + } else { + ops := make([]mcms.Operation, 0) + if authTx != nil { + ops = append(ops, mcms.Operation{ + To: nm.Address(), + Data: authTx.Data(), + Value: big.NewInt(0), + }) + } + if prevRampsTx != nil { + ops = append(ops, mcms.Operation{ + To: nm.Address(), + Data: prevRampsTx.Data(), + Value: big.NewInt(0), + }) + } + if len(ops) == 0 { + return deployment.ChangesetOutput{}, errors.New("no operations to batch") + } + batches = append(batches, timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(chainSel), + Batch: ops, + }) + timelocks[chainSel] = s.Chains[chainSel].Timelock.Address() + proposers[chainSel] = s.Chains[chainSel].ProposerMcm + } + } + if cfg.MCMS == nil { + return deployment.ChangesetOutput{}, nil + } + + p, err := proposalutils.BuildProposalFromBatches( + timelocks, + proposers, + batches, + "Update nonce manager for previous ramps and authorized callers", + cfg.MCMS.MinDelay, + ) + if err != nil { + return deployment.ChangesetOutput{}, err + } + return deployment.ChangesetOutput{Proposals: []timelock.MCMSWithTimelockProposal{ + *p, + }}, nil +} + type UpdateOnRampDestsConfig struct { UpdatesByChain map[uint64]map[uint64]OnRampDestinationUpdate // Disallow mixing MCMS/non-MCMS per chain for simplicity. @@ -167,6 +352,165 @@ func UpdateOnRampsDests(e deployment.Environment, cfg UpdateOnRampDestsConfig) ( }}, nil } +type UpdateFeeQuoterPricesConfig struct { + PricesByChain map[uint64]FeeQuoterPriceUpdatePerSource // source -> PriceDetails + MCMS *MCMSConfig +} + +type FeeQuoterPriceUpdatePerSource struct { + TokenPrices map[common.Address]*big.Int // token address -> price + GasPrices map[uint64]*big.Int // dest chain -> gas price +} + +func (cfg UpdateFeeQuoterPricesConfig) Validate(e deployment.Environment) error { + state, err := LoadOnchainState(e) + if err != nil { + return err + } + for chainSel, initialPrice := range cfg.PricesByChain { + if err := deployment.IsValidChainSelector(chainSel); err != nil { + return fmt.Errorf("invalid chain selector: %w", err) + } + chainState, ok := state.Chains[chainSel] + if !ok { + return fmt.Errorf("chain %d not found in onchain state", chainSel) + } + fq := chainState.FeeQuoter + if fq == nil { + return fmt.Errorf("missing fee quoter for chain %d", chainSel) + } + if err := commoncs.ValidateOwnership(e.GetContext(), cfg.MCMS != nil, e.Chains[chainSel].DeployerKey.From, chainState.Timelock.Address(), chainState.FeeQuoter); err != nil { + return err + } + // check that whether price updaters are set + authCallers, err := fq.GetAllAuthorizedCallers(&bind.CallOpts{Context: e.GetContext()}) + if err != nil { + return fmt.Errorf("failed to get authorized callers for chain %d: %w", chainSel, err) + } + if len(authCallers) == 0 { + return fmt.Errorf("no authorized callers for chain %d", chainSel) + } + expectedAuthCaller := e.Chains[chainSel].DeployerKey.From + if cfg.MCMS != nil { + expectedAuthCaller = chainState.Timelock.Address() + } + foundCaller := false + for _, authCaller := range authCallers { + if authCaller.Cmp(expectedAuthCaller) == 0 { + foundCaller = true + } + } + if !foundCaller { + return fmt.Errorf("expected authorized caller %s not found for chain %d", expectedAuthCaller.String(), chainSel) + } + for token, price := range initialPrice.TokenPrices { + if price == nil { + return fmt.Errorf("token price for chain %d is nil", chainSel) + } + if token == (common.Address{}) { + return fmt.Errorf("token address for chain %d is empty", chainSel) + } + contains, err := deployment.AddressBookContains(e.ExistingAddresses, chainSel, token.String()) + if err != nil { + return fmt.Errorf("error checking address book for token %s: %w", token.String(), err) + } + if !contains { + return fmt.Errorf("token %s not found in address book for chain %d", token.String(), chainSel) + } + } + for dest, price := range initialPrice.GasPrices { + if chainSel == dest { + return errors.New("source and dest chain cannot be the same") + } + if err := deployment.IsValidChainSelector(dest); err != nil { + return fmt.Errorf("invalid dest chain selector: %w", err) + } + if price == nil { + return fmt.Errorf("gas price for chain %d is nil", chainSel) + } + if _, ok := state.Chains[dest]; !ok { + return fmt.Errorf("dest chain %d not found in onchain state for chain %d", dest, chainSel) + } + } + } + + return nil +} + +func UpdateFeeQuoterPricesCS(e deployment.Environment, cfg UpdateFeeQuoterPricesConfig) (deployment.ChangesetOutput, error) { + if err := cfg.Validate(e); err != nil { + return deployment.ChangesetOutput{}, err + } + s, err := LoadOnchainState(e) + if err != nil { + return deployment.ChangesetOutput{}, err + } + var batches []timelock.BatchChainOperation + timelocks := make(map[uint64]common.Address) + proposers := make(map[uint64]*gethwrappers.ManyChainMultiSig) + for chainSel, initialPrice := range cfg.PricesByChain { + txOpts := e.Chains[chainSel].DeployerKey + if cfg.MCMS != nil { + txOpts = deployment.SimTransactOpts() + } + fq := s.Chains[chainSel].FeeQuoter + var tokenPricesArgs []fee_quoter.InternalTokenPriceUpdate + for token, price := range initialPrice.TokenPrices { + tokenPricesArgs = append(tokenPricesArgs, fee_quoter.InternalTokenPriceUpdate{ + SourceToken: token, + UsdPerToken: price, + }) + } + var gasPricesArgs []fee_quoter.InternalGasPriceUpdate + for dest, price := range initialPrice.GasPrices { + gasPricesArgs = append(gasPricesArgs, fee_quoter.InternalGasPriceUpdate{ + DestChainSelector: dest, + UsdPerUnitGas: price, + }) + } + tx, err := fq.UpdatePrices(txOpts, fee_quoter.InternalPriceUpdates{ + TokenPriceUpdates: tokenPricesArgs, + GasPriceUpdates: gasPricesArgs, + }) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("error updating prices for chain %s: %w", e.Chains[chainSel].String(), err) + } + if cfg.MCMS == nil { + if _, err := deployment.ConfirmIfNoError(e.Chains[chainSel], tx, err); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("error confirming transaction for chain %s: %w", e.Chains[chainSel].String(), err) + } + } else { + batches = append(batches, timelock.BatchChainOperation{ + ChainIdentifier: mcms.ChainIdentifier(chainSel), + Batch: []mcms.Operation{ + { + To: fq.Address(), + Data: tx.Data(), + Value: big.NewInt(0), + }, + }, + }) + } + } + if cfg.MCMS == nil { + return deployment.ChangesetOutput{}, nil + } + + p, err := proposalutils.BuildProposalFromBatches( + timelocks, + proposers, + batches, + "Update fq prices", + cfg.MCMS.MinDelay, + ) + if err != nil { + return deployment.ChangesetOutput{}, err + } + return deployment.ChangesetOutput{Proposals: []timelock.MCMSWithTimelockProposal{ + *p, + }}, nil +} + type UpdateFeeQuoterDestsConfig struct { UpdatesByChain map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig // Disallow mixing MCMS/non-MCMS per chain for simplicity. @@ -208,7 +552,7 @@ func (cfg UpdateFeeQuoterDestsConfig) Validate(e deployment.Environment) error { return fmt.Errorf("failed to get onramp static config %s: %w", chainState.OnRamp.Address(), err) } if destination == sc.ChainSelector { - return fmt.Errorf("cannot update onramp destination to the same chain") + return fmt.Errorf("source and destination chain cannot be the same") } } } @@ -755,3 +1099,31 @@ func isOCR3ConfigSetOnOffRamp( } return true, nil } + +func DefaultFeeQuoterDestChainConfig() fee_quoter.FeeQuoterDestChainConfig { + // https://github.com/smartcontractkit/ccip/blob/c4856b64bd766f1ddbaf5d13b42d3c4b12efde3a/contracts/src/v0.8/ccip/libraries/Internal.sol#L337-L337 + /* + ```Solidity + // bytes4(keccak256("CCIP ChainFamilySelector EVM")) + bytes4 public constant CHAIN_FAMILY_SELECTOR_EVM = 0x2812d52c; + ``` + */ + evmFamilySelector, _ := hex.DecodeString("2812d52c") + return fee_quoter.FeeQuoterDestChainConfig{ + IsEnabled: true, + MaxNumberOfTokensPerMsg: 10, + MaxDataBytes: 256, + MaxPerMsgGasLimit: 3_000_000, + DestGasOverhead: ccipevm.DestGasOverhead, + DefaultTokenFeeUSDCents: 1, + DestGasPerPayloadByte: ccipevm.CalldataGasPerByte, + DestDataAvailabilityOverheadGas: 100, + DestGasPerDataAvailabilityByte: 100, + DestDataAvailabilityMultiplierBps: 1, + DefaultTokenDestGasOverhead: 125_000, + DefaultTxGasLimit: 200_000, + GasMultiplierWeiPerEth: 11e17, // Gas multiplier in wei per eth is scaled by 1e18, so 11e17 is 1.1 = 110% + NetworkFeeUSDCents: 1, + ChainFamilySelector: [4]byte(evmFamilySelector), + } +} diff --git a/deployment/ccip/changeset/cs_chain_contracts_test.go b/deployment/ccip/changeset/cs_chain_contracts_test.go index ad1b1a9f2b5..0a1e0ce3b7b 100644 --- a/deployment/ccip/changeset/cs_chain_contracts_test.go +++ b/deployment/ccip/changeset/cs_chain_contracts_test.go @@ -9,6 +9,7 @@ import ( "golang.org/x/exp/maps" "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" ) @@ -303,3 +304,61 @@ func TestUpdateRouterRamps(t *testing.T) { }) } } + +func TestUpdateNonceManagersCS(t *testing.T) { + for _, tc := range []struct { + name string + mcmsEnabled bool + }{ + { + name: "MCMS enabled", + mcmsEnabled: true, + }, + { + name: "MCMS disabled", + mcmsEnabled: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + tenv := NewMemoryEnvironment(t) + state, err := LoadOnchainState(tenv.Env) + require.NoError(t, err) + + allChains := maps.Keys(tenv.Env.Chains) + source := allChains[0] + dest := allChains[1] + + if tc.mcmsEnabled { + // Transfer ownership to timelock so that we can promote the zero digest later down the line. + transferToTimelock(t, tenv, state, source, dest) + } + + var mcmsConfig *MCMSConfig + if tc.mcmsEnabled { + mcmsConfig = &MCMSConfig{ + MinDelay: 0, + } + } + + _, err = commonchangeset.ApplyChangesets(t, tenv.Env, tenv.TimelockContracts(t), []commonchangeset.ChangesetApplication{ + { + Changeset: commonchangeset.WrapChangeSet(UpdateNonceManagersCS), + Config: UpdateNonceManagerConfig{ + UpdatesByChain: map[uint64]NonceManagerUpdate{ + source: { + RemovedAuthCallers: []common.Address{state.Chains[source].OnRamp.Address()}, + }, + }, + MCMS: mcmsConfig, + }, + }, + }) + require.NoError(t, err) + // Assert the nonce manager configuration is as we expect. + callers, err := state.Chains[source].NonceManager.GetAllAuthorizedCallers(nil) + require.NoError(t, err) + require.NotContains(t, callers, state.Chains[source].OnRamp.Address()) + require.Contains(t, callers, state.Chains[source].OffRamp.Address()) + }) + } +} diff --git a/deployment/ccip/changeset/test_environment.go b/deployment/ccip/changeset/test_environment.go index 1ab184573ce..f723efbf619 100644 --- a/deployment/ccip/changeset/test_environment.go +++ b/deployment/ccip/changeset/test_environment.go @@ -551,7 +551,7 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test { // Promote everything Changeset: commonchangeset.WrapChangeSet(PromoteAllCandidatesChangeset), - Config: PromoteAllCandidatesChangesetConfig{ + Config: PromoteCandidatesChangesetConfig{ HomeChainSelector: e.HomeChainSel, RemoteChainSelectors: allChains, PluginType: types.PluginTypeCCIPCommit, @@ -560,7 +560,7 @@ func NewEnvironmentWithJobsAndContracts(t *testing.T, tc *TestConfigs, tEnv Test { // Promote everything Changeset: commonchangeset.WrapChangeSet(PromoteAllCandidatesChangeset), - Config: PromoteAllCandidatesChangesetConfig{ + Config: PromoteCandidatesChangesetConfig{ HomeChainSelector: e.HomeChainSel, RemoteChainSelectors: allChains, PluginType: types.PluginTypeCCIPExec, diff --git a/deployment/ccip/changeset/test_helpers.go b/deployment/ccip/changeset/test_helpers.go index 75801d99cff..cfd24d88ebd 100644 --- a/deployment/ccip/changeset/test_helpers.go +++ b/deployment/ccip/changeset/test_helpers.go @@ -18,6 +18,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/ethereum/go-ethereum/common" @@ -60,6 +62,10 @@ var ( evmExtraArgsV2Tag = hexutil.MustDecode("0x181dcf10") routerABI = abihelpers.MustParseABI(router.RouterABI) + + DefaultLinkPrice = deployment.E18Mult(20) + DefaultWethPrice = deployment.E18Mult(4000) + DefaultGasPrice = ToPackedFee(big.NewInt(8e14), big.NewInt(0)) ) // Context returns a context with the test's deadline, if available. @@ -384,30 +390,102 @@ func MakeEVMExtraArgsV2(gasLimit uint64, allowOOO bool) []byte { return extraArgs } -func AddLaneWithDefaultPricesAndFeeQuoterConfig(e deployment.Environment, state CCIPOnChainState, from, to uint64, isTestRouter bool) error { - cfg := LaneConfig{ - SourceSelector: from, - DestSelector: to, - InitialPricesBySource: DefaultInitialPrices, - FeeQuoterDestChain: DefaultFeeQuoterDestChainConfig(), - } - return addLane(e, state, cfg, isTestRouter) +func AddLane(t *testing.T, e *DeployedEnv, from, to uint64, isTestRouter bool, gasprice map[uint64]*big.Int, tokenPrices map[common.Address]*big.Int, fqCfg fee_quoter.FeeQuoterDestChainConfig) { + var err error + e.Env, err = commoncs.ApplyChangesets(t, e.Env, e.TimelockContracts(t), []commoncs.ChangesetApplication{ + { + Changeset: commoncs.WrapChangeSet(UpdateOnRampsDests), + Config: UpdateOnRampDestsConfig{ + UpdatesByChain: map[uint64]map[uint64]OnRampDestinationUpdate{ + from: { + to: { + IsEnabled: true, + TestRouter: isTestRouter, + AllowListEnabled: false, + }, + }, + }, + }, + }, + { + Changeset: commoncs.WrapChangeSet(UpdateFeeQuoterPricesCS), + Config: UpdateFeeQuoterPricesConfig{ + PricesByChain: map[uint64]FeeQuoterPriceUpdatePerSource{ + from: { + TokenPrices: tokenPrices, + GasPrices: gasprice, + }, + }, + }, + }, + { + Changeset: commoncs.WrapChangeSet(UpdateFeeQuoterDests), + Config: UpdateFeeQuoterDestsConfig{ + UpdatesByChain: map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig{ + from: { + to: fqCfg, + }, + }, + }, + }, + { + Changeset: commoncs.WrapChangeSet(UpdateOffRampSources), + Config: UpdateOffRampSourcesConfig{ + UpdatesByChain: map[uint64]map[uint64]OffRampSourceUpdate{ + to: { + from: { + IsEnabled: true, + TestRouter: isTestRouter, + }, + }, + }, + }, + }, + { + Changeset: commoncs.WrapChangeSet(UpdateRouterRamps), + Config: UpdateRouterRampsConfig{ + TestRouter: isTestRouter, + UpdatesByChain: map[uint64]RouterUpdates{ + // onRamp update on source chain + from: { + OnRampUpdates: map[uint64]bool{ + to: true, + }, + }, + // offramp update on dest chain + to: { + OffRampUpdates: map[uint64]bool{ + from: true, + }, + }, + }, + }, + }, + }) + require.NoError(t, err) +} + +func AddLaneWithDefaultPricesAndFeeQuoterConfig(t *testing.T, e *DeployedEnv, state CCIPOnChainState, from, to uint64, isTestRouter bool) { + stateChainFrom := state.Chains[from] + AddLane(t, e, from, to, isTestRouter, + map[uint64]*big.Int{ + to: DefaultGasPrice, + }, map[common.Address]*big.Int{ + stateChainFrom.LinkToken.Address(): DefaultLinkPrice, + stateChainFrom.Weth9.Address(): DefaultWethPrice, + }, DefaultFeeQuoterDestChainConfig()) } // AddLanesForAll adds densely connected lanes for all chains in the environment so that each chain // is connected to every other chain except itself. -func AddLanesForAll(e deployment.Environment, state CCIPOnChainState) error { - for source := range e.Chains { - for dest := range e.Chains { +func AddLanesForAll(t *testing.T, e *DeployedEnv, state CCIPOnChainState) { + for source := range e.Env.Chains { + for dest := range e.Env.Chains { if source != dest { - err := AddLaneWithDefaultPricesAndFeeQuoterConfig(e, state, source, dest, false) - if err != nil { - return err - } + AddLaneWithDefaultPricesAndFeeQuoterConfig(t, e, state, source, dest, false) } } } - return nil } func ToPackedFee(execFee, daFee *big.Int) *big.Int { diff --git a/deployment/environment/crib/ccip_deployer.go b/deployment/environment/crib/ccip_deployer.go index 4c52cc72416..bb3acec8aa4 100644 --- a/deployment/environment/crib/ccip_deployer.go +++ b/deployment/environment/crib/ccip_deployer.go @@ -12,6 +12,7 @@ import ( commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/devenv" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/deployment" @@ -126,9 +127,91 @@ func DeployCCIPAndAddLanes(ctx context.Context, lggr logger.Logger, envConfig de return DeployCCIPOutput{}, fmt.Errorf("failed to load onchain state: %w", err) } // Add all lanes - err = changeset.AddLanesForAll(*e, state) - if err != nil { - return DeployCCIPOutput{}, fmt.Errorf("failed to add lanes: %w", err) + for from := range e.Chains { + for to := range e.Chains { + if from != to { + stateChain1 := state.Chains[from] + newEnv, err := commonchangeset.ApplyChangesets(nil, *e, nil, []commonchangeset.ChangesetApplication{ + { + Changeset: commonchangeset.WrapChangeSet(changeset.UpdateOnRampsDests), + Config: changeset.UpdateOnRampDestsConfig{ + UpdatesByChain: map[uint64]map[uint64]changeset.OnRampDestinationUpdate{ + from: { + to: { + IsEnabled: true, + TestRouter: false, + AllowListEnabled: false, + }, + }, + }, + }, + }, + { + Changeset: commonchangeset.WrapChangeSet(changeset.UpdateFeeQuoterPricesCS), + Config: changeset.UpdateFeeQuoterPricesConfig{ + PricesByChain: map[uint64]changeset.FeeQuoterPriceUpdatePerSource{ + from: { + TokenPrices: map[common.Address]*big.Int{ + stateChain1.LinkToken.Address(): changeset.DefaultLinkPrice, + stateChain1.Weth9.Address(): changeset.DefaultWethPrice, + }, + GasPrices: map[uint64]*big.Int{ + to: changeset.DefaultGasPrice, + }, + }, + }, + }, + }, + { + Changeset: commonchangeset.WrapChangeSet(changeset.UpdateFeeQuoterDests), + Config: changeset.UpdateFeeQuoterDestsConfig{ + UpdatesByChain: map[uint64]map[uint64]fee_quoter.FeeQuoterDestChainConfig{ + from: { + to: changeset.DefaultFeeQuoterDestChainConfig(), + }, + }, + }, + }, + { + Changeset: commonchangeset.WrapChangeSet(changeset.UpdateOffRampSources), + Config: changeset.UpdateOffRampSourcesConfig{ + UpdatesByChain: map[uint64]map[uint64]changeset.OffRampSourceUpdate{ + to: { + from: { + IsEnabled: true, + TestRouter: true, + }, + }, + }, + }, + }, + { + Changeset: commonchangeset.WrapChangeSet(changeset.UpdateRouterRamps), + Config: changeset.UpdateRouterRampsConfig{ + TestRouter: true, + UpdatesByChain: map[uint64]changeset.RouterUpdates{ + // onRamp update on source chain + from: { + OnRampUpdates: map[uint64]bool{ + to: true, + }, + }, + // off + from: { + OffRampUpdates: map[uint64]bool{ + to: true, + }, + }, + }, + }, + }, + }) + if err != nil { + return DeployCCIPOutput{}, fmt.Errorf("failed to apply changesets: %w", err) + } + e = &newEnv + } + } } addresses, err := e.ExistingAddresses.Addresses() diff --git a/integration-tests/contracts/ccipreader_test.go b/integration-tests/contracts/ccipreader_test.go index 3b0ac1d79a1..f46a680c8a0 100644 --- a/integration-tests/contracts/ccipreader_test.go +++ b/integration-tests/contracts/ccipreader_test.go @@ -597,8 +597,8 @@ func TestCCIPReader_GetExpectedNextSequenceNumber(t *testing.T) { selectors := env.Env.AllChainSelectors() destChain, srcChain := selectors[0], selectors[1] - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, destChain, srcChain, false)) - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, srcChain, destChain, false)) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, destChain, srcChain, false) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, srcChain, destChain, false) reader := testSetupRealContracts( ctx, @@ -707,8 +707,8 @@ func Test_GetChainFeePriceUpdates(t *testing.T) { selectors := env.Env.AllChainSelectors() chain1, chain2 := selectors[0], selectors[1] - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain1, chain2, false)) - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain2, chain1, false)) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain1, chain2, false) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain2, chain1, false) // Change the gas price for chain2 feeQuoter := state.Chains[chain1].FeeQuoter @@ -763,8 +763,8 @@ func Test_LinkPriceUSD(t *testing.T) { selectors := env.Env.AllChainSelectors() chain1, chain2 := selectors[0], selectors[1] - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain1, chain2, false)) - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain2, chain1, false)) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain1, chain2, false) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain2, chain1, false) reader := testSetupRealContracts( ctx, @@ -785,7 +785,7 @@ func Test_LinkPriceUSD(t *testing.T) { linkPriceUSD, err := reader.LinkPriceUSD(ctx) require.NoError(t, err) require.NotNil(t, linkPriceUSD.Int) - require.Equal(t, changeset.DefaultInitialPrices.LinkPrice, linkPriceUSD.Int) + require.Equal(t, changeset.DefaultLinkPrice, linkPriceUSD.Int) } func Test_GetMedianDataAvailabilityGasConfig(t *testing.T) { @@ -798,9 +798,9 @@ func Test_GetMedianDataAvailabilityGasConfig(t *testing.T) { selectors := env.Env.AllChainSelectors() destChain, chain1, chain2, chain3 := selectors[0], selectors[1], selectors[2], selectors[3] - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain1, destChain, false)) - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain2, destChain, false)) - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain3, destChain, false)) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain1, destChain, false) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain2, destChain, false) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain3, destChain, false) boundContracts := map[cciptypes.ChainSelector][]types.BoundContract{} for i, selector := range env.Env.AllChainSelectorsExcluding([]uint64{destChain}) { @@ -857,8 +857,8 @@ func Test_GetWrappedNativeTokenPriceUSD(t *testing.T) { selectors := env.Env.AllChainSelectors() chain1, chain2 := selectors[0], selectors[1] - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain1, chain2, false)) - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(env.Env, state, chain2, chain1, false)) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain1, chain2, false) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &env, state, chain2, chain1, false) reader := testSetupRealContracts( ctx, @@ -884,7 +884,7 @@ func Test_GetWrappedNativeTokenPriceUSD(t *testing.T) { // Only chainD has reader contracts bound require.Len(t, prices, 1) - require.Equal(t, changeset.DefaultInitialPrices.WethPrice, prices[cciptypes.ChainSelector(chain1)].Int) + require.Equal(t, changeset.DefaultWethPrice, prices[cciptypes.ChainSelector(chain1)].Int) } // Benchmark Results: diff --git a/integration-tests/smoke/ccip/ccip_batching_test.go b/integration-tests/smoke/ccip/ccip_batching_test.go index 58f4e922ac5..3752faa4e6e 100644 --- a/integration-tests/smoke/ccip/ccip_batching_test.go +++ b/integration-tests/smoke/ccip/ccip_batching_test.go @@ -63,8 +63,8 @@ func newBatchTestSetup(t *testing.T) batchTestSetup { ) // connect sourceChain1 and sourceChain2 to destChain - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(e.Env, state, sourceChain1, destChain, false)) - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(e.Env, state, sourceChain2, destChain, false)) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &e, state, sourceChain1, destChain, false) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &e, state, sourceChain2, destChain, false) return batchTestSetup{e, state, sourceChain1, sourceChain2, destChain} } diff --git a/integration-tests/smoke/ccip/ccip_fee_boosting_test.go b/integration-tests/smoke/ccip/ccip_fee_boosting_test.go index 576ee356fbb..3b0ebf22455 100644 --- a/integration-tests/smoke/ccip/ccip_fee_boosting_test.go +++ b/integration-tests/smoke/ccip/ccip_fee_boosting_test.go @@ -2,15 +2,13 @@ package smoke import ( "context" - "fmt" "math/big" "testing" "time" - "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-common/pkg/config" - commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + + commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -20,11 +18,10 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ccipevm" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/fee_quoter" - cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ccipevm" + "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/onramp" @@ -84,32 +81,31 @@ func Test_CCIPFeeBoosting(t *testing.T) { ) t.Log("Adjusted gas price on dest chain:", adjustedGasPriceDest) - initialPrices := changeset.InitialPrices{ - LinkPrice: linkPrice, - WethPrice: wethPrice, - GasPrice: changeset.ToPackedFee(adjustedGasPriceDest, big.NewInt(0)), - } - - laneCfg := changeset.LaneConfig{ - SourceSelector: sourceChain, - DestSelector: destChain, - InitialPricesBySource: initialPrices, - FeeQuoterDestChain: changeset.DefaultFeeQuoterDestChainConfig(), - TestRouter: false, - } - - e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, []commonchangeset.ChangesetApplication{ - { - Changeset: commonchangeset.WrapChangeSet(changeset.AddLanes), - Config: changeset.AddLanesConfig{LaneConfigs: []changeset.LaneConfig{laneCfg}}, + changeset.AddLane(t, &e, sourceChain, destChain, false, + map[uint64]*big.Int{ + destChain: changeset.ToPackedFee(adjustedGasPriceDest, big.NewInt(0)), }, - }) - require.NoError(t, err) + map[common.Address]*big.Int{ + state.Chains[sourceChain].LinkToken.Address(): linkPrice, + state.Chains[sourceChain].Weth9.Address(): wethPrice, + }, + changeset.DefaultFeeQuoterDestChainConfig()) // Update token prices in destination chain FeeQuoter - err = updateTokensPrices(e, state, destChain, map[common.Address]*big.Int{ - state.Chains[destChain].LinkToken.Address(): linkPrice, - state.Chains[destChain].Weth9.Address(): wethPrice, + e.Env, err = commoncs.ApplyChangesets(t, e.Env, e.TimelockContracts(t), []commoncs.ChangesetApplication{ + { + Changeset: commoncs.WrapChangeSet(changeset.UpdateFeeQuoterPricesCS), + Config: changeset.UpdateFeeQuoterPricesConfig{ + PricesByChain: map[uint64]changeset.FeeQuoterPriceUpdatePerSource{ + destChain: { + TokenPrices: map[common.Address]*big.Int{ + state.Chains[destChain].LinkToken.Address(): linkPrice, + state.Chains[destChain].Weth9.Address(): wethPrice, + }, + }, + }, + }, + }, }) require.NoError(t, err) @@ -137,7 +133,20 @@ func Test_CCIPFeeBoosting(t *testing.T) { DestChainSelector: destChain, }] = []uint64{msgSentEvent.SequenceNumber} - err = updateGasPrice(e, state, sourceChain, destChain, originalGasPriceDestUSD) + e.Env, err = commoncs.ApplyChangesets(t, e.Env, e.TimelockContracts(t), []commoncs.ChangesetApplication{ + { + Changeset: commoncs.WrapChangeSet(changeset.UpdateFeeQuoterPricesCS), + Config: changeset.UpdateFeeQuoterPricesConfig{ + PricesByChain: map[uint64]changeset.FeeQuoterPriceUpdatePerSource{ + sourceChain: { + GasPrices: map[uint64]*big.Int{ + destChain: originalGasPriceDestUSD, + }, + }, + }, + }, + }, + }) require.NoError(t, err) // Confirm gas prices are updated @@ -270,61 +279,3 @@ func convertToMessage(msg onramp.InternalEVM2AnyRampMessage) cciptypes.Message { TokenAmounts: tokenAmounts, } } - -func updateGasPrice(env changeset.DeployedEnv, state changeset.CCIPOnChainState, srcChain, destChain uint64, gasPrice *big.Int) error { - chainState, exists := state.Chains[srcChain] - if !exists { - return fmt.Errorf("chain state not found for selector: %d", srcChain) - } - - feeQuoter := chainState.FeeQuoter - // Update gas price - auth := env.Env.Chains[srcChain].DeployerKey - tx, err := feeQuoter.UpdatePrices(auth, fee_quoter.InternalPriceUpdates{ - TokenPriceUpdates: nil, - GasPriceUpdates: []fee_quoter.InternalGasPriceUpdate{ - { - DestChainSelector: destChain, - UsdPerUnitGas: gasPrice, - }, - }, - }) - if err != nil { - return errors.Wrapf(err, "updating gas price on chain %d", srcChain) - } - if _, err := deployment.ConfirmIfNoError(env.Env.Chains[srcChain], tx, err); err != nil { - return err - } - - return nil -} - -func updateTokensPrices(env changeset.DeployedEnv, state changeset.CCIPOnChainState, chain uint64, tokenPrices map[common.Address]*big.Int) error { - chainState, exists := state.Chains[chain] - if !exists { - return fmt.Errorf("chain state not found for selector: %d", chain) - } - - feeQuoter := chainState.FeeQuoter - // Update token prices - auth := env.Env.Chains[chain].DeployerKey - tokenPricesUpdates := make([]fee_quoter.InternalTokenPriceUpdate, 0, len(tokenPrices)) - for token, price := range tokenPrices { - tokenPricesUpdates = append(tokenPricesUpdates, fee_quoter.InternalTokenPriceUpdate{ - SourceToken: token, - UsdPerToken: price, - }) - } - tx, err := feeQuoter.UpdatePrices(auth, fee_quoter.InternalPriceUpdates{ - TokenPriceUpdates: tokenPricesUpdates, - GasPriceUpdates: nil, - }) - if err != nil { - return errors.Wrapf(err, "updating token prices on chain %d", chain) - } - if _, err := deployment.ConfirmIfNoError(env.Env.Chains[chain], tx, err); err != nil { - return err - } - - return nil -} diff --git a/integration-tests/smoke/ccip/ccip_fees_test.go b/integration-tests/smoke/ccip/ccip_fees_test.go index 55788a4aa5f..57a6bc58d82 100644 --- a/integration-tests/smoke/ccip/ccip_fees_test.go +++ b/integration-tests/smoke/ccip/ccip_fees_test.go @@ -130,7 +130,7 @@ func Test_CCIPFees(t *testing.T) { changeset.ReplayLogs(t, e.Offchain, tenv.ReplayBlocks) // Add all lanes - require.NoError(t, changeset.AddLanesForAll(e, state)) + changeset.AddLanesForAll(t, &tenv, state) t.Run("Send programmable token transfer pay with Link token", func(t *testing.T) { runFeeTokenTestCase(feeTokenTestCase{ diff --git a/integration-tests/smoke/ccip/ccip_gas_price_updates_test.go b/integration-tests/smoke/ccip/ccip_gas_price_updates_test.go index d11e4304366..2c1d97f6c12 100644 --- a/integration-tests/smoke/ccip/ccip_gas_price_updates_test.go +++ b/integration-tests/smoke/ccip/ccip_gas_price_updates_test.go @@ -34,7 +34,7 @@ func Test_CCIPGasPriceUpdates(t *testing.T) { ) state, err := changeset.LoadOnchainState(e.Env) require.NoError(t, err) - require.NoError(t, changeset.AddLanesForAll(e.Env, state)) + changeset.AddLanesForAll(t, &e, state) allChainSelectors := maps.Keys(e.Env.Chains) assert.GreaterOrEqual(t, len(allChainSelectors), 2, "test requires at least 2 chains") diff --git a/integration-tests/smoke/ccip/ccip_message_limitations_test.go b/integration-tests/smoke/ccip/ccip_message_limitations_test.go index 9398fd9f932..f9299b735d0 100644 --- a/integration-tests/smoke/ccip/ccip_message_limitations_test.go +++ b/integration-tests/smoke/ccip/ccip_message_limitations_test.go @@ -29,7 +29,7 @@ func Test_CCIPMessageLimitations(t *testing.T) { onChainState, err := changeset.LoadOnchainState(testEnv.Env) require.NoError(t, err) - require.NoError(t, changeset.AddLanesForAll(testEnv.Env, onChainState)) + changeset.AddLanesForAll(t, &testEnv, onChainState) srcToken, _ := setupTokens( t, diff --git a/integration-tests/smoke/ccip/ccip_messaging_test.go b/integration-tests/smoke/ccip/ccip_messaging_test.go index 13f14fcda16..8ee18a31918 100644 --- a/integration-tests/smoke/ccip/ccip_messaging_test.go +++ b/integration-tests/smoke/ccip/ccip_messaging_test.go @@ -63,7 +63,7 @@ func Test_CCIPMessaging(t *testing.T) { ", dest chain selector:", destChain, ) // connect a single lane, source to dest - require.NoError(t, changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(e.Env, state, sourceChain, destChain, false)) + changeset.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &e, state, sourceChain, destChain, false) var ( replayed bool diff --git a/integration-tests/smoke/ccip/ccip_ooo_execution_test.go b/integration-tests/smoke/ccip/ccip_ooo_execution_test.go index 19c36c6e021..e3da473984d 100644 --- a/integration-tests/smoke/ccip/ccip_ooo_execution_test.go +++ b/integration-tests/smoke/ccip/ccip_ooo_execution_test.go @@ -86,7 +86,7 @@ func Test_OutOfOrderExecution(t *testing.T) { }, }, ) - require.NoError(t, changeset.AddLanesForAll(e, state)) + changeset.AddLanesForAll(t, &tenv, state) tokenTransfer := []router.ClientEVMTokenAmount{ { diff --git a/integration-tests/smoke/ccip/ccip_rmn_test.go b/integration-tests/smoke/ccip/ccip_rmn_test.go index 166f4422fe6..a3877013103 100644 --- a/integration-tests/smoke/ccip/ccip_rmn_test.go +++ b/integration-tests/smoke/ccip/ccip_rmn_test.go @@ -323,7 +323,7 @@ func runRmnTestCase(t *testing.T, tc rmnTestCase) { tc.killMarkedRmnNodes(t, rmnCluster) changeset.ReplayLogs(t, envWithRMN.Env.Offchain, envWithRMN.ReplayBlocks) - require.NoError(t, changeset.AddLanesForAll(envWithRMN.Env, onChainState)) + changeset.AddLanesForAll(t, &envWithRMN, onChainState) disabledNodes := tc.disableOraclesIfThisIsACursingTestCase(ctx, t, envWithRMN) startBlocks, seqNumCommit, seqNumExec := tc.sendMessages(t, onChainState, envWithRMN) diff --git a/integration-tests/smoke/ccip/ccip_token_price_updates_test.go b/integration-tests/smoke/ccip/ccip_token_price_updates_test.go index e3496b6f407..fb7ddc847d4 100644 --- a/integration-tests/smoke/ccip/ccip_token_price_updates_test.go +++ b/integration-tests/smoke/ccip/ccip_token_price_updates_test.go @@ -35,7 +35,7 @@ func Test_CCIPTokenPriceUpdates(t *testing.T) { })) state, err := changeset.LoadOnchainState(e.Env) require.NoError(t, err) - require.NoError(t, changeset.AddLanesForAll(e.Env, state)) + changeset.AddLanesForAll(t, &e, state) allChainSelectors := maps.Keys(e.Env.Chains) assert.GreaterOrEqual(t, len(allChainSelectors), 2, "test requires at least 2 chains") diff --git a/integration-tests/smoke/ccip/ccip_token_transfer_test.go b/integration-tests/smoke/ccip/ccip_token_transfer_test.go index 2088960639e..c5cabfe63e4 100644 --- a/integration-tests/smoke/ccip/ccip_token_transfer_test.go +++ b/integration-tests/smoke/ccip/ccip_token_transfer_test.go @@ -69,7 +69,7 @@ func TestTokenTransfer(t *testing.T) { "SELF_SERVE_TOKEN", ) require.NoError(t, err) - require.NoError(t, changeset.AddLanesForAll(e, state)) + changeset.AddLanesForAll(t, &tenv, state) changeset.MintAndAllow( t, diff --git a/integration-tests/smoke/ccip/ccip_usdc_test.go b/integration-tests/smoke/ccip/ccip_usdc_test.go index 174ab941387..7bea68a9cbf 100644 --- a/integration-tests/smoke/ccip/ccip_usdc_test.go +++ b/integration-tests/smoke/ccip/ccip_usdc_test.go @@ -71,7 +71,7 @@ func TestUSDCTokenTransfer(t *testing.T) { require.NoError(t, err) // Add all lanes - require.NoError(t, changeset.AddLanesForAll(e, state)) + changeset.AddLanesForAll(t, &tenv, state) changeset.MintAndAllow( t,