@@ -6,13 +6,15 @@ import (
66 "errors"
77 "fmt"
88 "math/big"
9+ "slices"
910 "strings"
1011
1112 "github.com/ethereum/go-ethereum/accounts/abi/bind"
1213 "github.com/ethereum/go-ethereum/common"
1314 "github.com/gagliardetto/solana-go"
1415 "golang.org/x/exp/maps"
1516
17+ "github.com/Masterminds/semver/v3"
1618 chain_selectors "github.com/smartcontractkit/chain-selectors"
1719
1820 mcmslib "github.com/smartcontractkit/mcms"
@@ -28,6 +30,7 @@ import (
2830 "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens"
2931 "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
3032 "github.com/smartcontractkit/chainlink-ccip/pluginconfig"
33+ "github.com/smartcontractkit/chainlink-deployments-framework/datastore"
3134
3235 capabilities_registry "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0"
3336
@@ -185,29 +188,36 @@ func validateUSDCConfig(usdcConfig *pluginconfig.USDCCCTPObserverConfig, state s
185188 if ! ok {
186189 return fmt .Errorf ("chain %d does not exist in EVM chain state but provided in USDCCCTPObserverConfig" , sel )
187190 }
188- if onchainState .USDCTokenPools == nil && onchainState .USDCTokenPoolsV1_6 == nil && onchainState .USDCTokenPoolProxies == nil {
189- return fmt .Errorf ("chain %d does not have any USDC token pools deployed" , sel )
191+ validSourcePools := make ([]common.Address , 0 , 3 )
192+ if pool , ok := onchainState .USDCTokenPools [deployment .Version1_5_1 ]; ok {
193+ validSourcePools = append (validSourcePools , pool .Address ())
194+ }
195+ if pool , ok := onchainState .USDCTokenPoolsV1_6 [deployment .Version1_6_2 ]; ok {
196+ validSourcePools = append (validSourcePools , pool .Address ())
190197 }
191-
192- var sourcePoolAddress common.Address
193198 if proxy , ok := onchainState .USDCTokenPoolProxies [deployment .Version1_7_0 ]; ok {
194- sourcePoolAddress = proxy
195- } else if pool , ok := onchainState .USDCTokenPoolsV1_6 [deployment .Version1_6_2 ]; ok {
196- sourcePoolAddress = pool .Address ()
197- } else if pool , ok := onchainState .USDCTokenPools [deployment .Version1_5_1 ]; ok {
198- sourcePoolAddress = pool .Address ()
199- } else {
199+ validSourcePools = append (validSourcePools , proxy )
200+ }
201+ if len (validSourcePools ) == 0 {
200202 return fmt .Errorf (
201203 "chain %d does not have USDC token pool deployed with version %s, %s, or %s" ,
202204 sel , deployment .Version1_5_1 , deployment .Version1_6_2 , deployment .Version1_7_0 ,
203205 )
204206 }
205207
206- if common .HexToAddress (token .SourcePoolAddress ) != sourcePoolAddress {
207- return fmt .Errorf ("chain %d has latest USDC token pool deployed at %s, " +
208- "but SourcePoolAddress %s is provided in USDCCCTPObserverConfig" ,
209- sel , sourcePoolAddress .String (), token .SourcePoolAddress )
208+ configuredSourcePool := common .HexToAddress (token .SourcePoolAddress )
209+ if slices .Contains (validSourcePools , configuredSourcePool ) {
210+ break
211+ }
212+
213+ expectedAddresses := make ([]string , 0 , len (validSourcePools ))
214+ for _ , sourcePoolAddress := range validSourcePools {
215+ expectedAddresses = append (expectedAddresses , sourcePoolAddress .String ())
210216 }
217+ return fmt .Errorf (
218+ "chain %d SourcePoolAddress %s is not one of the deployed USDC pools %v" ,
219+ sel , token .SourcePoolAddress , expectedAddresses ,
220+ )
211221 case chain_selectors .FamilySolana :
212222 onchainState , ok := state .SolChains [uint64 (sel )]
213223 if ! ok {
@@ -249,6 +259,53 @@ func validateUSDCConfig(usdcConfig *pluginconfig.USDCCCTPObserverConfig, state s
249259 return nil
250260}
251261
262+ func loadOnchainStateForCandidateChangesets (e cldf.Environment ) (stateview.CCIPOnChainState , error ) {
263+ state , err := stateview .LoadOnchainState (e )
264+ if err != nil {
265+ return stateview.CCIPOnChainState {}, err
266+ }
267+ if e .DataStore == nil {
268+ return state , nil
269+ }
270+
271+ for chainSelector := range e .BlockChains .EVMChains () {
272+ refs := e .DataStore .Addresses ().Filter (
273+ datastore .AddressRefByChainSelector (chainSelector ),
274+ datastore .AddressRefByType (datastore .ContractType (shared .USDCTokenPoolProxy )),
275+ datastore .AddressRefByVersion (& deployment .Version1_7_0 ),
276+ )
277+ if len (refs ) == 0 {
278+ continue
279+ }
280+ if len (refs ) > 1 {
281+ return stateview.CCIPOnChainState {}, fmt .Errorf (
282+ "multiple datastore entries found for %s %s on chain %d; qualifiers=%v" ,
283+ shared .USDCTokenPoolProxy , deployment .Version1_7_0 , chainSelector , maps .Keys (refsByQualifier (refs )),
284+ )
285+ }
286+
287+ chainState , ok := state .EVMChainState (chainSelector )
288+ if ! ok {
289+ return stateview.CCIPOnChainState {}, fmt .Errorf ("chain %d not found in state" , chainSelector )
290+ }
291+ if chainState .USDCTokenPoolProxies == nil {
292+ chainState .USDCTokenPoolProxies = make (map [semver.Version ]common.Address )
293+ }
294+ chainState .USDCTokenPoolProxies [deployment .Version1_7_0 ] = common .HexToAddress (refs [0 ].Address )
295+ state .WriteEVMChainState (chainSelector , chainState )
296+ }
297+
298+ return state , state .Validate ()
299+ }
300+
301+ func refsByQualifier (refs []datastore.AddressRef ) map [string ]struct {} {
302+ out := make (map [string ]struct {}, len (refs ))
303+ for _ , ref := range refs {
304+ out [ref .Qualifier ] = struct {}{}
305+ }
306+ return out
307+ }
308+
252309type CCIPOCRParams struct {
253310 // OCRParameters contains the parameters for the OCR plugin.
254311 OCRParameters commontypes.OCRParameters `json:"ocrParameters"`
@@ -312,7 +369,7 @@ type PromoteCandidateChangesetConfig struct {
312369}
313370
314371func (p PromoteCandidateChangesetConfig ) Validate (e cldf.Environment ) (map [uint64 ]uint32 , error ) {
315- state , err := stateview . LoadOnchainState (e )
372+ state , err := loadOnchainStateForCandidateChangesets (e )
316373 if err != nil {
317374 return nil , err
318375 }
@@ -398,7 +455,7 @@ func PromoteCandidateChangeset(
398455 if err != nil {
399456 return cldf.ChangesetOutput {}, fmt .Errorf ("%w: %w" , cldf .ErrInvalidConfig , err )
400457 }
401- state , err := stateview . LoadOnchainState (e )
458+ state , err := loadOnchainStateForCandidateChangesets (e )
402459 if err != nil {
403460 return cldf.ChangesetOutput {}, err
404461 }
@@ -630,7 +687,7 @@ func AddDonAndSetCandidateChangeset(
630687 e cldf.Environment ,
631688 cfg AddDonAndSetCandidateChangesetConfig ,
632689) (cldf.ChangesetOutput , error ) {
633- state , err := stateview . LoadOnchainState (e )
690+ state , err := loadOnchainStateForCandidateChangesets (e )
634691 if err != nil {
635692 return cldf.ChangesetOutput {}, err
636693 }
@@ -763,7 +820,7 @@ func SetCandidateChangeset(
763820 e cldf.Environment ,
764821 cfg SetCandidateChangesetConfig ,
765822) (cldf.ChangesetOutput , error ) {
766- state , err := stateview . LoadOnchainState (e )
823+ state , err := loadOnchainStateForCandidateChangesets (e )
767824 if err != nil {
768825 return cldf.ChangesetOutput {}, err
769826 }
0 commit comments