Skip to content

Commit 1e33565

Browse files
authored
Consolidate CS to enable lane between an existing EVM chain and solana (#17607)
* clean up * updates * changes * consolidated changeset * updates * updates * tests * all changes * updates * updates * fix lint * more fix
1 parent 8e5a247 commit 1e33565

14 files changed

+702
-69
lines changed

deployment/ccip/changeset/crossfamily/v1_6/cs_add_evm_solana_lane.go

Lines changed: 443 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package v1_6_test
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
"time"
7+
8+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/gagliardetto/solana-go"
11+
"github.com/stretchr/testify/require"
12+
13+
solOffRamp "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp"
14+
solRouter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router"
15+
solFeeQuoter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/fee_quoter"
16+
solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state"
17+
"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
18+
19+
ccipchangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
20+
crossfamily "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/crossfamily/v1_6"
21+
ccipChangesetSolana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana"
22+
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers"
23+
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6"
24+
commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset"
25+
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
26+
)
27+
28+
func TestAddEVMSolanaLaneBidirectional(t *testing.T) {
29+
for _, tc := range []struct {
30+
name string
31+
mcmsEnabled bool
32+
}{
33+
{
34+
name: "MCMS disabled",
35+
mcmsEnabled: false,
36+
},
37+
{
38+
name: "MCMS enabled",
39+
mcmsEnabled: true,
40+
},
41+
} {
42+
t.Run(tc.name, func(t *testing.T) {
43+
t.Parallel()
44+
ctx := testcontext.Get(t)
45+
tenv, _ := testhelpers.NewMemoryEnvironment(t, testhelpers.WithSolChains(1))
46+
e := tenv.Env
47+
solChains := tenv.Env.AllChainSelectorsSolana()
48+
require.NotEmpty(t, solChains)
49+
evmChains := tenv.Env.AllChainSelectors()
50+
require.NotEmpty(t, evmChains)
51+
solChain := solChains[0]
52+
evmChain := evmChains[0]
53+
evmState, err := ccipchangeset.LoadOnchainState(e)
54+
require.NoError(t, err)
55+
var mcmsConfig *ccipChangesetSolana.MCMSConfigSolana
56+
if tc.mcmsEnabled {
57+
_, _ = testhelpers.TransferOwnershipSolana(t, &e, solChain, true,
58+
ccipChangesetSolana.CCIPContractsToTransfer{
59+
Router: true,
60+
FeeQuoter: true,
61+
OffRamp: true,
62+
})
63+
mcmsConfig = &ccipChangesetSolana.MCMSConfigSolana{
64+
MCMS: &proposalutils.TimelockConfig{
65+
MinDelay: 1 * time.Second,
66+
},
67+
RouterOwnedByTimelock: true,
68+
FeeQuoterOwnedByTimelock: true,
69+
OffRampOwnedByTimelock: true,
70+
}
71+
testhelpers.TransferToTimelock(t, tenv, evmState, []uint64{evmChain})
72+
}
73+
74+
// Add EVM and Solana lane
75+
evmChainState := evmState.Chains[evmChain]
76+
feeQCfgSolana := solFeeQuoter.DestChainConfig{
77+
IsEnabled: true,
78+
DefaultTxGasLimit: 200000,
79+
MaxPerMsgGasLimit: 3000000,
80+
MaxDataBytes: 30000,
81+
MaxNumberOfTokensPerMsg: 5,
82+
DefaultTokenDestGasOverhead: 90000,
83+
DestGasOverhead: 90000,
84+
// bytes4(keccak256("CCIP ChainFamilySelector EVM"))
85+
ChainFamilySelector: [4]uint8{40, 18, 213, 44},
86+
}
87+
feeQCfgEVM := v1_6.DefaultFeeQuoterDestChainConfig(true, solChain)
88+
evmSolanaLaneCSInput := crossfamily.AddRemoteChainE2EConfig{
89+
SolanaChainSelector: solChain,
90+
EVMChainSelector: evmChain,
91+
IsTestRouter: true,
92+
EVMOnRampAllowListEnabled: false,
93+
EVMFeeQuoterDestChainInput: feeQCfgEVM,
94+
InitialSolanaGasPriceForEVMFeeQuoter: testhelpers.DefaultGasPrice,
95+
InitialEVMTokenPricesForEVMFeeQuoter: map[common.Address]*big.Int{
96+
evmChainState.LinkToken.Address(): testhelpers.DefaultLinkPrice,
97+
evmChainState.Weth9.Address(): testhelpers.DefaultWethPrice,
98+
},
99+
IsRMNVerificationEnabledOnEVMOffRamp: true,
100+
SolanaRouterConfig: ccipChangesetSolana.RouterConfig{
101+
RouterDestinationConfig: solRouter.DestChainConfig{
102+
AllowListEnabled: true,
103+
AllowedSenders: []solana.PublicKey{e.SolChains[solChain].DeployerKey.PublicKey()},
104+
},
105+
},
106+
SolanaOffRampConfig: ccipChangesetSolana.OffRampConfig{
107+
EnabledAsSource: true,
108+
},
109+
SolanaFeeQuoterConfig: ccipChangesetSolana.FeeQuoterConfig{
110+
FeeQuoterDestinationConfig: feeQCfgSolana,
111+
},
112+
MCMSConfig: mcmsConfig,
113+
}
114+
115+
// run the changeset
116+
e, _, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{
117+
commonchangeset.Configure(crossfamily.AddEVMAndSolanaLaneChangeset, evmSolanaLaneCSInput),
118+
})
119+
require.NoError(t, err)
120+
121+
// Check that the changeset was applied
122+
evmState, err = ccipchangeset.LoadOnchainState(e)
123+
require.NoError(t, err)
124+
125+
solanaState, err := ccipchangeset.LoadOnchainStateSolana(e)
126+
require.NoError(t, err)
127+
128+
// evm changes
129+
evmChainState = evmState.Chains[evmChain]
130+
131+
destCfg, err := evmChainState.OnRamp.GetDestChainConfig(&bind.CallOpts{Context: ctx}, solChain)
132+
require.NoError(t, err)
133+
require.Equal(t, evmChainState.TestRouter.Address(), destCfg.Router)
134+
require.False(t, destCfg.AllowlistEnabled)
135+
136+
srcCfg, err := evmChainState.OffRamp.GetSourceChainConfig(&bind.CallOpts{Context: ctx}, solChain)
137+
require.NoError(t, err)
138+
require.Equal(t, evmChainState.TestRouter.Address(), destCfg.Router)
139+
require.True(t, srcCfg.IsRMNVerificationDisabled)
140+
require.True(t, srcCfg.IsEnabled)
141+
expOnRamp, err := evmState.GetOnRampAddressBytes(solChain)
142+
require.NoError(t, err)
143+
require.Equal(t, expOnRamp, srcCfg.OnRamp)
144+
145+
fqDestCfg, err := evmChainState.FeeQuoter.GetDestChainConfig(&bind.CallOpts{Context: ctx}, solChain)
146+
require.NoError(t, err)
147+
testhelpers.AssertEqualFeeConfig(t, feeQCfgEVM, fqDestCfg)
148+
149+
// solana changes
150+
var offRampSourceChain solOffRamp.SourceChain
151+
var destChainStateAccount solRouter.DestChain
152+
var destChainFqAccount solFeeQuoter.DestChain
153+
var offRampEvmSourceChainPDA solana.PublicKey
154+
var evmDestChainStatePDA solana.PublicKey
155+
var fqEvmDestChainPDA solana.PublicKey
156+
offRampEvmSourceChainPDA, _, _ = solState.FindOfframpSourceChainPDA(evmChain, solanaState.SolChains[solChain].OffRamp)
157+
err = e.SolChains[solChain].GetAccountDataBorshInto(e.GetContext(), offRampEvmSourceChainPDA, &offRampSourceChain)
158+
require.NoError(t, err)
159+
require.True(t, offRampSourceChain.Config.IsEnabled)
160+
161+
fqEvmDestChainPDA, _, _ = solState.FindFqDestChainPDA(evmChain, solanaState.SolChains[solChain].FeeQuoter)
162+
err = e.SolChains[solChain].GetAccountDataBorshInto(e.GetContext(), fqEvmDestChainPDA, &destChainFqAccount)
163+
require.NoError(t, err, "failed to get account info")
164+
require.Equal(t, solFeeQuoter.TimestampedPackedU224{}, destChainFqAccount.State.UsdPerUnitGas)
165+
require.True(t, destChainFqAccount.Config.IsEnabled)
166+
require.Equal(t, feeQCfgSolana, destChainFqAccount.Config)
167+
168+
evmDestChainStatePDA, _ = solState.FindDestChainStatePDA(evmChain, solanaState.SolChains[solChain].Router)
169+
err = e.SolChains[solChain].GetAccountDataBorshInto(e.GetContext(), evmDestChainStatePDA, &destChainStateAccount)
170+
require.NoError(t, err)
171+
require.NotEmpty(t, destChainStateAccount.Config.AllowedSenders)
172+
require.True(t, destChainStateAccount.Config.AllowListEnabled)
173+
})
174+
}
175+
}

deployment/ccip/changeset/cs_prerequisites.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
"github.com/ethereum/go-ethereum/common"
88
"github.com/pkg/errors"
9-
"github.com/smartcontractkit/ccip-owner-contracts/pkg/proposal/timelock"
109
"golang.org/x/sync/errgroup"
1110

1211
"github.com/smartcontractkit/chainlink-ccip/pkg/reader"
@@ -57,7 +56,6 @@ func DeployPrerequisitesChangeset(env deployment.Environment, cfg DeployPrerequi
5756
}, fmt.Errorf("failed to deploy prerequisite contracts: %w", err)
5857
}
5958
return deployment.ChangesetOutput{
60-
Proposals: []timelock.MCMSWithTimelockProposal{},
6159
AddressBook: ab,
6260
}, nil
6361
}

deployment/ccip/changeset/solana/cs_add_remote_chain.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ package solana
22

33
import (
44
"context"
5-
"math"
6-
75
"fmt"
6+
"math"
87
"strconv"
98

109
"github.com/gagliardetto/solana-go"

deployment/ccip/changeset/testhelpers/test_helpers.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6"
3232
commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset"
3333
"github.com/smartcontractkit/chainlink/deployment/common/changeset/state"
34+
commonstate "github.com/smartcontractkit/chainlink/deployment/common/changeset/state"
3435
"github.com/smartcontractkit/chainlink/deployment/common/proposalutils"
3536
commontypes "github.com/smartcontractkit/chainlink/deployment/common/types"
3637
"github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ccipevm"
@@ -2316,7 +2317,7 @@ func DeployLinkTokenTest(t *testing.T, solChains int) {
23162317
require.NoError(t, err)
23172318
addrs, err := e.ExistingAddresses.AddressesForChain(chain1)
23182319
require.NoError(t, err)
2319-
state, err := commoncs.MaybeLoadLinkTokenChainState(e.Chains[chain1], addrs)
2320+
state, err := commonstate.MaybeLoadLinkTokenChainState(e.Chains[chain1], addrs)
23202321
require.NoError(t, err)
23212322
// View itself already unit tested
23222323
_, err = state.GenerateLinkView()
@@ -2329,3 +2330,33 @@ func DeployLinkTokenTest(t *testing.T, solChains int) {
23292330
require.NotEmpty(t, addrs)
23302331
}
23312332
}
2333+
2334+
func TransferToTimelock(
2335+
t *testing.T,
2336+
tenv DeployedEnv,
2337+
state changeset.CCIPOnChainState,
2338+
chains []uint64,
2339+
) {
2340+
timelockContracts := make(map[uint64]*proposalutils.TimelockExecutionContracts, len(chains)+1)
2341+
for _, chain := range chains {
2342+
timelockContracts[chain] = &proposalutils.TimelockExecutionContracts{
2343+
Timelock: state.Chains[chain].Timelock,
2344+
CallProxy: state.Chains[chain].CallProxy,
2345+
}
2346+
}
2347+
// Add the home chain to the timelock contracts.
2348+
timelockContracts[tenv.HomeChainSel] = &proposalutils.TimelockExecutionContracts{
2349+
Timelock: state.Chains[tenv.HomeChainSel].Timelock,
2350+
CallProxy: state.Chains[tenv.HomeChainSel].CallProxy,
2351+
}
2352+
// Transfer ownership to timelock so that we can promote the zero digest later down the line.
2353+
_, err := commoncs.Apply(t, tenv.Env,
2354+
timelockContracts,
2355+
commoncs.Configure(
2356+
cldf.CreateLegacyChangeSet(commoncs.TransferToMCMSWithTimelockV2),
2357+
GenTestTransferOwnershipConfig(tenv, chains, state),
2358+
),
2359+
)
2360+
require.NoError(t, err)
2361+
AssertTimelockOwnership(t, tenv, chains, state)
2362+
}

deployment/ccip/changeset/v1_5_1/cs_add_token_e2e.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ func addTokenE2ELogic(env deployment.Environment, config AddTokensE2EConfig) (de
348348
// if there are multiple proposals, aggregate them so that we don't have to propose them separately
349349
if len(finalCSOut.MCMSTimelockProposals) > 1 {
350350
aggregatedProposals, err := proposalutils.AggregateProposals(
351-
e, state.EVMMCMSStateByChain(), finalCSOut.MCMSTimelockProposals, nil,
351+
e, state.EVMMCMSStateByChain(), nil, finalCSOut.MCMSTimelockProposals, nil,
352352
"Add Tokens E2E", config.MCMS)
353353
if err != nil {
354354
return deployment.ChangesetOutput{}, fmt.Errorf("failed to aggregate proposals: %w", err)

deployment/ccip/changeset/v1_6/cs_add_new_chain_e2e.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
mcmslib "github.com/smartcontractkit/mcms"
1212

1313
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
14+
1415
"github.com/smartcontractkit/chainlink/deployment"
1516
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
1617
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/globals"
@@ -404,6 +405,7 @@ func addCandidatesForNewChainLogic(e deployment.Environment, c AddCandidatesForN
404405
proposal, err := proposalutils.AggregateProposals(
405406
e,
406407
state.EVMMCMSStateByChain(),
408+
nil,
407409
allProposals,
408410
nil,
409411
fmt.Sprintf("Deploy and set candidates for chain with selector %d", c.NewChain.Selector),
@@ -583,6 +585,7 @@ func promoteNewChainForConfigLogic(e deployment.Environment, c PromoteNewChainFo
583585
proposal, err := proposalutils.AggregateProposals(
584586
e,
585587
state.EVMMCMSStateByChain(),
588+
nil,
586589
allProposals,
587590
nil,
588591
fmt.Sprintf("Promote chain with selector %d for testing", c.NewChain.Selector),
@@ -801,6 +804,7 @@ func connectNewChainLogic(env deployment.Environment, c ConnectNewChainConfig) (
801804
proposal, err := proposalutils.AggregateProposals(
802805
env,
803806
state.EVMMCMSStateByChain(),
807+
nil,
804808
allEnablementProposals,
805809
ownershipTransferProposals,
806810
fmt.Sprintf("Connect chain with selector %d to other chains", c.NewChainSelector),

deployment/ccip/changeset/v1_6/cs_ccip_home_test.go

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"
1616

1717
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
18+
1819
"github.com/smartcontractkit/chainlink/deployment"
1920
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
2021
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/globals"
@@ -123,7 +124,7 @@ func Test_PromoteCandidate(t *testing.T) {
123124

124125
if tc.mcmsEnabled {
125126
// Transfer ownership to timelock so that we can promote the zero digest later down the line.
126-
transferToTimelock(t, tenv, state, []uint64{source, dest})
127+
testhelpers.TransferToTimelock(t, tenv, state, []uint64{source, dest})
127128
}
128129

129130
var (
@@ -221,7 +222,7 @@ func Test_SetCandidate(t *testing.T) {
221222

222223
if tc.mcmsEnabled {
223224
// Transfer ownership to timelock so that we can promote the zero digest later down the line.
224-
transferToTimelock(t, tenv, state, []uint64{source, dest})
225+
testhelpers.TransferToTimelock(t, tenv, state, []uint64{source, dest})
225226
}
226227

227228
var (
@@ -361,7 +362,7 @@ func Test_RevokeCandidate(t *testing.T) {
361362

362363
if tc.mcmsEnabled {
363364
// Transfer ownership to timelock so that we can promote the zero digest later down the line.
364-
transferToTimelock(t, tenv, state, []uint64{source, dest})
365+
testhelpers.TransferToTimelock(t, tenv, state, []uint64{source, dest})
365366
}
366367

367368
var (
@@ -486,36 +487,6 @@ func Test_RevokeCandidate(t *testing.T) {
486487
}
487488
}
488489

489-
func transferToTimelock(
490-
t *testing.T,
491-
tenv testhelpers.DeployedEnv,
492-
state changeset.CCIPOnChainState,
493-
chains []uint64,
494-
) {
495-
timelockContracts := make(map[uint64]*proposalutils.TimelockExecutionContracts, len(chains)+1)
496-
for _, chain := range chains {
497-
timelockContracts[chain] = &proposalutils.TimelockExecutionContracts{
498-
Timelock: state.Chains[chain].Timelock,
499-
CallProxy: state.Chains[chain].CallProxy,
500-
}
501-
}
502-
// Add the home chain to the timelock contracts.
503-
timelockContracts[tenv.HomeChainSel] = &proposalutils.TimelockExecutionContracts{
504-
Timelock: state.Chains[tenv.HomeChainSel].Timelock,
505-
CallProxy: state.Chains[tenv.HomeChainSel].CallProxy,
506-
}
507-
// Transfer ownership to timelock so that we can promote the zero digest later down the line.
508-
_, err := commonchangeset.Apply(t, tenv.Env,
509-
timelockContracts,
510-
commonchangeset.Configure(
511-
cldf.CreateLegacyChangeSet(commonchangeset.TransferToMCMSWithTimelock),
512-
testhelpers.GenTestTransferOwnershipConfig(tenv, chains, state),
513-
),
514-
)
515-
require.NoError(t, err)
516-
testhelpers.AssertTimelockOwnership(t, tenv, chains, state)
517-
}
518-
519490
func Test_UpdateChainConfigs(t *testing.T) {
520491
for _, tc := range []struct {
521492
name string
@@ -542,7 +513,7 @@ func Test_UpdateChainConfigs(t *testing.T) {
542513

543514
if tc.mcmsEnabled {
544515
// Transfer ownership to timelock so that we can promote the zero digest later down the line.
545-
transferToTimelock(t, tenv, state, []uint64{source, dest})
516+
testhelpers.TransferToTimelock(t, tenv, state, []uint64{source, dest})
546517
}
547518

548519
ccipHome := state.Chains[tenv.HomeChainSel].CCIPHome

0 commit comments

Comments
 (0)