diff --git a/chains/evm/deployment/go.mod b/chains/evm/deployment/go.mod index 94c947248f..bb76d23450 100644 --- a/chains/evm/deployment/go.mod +++ b/chains/evm/deployment/go.mod @@ -16,7 +16,7 @@ require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/ethereum/go-ethereum v1.16.2 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250320090719-315440f5b0a7 - github.com/smartcontractkit/chainlink-common v0.9.5-0.20250910201107-cb4c31b624d3 + github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76 github.com/smartcontractkit/chainlink-deployments-framework v0.37.1 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3544aab8f3 github.com/smartcontractkit/mcms v0.21.1 @@ -218,8 +218,8 @@ require ( github.com/smartcontractkit/chainlink-aptos v0.0.0-20250414155853-651b4e583ee9 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250805210128-7f8a0f403c3a // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250805210128-7f8a0f403c3a // indirect - github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.1 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250905211734-167560f092c1 // indirect + github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.4 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.12.0 // indirect github.com/smartcontractkit/chainlink-testing-framework/framework v0.10.15 // indirect github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.2 // indirect diff --git a/chains/evm/deployment/go.sum b/chains/evm/deployment/go.sum index 0b217ff585..0d9d969aaa 100644 --- a/chains/evm/deployment/go.sum +++ b/chains/evm/deployment/go.sum @@ -664,16 +664,16 @@ github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250805210128-7 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250805210128-7f8a0f403c3a/go.mod h1:Ve1xD71bl193YIZQEoJMmBqLGQJdNs29bwbuObwvbhQ= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250805210128-7f8a0f403c3a h1:38dAlTPRUQHZus5dCnBnQyf/V4oYn0p2svWlbPgHDQ4= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250805210128-7f8a0f403c3a/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= -github.com/smartcontractkit/chainlink-common v0.9.5-0.20250910201107-cb4c31b624d3 h1:dMvSvJ7nRAQViYSjdfj/vRGLULY7mJ3X4UgsPMmXgOI= -github.com/smartcontractkit/chainlink-common v0.9.5-0.20250910201107-cb4c31b624d3/go.mod h1:1diMLMwfIACeqJFt7ySGaBrJIeUwHTLhVVYlb41EyKk= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.1 h1:ca2z5OXgnbBPQRxpwXwBLJsUA1+cAp5ncfW4Ssvd6eY= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.1/go.mod h1:NZv/qKYGFRnkjOYBouajnDfFoZ+WDa6H2KNmSf1dnKc= +github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76 h1:Slnws8RoXRUYGgEMYK6X2yYzjZwNgVb93PxU45VEObQ= +github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76/go.mod h1:1r3aM96KHAESfnayJ3BTHCkP1qJS1BEG1r4czeoaXlA= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.4 h1:hvqATtrZ0iMRTI80cpBot/3JFbjz2j+2tvpfooVhRHw= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.4/go.mod h1:eKGyfTKzr0/PeR7qKN4l2FcW9p+HzyKUwAfGhm/5YZc= github.com/smartcontractkit/chainlink-deployments-framework v0.37.1 h1:GqqcSz0egZGS6HGk9CnswvONwVAQsP3VWGpLGj8DR7M= github.com/smartcontractkit/chainlink-deployments-framework v0.37.1/go.mod h1:nvsOomMe/u/T4vekY7sd10HEGDbvadEw6bUMEblPDP0= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3544aab8f3 h1:SRMNzCdQnF2x6+QlL5YSzVeWyJb/BXqMrg+zSGaBPVg= github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20250808121824-2c3544aab8f3/go.mod h1:3Lsp38qxen9PABVF+O5eocveQev+hyo9HLAgRodBD4Q= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250905211734-167560f092c1 h1:HZt/80mhcNw6/MlYBIRracxfHWNqFF0iZ5nZEVZBUgo= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250905211734-167560f092c1/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2 h1:1/KdO5AbUr3CmpLjMPuJXPo2wHMbfB8mldKLsg7D4M8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= github.com/smartcontractkit/chainlink-protos/job-distributor v0.12.0 h1:/bhoALRzNXZkdzxBkNM505pMofNy0K0eW1nCzXw+AUI= github.com/smartcontractkit/chainlink-protos/job-distributor v0.12.0/go.mod h1:/dVVLXrsp+V0AbcYGJo3XMzKg3CkELsweA/TTopCsKE= github.com/smartcontractkit/chainlink-testing-framework/framework v0.10.15 h1:OyX2Z68z6VDY4aadqMXjwSTE/0misA5fk8Iq710nxkk= diff --git a/execute/factory.go b/execute/factory.go index 3c89e19135..5f6703e48f 100644 --- a/execute/factory.go +++ b/execute/factory.go @@ -159,6 +159,7 @@ func (p PluginFactory) NewReportingPlugin( p.ocrConfig.Config.ChainSelector, offchainConfig.TokenDataObservers, p.tokenDataEncoder, + p.chainAccessors, p.extendedReaders, p.addrCodec, ) diff --git a/execute/test_utils.go b/execute/test_utils.go index 36a605164d..1675c5eac6 100644 --- a/execute/test_utils.go +++ b/execute/test_utils.go @@ -266,6 +266,7 @@ func (it *IntTest) Start() *testhelpers.OCR3Runner[[]byte] { }) } + emptyChainAccessors := make(map[cciptypes.ChainSelector]cciptypes.ChainAccessor) homeChain := setupHomeChainPoller(it.t, it.lggr, chainConfigInfos) ctx := it.t.Context() err := homeChain.Start(ctx) @@ -277,6 +278,7 @@ func (it *IntTest) Start() *testhelpers.OCR3Runner[[]byte] { it.dstSelector, it.tokenObserverConfig, testhelpers.TokenDataEncoderInstance, + emptyChainAccessors, it.tokenChainReader, mockAddrCodec, ) diff --git a/execute/tokendata/observer/observer.go b/execute/tokendata/observer/observer.go index 7ebefde0de..cec883949f 100644 --- a/execute/tokendata/observer/observer.go +++ b/execute/tokendata/observer/observer.go @@ -58,6 +58,7 @@ func NewConfigBasedCompositeObservers( destChainSelector cciptypes.ChainSelector, config []pluginconfig.TokenDataObserverConfig, encoder cciptypes.TokenDataEncoder, + chainAccessors map[cciptypes.ChainSelector]cciptypes.ChainAccessor, readers map[cciptypes.ChainSelector]contractreader.Extended, addrCodec cciptypes.AddressCodec, ) (TokenDataObserver, error) { @@ -67,9 +68,10 @@ func NewConfigBasedCompositeObservers( // e.g. observers[i] := config.CreateTokenDataObserver() switch { case c.USDCCCTPObserverConfig != nil: - observer, err := usdc.NewUSDCTokenDataObserver(ctx, lggr, destChainSelector, + observer, err := usdc.NewUSDCTokenDataObserver( + ctx, lggr, destChainSelector, *c.USDCCCTPObserverConfig, - encoder.EncodeUSDC, readers, addrCodec) + encoder.EncodeUSDC, chainAccessors, readers, addrCodec) if err != nil { return nil, fmt.Errorf("create USDC/CCTP token observer: %w", err) } diff --git a/execute/tokendata/observer/observer_test.go b/execute/tokendata/observer/observer_test.go index 372382fbf7..f8851c5a9d 100644 --- a/execute/tokendata/observer/observer_test.go +++ b/execute/tokendata/observer/observer_test.go @@ -27,6 +27,7 @@ func Test_CompositeTokenDataObserver_EmptyObservers(t *testing.T) { []pluginconfig.TokenDataObserverConfig{}, nil, nil, + nil, mockAddrCodec, ) require.NoError(t, err) diff --git a/execute/tokendata/usdc/usdc.go b/execute/tokendata/usdc/usdc.go index 3a83958c58..ffe5cef1fe 100644 --- a/execute/tokendata/usdc/usdc.go +++ b/execute/tokendata/usdc/usdc.go @@ -35,6 +35,7 @@ func NewUSDCTokenDataObserver( destChainSelector cciptypes.ChainSelector, usdcConfig pluginconfig.USDCCCTPObserverConfig, attestationEncoder AttestationEncoder, + chainAccessors map[cciptypes.ChainSelector]cciptypes.ChainAccessor, readers map[cciptypes.ChainSelector]contractreader.Extended, addrCodec cciptypes.AddressCodec, ) (*USDCTokenDataObserver, error) { @@ -43,6 +44,7 @@ func NewUSDCTokenDataObserver( ctx, lggr, usdcConfig.Tokens, + chainAccessors, readers, addrCodec, ) diff --git a/execute/tokendata/usdc/usdc_int_test.go b/execute/tokendata/usdc/usdc_int_test.go index fe1541294f..21edc4affe 100644 --- a/execute/tokendata/usdc/usdc_int_test.go +++ b/execute/tokendata/usdc/usdc_int_test.go @@ -247,6 +247,7 @@ func Test_USDC_CCTP_Flow(t *testing.T) { }, } + emptyChainAccessors := make(map[cciptypes.ChainSelector]cciptypes.ChainAccessor) mockAddrCodec := internal.NewMockAddressCodecHex(t) tkReader, err := usdc.NewUSDCTokenDataObserver( t.Context(), @@ -254,6 +255,7 @@ func Test_USDC_CCTP_Flow(t *testing.T) { baseChain, config, testhelpers.USDCEncoder, + emptyChainAccessors, map[cciptypes.ChainSelector]contractreader.Extended{ fujiChain: mockReader(t, fuji), sepoliaChain: mockReader(t, sepolia), diff --git a/go.mod b/go.mod index 113aad2c34..397f225dcf 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/prometheus/client_golang v1.22.0 github.com/prometheus/client_model v0.6.2 github.com/smartcontractkit/chain-selectors v1.0.67 - github.com/smartcontractkit/chainlink-common v0.9.5-0.20250910201107-cb4c31b624d3 + github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76 github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 github.com/smartcontractkit/libocr v0.0.0-20250707144819-babe0ec4e358 github.com/stretchr/testify v1.10.0 @@ -82,8 +82,8 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.1 // indirect - github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250905211734-167560f092c1 // indirect + github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.4 // indirect + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2 // indirect github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/supranational/blst v0.3.14 // indirect diff --git a/go.sum b/go.sum index b05870b8ee..cc1b739fc8 100644 --- a/go.sum +++ b/go.sum @@ -263,12 +263,12 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/smartcontractkit/chain-selectors v1.0.67 h1:gxTqP/JC40KDe3DE1SIsIKSTKTZEPyEU1YufO1admnw= github.com/smartcontractkit/chain-selectors v1.0.67/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= -github.com/smartcontractkit/chainlink-common v0.9.5-0.20250910201107-cb4c31b624d3 h1:dMvSvJ7nRAQViYSjdfj/vRGLULY7mJ3X4UgsPMmXgOI= -github.com/smartcontractkit/chainlink-common v0.9.5-0.20250910201107-cb4c31b624d3/go.mod h1:1diMLMwfIACeqJFt7ySGaBrJIeUwHTLhVVYlb41EyKk= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.1 h1:ca2z5OXgnbBPQRxpwXwBLJsUA1+cAp5ncfW4Ssvd6eY= -github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.1/go.mod h1:NZv/qKYGFRnkjOYBouajnDfFoZ+WDa6H2KNmSf1dnKc= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250905211734-167560f092c1 h1:HZt/80mhcNw6/MlYBIRracxfHWNqFF0iZ5nZEVZBUgo= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250905211734-167560f092c1/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76 h1:Slnws8RoXRUYGgEMYK6X2yYzjZwNgVb93PxU45VEObQ= +github.com/smartcontractkit/chainlink-common v0.9.6-0.20250929154511-1f5fbda7ae76/go.mod h1:1r3aM96KHAESfnayJ3BTHCkP1qJS1BEG1r4czeoaXlA= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.4 h1:hvqATtrZ0iMRTI80cpBot/3JFbjz2j+2tvpfooVhRHw= +github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.4/go.mod h1:eKGyfTKzr0/PeR7qKN4l2FcW9p+HzyKUwAfGhm/5YZc= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2 h1:1/KdO5AbUr3CmpLjMPuJXPo2wHMbfB8mldKLsg7D4M8= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250911124514-5874cc6d62b2/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6 h1:L6KJ4kGv/yNNoCk8affk7Y1vAY0qglPMXC/hevV/IsA= github.com/smartcontractkit/chainlink-protos/rmn/v1.6/go v0.0.0-20250131130834-15e0d4cde2a6/go.mod h1:FRwzI3hGj4CJclNS733gfcffmqQ62ONCkbGi49s658w= github.com/smartcontractkit/libocr v0.0.0-20250707144819-babe0ec4e358 h1:+NVzR5LZVazRUunzVn34u+lwnpmn6NTVPCeZOVyQHLo= diff --git a/mocks/chainlink_common/ccipocr3/chain_accessor.go b/mocks/chainlink_common/ccipocr3/chain_accessor.go index f11508057f..dc78c4d70a 100644 --- a/mocks/chainlink_common/ccipocr3/chain_accessor.go +++ b/mocks/chainlink_common/ccipocr3/chain_accessor.go @@ -502,9 +502,9 @@ func (_c *MockChainAccessor_GetFeeQuoterDestChainConfig_Call) RunAndReturn(run f return _c } -// GetFeeQuoterTokenUpdates provides a mock function with given fields: ctx, tokens, chain -func (_m *MockChainAccessor) GetFeeQuoterTokenUpdates(ctx context.Context, tokens []ccipocr3.UnknownEncodedAddress, chain ccipocr3.ChainSelector) (map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig, error) { - ret := _m.Called(ctx, tokens, chain) +// GetFeeQuoterTokenUpdates provides a mock function with given fields: ctx, tokensBytes +func (_m *MockChainAccessor) GetFeeQuoterTokenUpdates(ctx context.Context, tokensBytes []ccipocr3.UnknownAddress) (map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig, error) { + ret := _m.Called(ctx, tokensBytes) if len(ret) == 0 { panic("no return value specified for GetFeeQuoterTokenUpdates") @@ -512,19 +512,19 @@ func (_m *MockChainAccessor) GetFeeQuoterTokenUpdates(ctx context.Context, token var r0 map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []ccipocr3.UnknownEncodedAddress, ccipocr3.ChainSelector) (map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig, error)); ok { - return rf(ctx, tokens, chain) + if rf, ok := ret.Get(0).(func(context.Context, []ccipocr3.UnknownAddress) (map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig, error)); ok { + return rf(ctx, tokensBytes) } - if rf, ok := ret.Get(0).(func(context.Context, []ccipocr3.UnknownEncodedAddress, ccipocr3.ChainSelector) map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig); ok { - r0 = rf(ctx, tokens, chain) + if rf, ok := ret.Get(0).(func(context.Context, []ccipocr3.UnknownAddress) map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig); ok { + r0 = rf(ctx, tokensBytes) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig) } } - if rf, ok := ret.Get(1).(func(context.Context, []ccipocr3.UnknownEncodedAddress, ccipocr3.ChainSelector) error); ok { - r1 = rf(ctx, tokens, chain) + if rf, ok := ret.Get(1).(func(context.Context, []ccipocr3.UnknownAddress) error); ok { + r1 = rf(ctx, tokensBytes) } else { r1 = ret.Error(1) } @@ -539,15 +539,14 @@ type MockChainAccessor_GetFeeQuoterTokenUpdates_Call struct { // GetFeeQuoterTokenUpdates is a helper method to define mock.On call // - ctx context.Context -// - tokens []ccipocr3.UnknownEncodedAddress -// - chain ccipocr3.ChainSelector -func (_e *MockChainAccessor_Expecter) GetFeeQuoterTokenUpdates(ctx interface{}, tokens interface{}, chain interface{}) *MockChainAccessor_GetFeeQuoterTokenUpdates_Call { - return &MockChainAccessor_GetFeeQuoterTokenUpdates_Call{Call: _e.mock.On("GetFeeQuoterTokenUpdates", ctx, tokens, chain)} +// - tokensBytes []ccipocr3.UnknownAddress +func (_e *MockChainAccessor_Expecter) GetFeeQuoterTokenUpdates(ctx interface{}, tokensBytes interface{}) *MockChainAccessor_GetFeeQuoterTokenUpdates_Call { + return &MockChainAccessor_GetFeeQuoterTokenUpdates_Call{Call: _e.mock.On("GetFeeQuoterTokenUpdates", ctx, tokensBytes)} } -func (_c *MockChainAccessor_GetFeeQuoterTokenUpdates_Call) Run(run func(ctx context.Context, tokens []ccipocr3.UnknownEncodedAddress, chain ccipocr3.ChainSelector)) *MockChainAccessor_GetFeeQuoterTokenUpdates_Call { +func (_c *MockChainAccessor_GetFeeQuoterTokenUpdates_Call) Run(run func(ctx context.Context, tokensBytes []ccipocr3.UnknownAddress)) *MockChainAccessor_GetFeeQuoterTokenUpdates_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]ccipocr3.UnknownEncodedAddress), args[2].(ccipocr3.ChainSelector)) + run(args[0].(context.Context), args[1].([]ccipocr3.UnknownAddress)) }) return _c } @@ -557,7 +556,7 @@ func (_c *MockChainAccessor_GetFeeQuoterTokenUpdates_Call) Return(_a0 map[ccipoc return _c } -func (_c *MockChainAccessor_GetFeeQuoterTokenUpdates_Call) RunAndReturn(run func(context.Context, []ccipocr3.UnknownEncodedAddress, ccipocr3.ChainSelector) (map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig, error)) *MockChainAccessor_GetFeeQuoterTokenUpdates_Call { +func (_c *MockChainAccessor_GetFeeQuoterTokenUpdates_Call) RunAndReturn(run func(context.Context, []ccipocr3.UnknownAddress) (map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig, error)) *MockChainAccessor_GetFeeQuoterTokenUpdates_Call { _c.Call.Return(run) return _c } diff --git a/pkg/chainaccessor/default_price_reader.go b/pkg/chainaccessor/default_price_reader.go index 39b966c430..a80379a9c9 100644 --- a/pkg/chainaccessor/default_price_reader.go +++ b/pkg/chainaccessor/default_price_reader.go @@ -107,33 +107,21 @@ func normalizePrice(price *big.Int, decimals uint8) *big.Int { func (l *DefaultAccessor) GetFeeQuoterTokenUpdates( ctx context.Context, - tokens []ccipocr3.UnknownEncodedAddress, - chain ccipocr3.ChainSelector, + tokenBytes []ccipocr3.UnknownAddress, ) (map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig, error) { lggr := logutil.WithContextValues(ctx, l.lggr) - byteTokens := make([][]byte, 0, len(tokens)) - for _, token := range tokens { - tokenAddressBytes, err := l.addrCodec.AddressStringToBytes(string(token), chain) - if err != nil { - lggr.Warnw("failed to convert token address to bytes", "token", token, "err", err) - continue - } - - byteTokens = append(byteTokens, tokenAddressBytes) - } - feeQuoterAddress, err := l.GetContractAddress(consts.ContractNameFeeQuoter) if err != nil { return nil, fmt.Errorf("failed to get fee quoter address: %w", err) } - feeQuoterAddressStr, err := l.addrCodec.AddressBytesToString(feeQuoterAddress[:], chain) + feeQuoterAddressStr, err := l.addrCodec.AddressBytesToString(feeQuoterAddress[:], l.chainSelector) if err != nil { - lggr.Warnw("failed to convert fee quoter address to string", "chain", chain, "err", err) + lggr.Warnw("failed to convert fee quoter address to string", "chain", l.chainSelector, "err", err) return make(map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig), nil } - updates := make([]ccipocr3.TimestampedUnixBig, len(tokens)) + updates := make([]ccipocr3.TimestampedUnixBig, len(tokenBytes)) boundContract := types.BoundContract{ Address: feeQuoterAddressStr, Name: consts.ContractNameFeeQuoter, @@ -147,7 +135,7 @@ func (l *DefaultAccessor) GetFeeQuoterTokenUpdates( boundContract.ReadIdentifier(consts.MethodNameFeeQuoterGetTokenPrices), primitives.Unconfirmed, map[string]any{ - "tokens": byteTokens, + "tokens": tokenBytes, }, &updates, ); err != nil { @@ -155,16 +143,19 @@ func (l *DefaultAccessor) GetFeeQuoterTokenUpdates( } updateMap := make(map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedUnixBig) - for i, token := range tokens { + for i, token := range tokenBytes { + tokenAddressStr, err := l.addrCodec.AddressBytesToString(token[:], l.chainSelector) + if err != nil { + lggr.Errorw("failed to convert token address to string", "token", token, "chain", l.chainSelector, "err", err) + continue + } + // token not available on fee quoter if updates[i].Timestamp == 0 || updates[i].Value == nil || updates[i].Value.Cmp(big.NewInt(0)) == 0 { - lggr.Debugw("empty fee quoter update found", - "chain", chain, - "token", token, - ) + lggr.Debugw("empty fee quoter update found", "chain", l.chainSelector, "token", tokenAddressStr) continue } - updateMap[token] = updates[i] + updateMap[ccipocr3.UnknownEncodedAddress(tokenAddressStr)] = updates[i] } return updateMap, nil diff --git a/pkg/reader/price_reader.go b/pkg/reader/price_reader.go index f2765a8f30..fe08dd31be 100644 --- a/pkg/reader/price_reader.go +++ b/pkg/reader/price_reader.go @@ -61,6 +61,17 @@ func (pr *priceReader) GetFeeQuoterTokenUpdates( ) (map[ccipocr3.UnknownEncodedAddress]ccipocr3.TimestampedBig, error) { lggr := logutil.WithContextValues(ctx, pr.lggr) + tokensBytes := make([]ccipocr3.UnknownAddress, 0, len(tokens)) + for _, token := range tokens { + tokenAddressBytes, err := pr.addressCodec.AddressStringToBytes(string(token), chain) + if err != nil { + lggr.Warnw("failed to convert token address to bytes", "token", token, "err", err) + continue + } + + tokensBytes = append(tokensBytes, tokenAddressBytes) + } + accessor, err := getChainAccessor(pr.chainAccessors, chain) if err != nil { // Don't return an error if the chain accessor is not found, just log warning and return nil @@ -68,7 +79,7 @@ func (pr *priceReader) GetFeeQuoterTokenUpdates( return nil, nil } - updates, err := accessor.GetFeeQuoterTokenUpdates(ctx, tokens, chain) + updates, err := accessor.GetFeeQuoterTokenUpdates(ctx, tokensBytes) if err != nil { return nil, fmt.Errorf("failed to get fee quoter token updates: %w", err) } diff --git a/pkg/reader/tokens.go b/pkg/reader/tokens.go index 2aa6a6681d..cd0981a539 100644 --- a/pkg/reader/tokens.go +++ b/pkg/reader/tokens.go @@ -1,25 +1,13 @@ package reader import ( - "fmt" - "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" ) // TODO: This is a temporary solution to avoid circular dependencies. There should be a better way to do this. -// MessageTokenID is a unique identifier for a message token data (per chain selector). It's a composite key of -// the message sequence number and the token index within the message. It's used to easier identify token data for -// messages without having to deal with nested maps. -type MessageTokenID struct { - SeqNr ccipocr3.SeqNum - Index int -} +type MessageTokenID = ccipocr3.MessageTokenID func NewMessageTokenID(seqNr ccipocr3.SeqNum, index int) MessageTokenID { - return MessageTokenID{SeqNr: seqNr, Index: index} -} - -func (mti MessageTokenID) String() string { - return fmt.Sprintf("%d_%d", mti.SeqNr, mti.Index) + return ccipocr3.NewMessageTokenID(seqNr, index) } diff --git a/pkg/reader/usdc_reader.go b/pkg/reader/usdc_reader.go index 79b910c2f3..bb9aff47a3 100644 --- a/pkg/reader/usdc_reader.go +++ b/pkg/reader/usdc_reader.go @@ -6,6 +6,7 @@ import ( "fmt" sel "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -75,6 +76,7 @@ func NewUSDCMessageReader( ctx context.Context, lggr logger.Logger, tokensConfig map[cciptypes.ChainSelector]pluginconfig.USDCCCTPTokenConfig, + chainAccessors map[cciptypes.ChainSelector]cciptypes.ChainAccessor, contractReaders map[cciptypes.ChainSelector]contractreader.Extended, addrCodec cciptypes.AddressCodec, ) (USDCMessageReader, error) { @@ -122,24 +124,20 @@ func NewUSDCMessageReader( return nil, err } - // Bind the 3rd party MessageTransmitter contract, this is where CCTP MessageSent events are emitted. - _, err = bindReaderContract( + // TODO: feature flag usdcReader via CR or ChainAccessor using NewSolanaUSDCReaderAccessor() + sr, err := NewSolanaUSDCReader( ctx, lggr, contractReaders, + addrCodec, chainSelector, - consts.ContractNameUSDCTokenPool, bytesAddress, - addrCodec, ) if err != nil { return nil, err } - readers[chainSelector] = solanaUSDCMessageReader{ - lggr: lggr, - contractReader: contractReaders[chainSelector], - } + readers[chainSelector] = sr default: return nil, fmt.Errorf("unsupported chain selector family %s for chain %d", family, chainSelector) } @@ -151,8 +149,6 @@ func NewUSDCMessageReader( }, nil } -// Deprecated -// TODO(NONEVM-1865): Remove once the chainAccessor is passed down here from the factory. Then use accessor.Sync(). func bindReaderContract[T contractreader.ContractReaderFacade]( ctx context.Context, lggr logger.Logger, diff --git a/pkg/reader/usdc_reader_solana.go b/pkg/reader/usdc_reader_solana.go index b4668f8df0..ac2019f40d 100644 --- a/pkg/reader/usdc_reader_solana.go +++ b/pkg/reader/usdc_reader_solana.go @@ -19,6 +19,34 @@ type solanaUSDCMessageReader struct { contractReader contractreader.Extended } +func NewSolanaUSDCReader( + ctx context.Context, + lggr logger.Logger, + contractReaders map[cciptypes.ChainSelector]contractreader.Extended, + addrCodec cciptypes.AddressCodec, + chainSelector cciptypes.ChainSelector, + bytesAddress cciptypes.UnknownAddress, +) (USDCMessageReader, error) { + // Bind the 3rd party MessageTransmitter contract, this is where CCTP MessageSent events are emitted. + _, err := bindReaderContract( + ctx, + lggr, + contractReaders, + chainSelector, + consts.ContractNameUSDCTokenPool, + bytesAddress, + addrCodec, + ) + if err != nil { + return nil, err + } + + return solanaUSDCMessageReader{ + lggr: lggr, + contractReader: contractReaders[chainSelector], + }, nil +} + // getMessageTokenData extracts token data from the CCTP MessageSent event. func (u solanaUSDCMessageReader) getMessageTokenData( tokens map[MessageTokenID]cciptypes.RampTokenAmount, diff --git a/pkg/reader/usdc_reader_solana_accessor.go b/pkg/reader/usdc_reader_solana_accessor.go new file mode 100644 index 0000000000..ca3044945d --- /dev/null +++ b/pkg/reader/usdc_reader_solana_accessor.go @@ -0,0 +1,72 @@ +package reader + +import ( + "context" + "fmt" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" + + "github.com/smartcontractkit/chainlink-ccip/pkg/consts" +) + +// solanaUSDCMessageReaderAccessor is a USDCReader for Solana that delegates calls to the Solana ChainAccessor +// to fetch onchain information. +type solanaUSDCMessageReaderAccessor struct { + lggr logger.Logger + chainAccessor cciptypes.ChainAccessor +} + +func NewSolanaUSDCReaderAccessor( + ctx context.Context, + lggr logger.Logger, + chainAccessors map[cciptypes.ChainSelector]cciptypes.ChainAccessor, + addrCodec cciptypes.AddressCodec, + chainSelector cciptypes.ChainSelector, + bytesAddress cciptypes.UnknownAddress, +) (USDCMessageReader, error) { + accessor, err := getChainAccessor(chainAccessors, chainSelector) + if err != nil { + return nil, fmt.Errorf("get chain accessor for Solana USDCReader: %w", err) + } + + addressStr, err := addrCodec.AddressBytesToString(bytesAddress, chainSelector) + if err != nil { + return nil, fmt.Errorf("unable to convert address bytes to string: %w, address: %v", err, bytesAddress) + } + + lggr.Debugw("Syncing contract to accessor", + "chainSelector", chainSelector, + "contractName", consts.ContractNameUSDCTokenPool, + "address", addressStr, + ) + + err = accessor.Sync(ctx, consts.ContractNameUSDCTokenPool, bytesAddress) + if err != nil { + return nil, fmt.Errorf("sync contract to Solana USDC accessor: %w", err) + } + + return solanaUSDCMessageReaderAccessor{ + lggr: lggr, + chainAccessor: accessor, + }, nil +} + +// MessagesByTokenID retrieves the CCTP MessageSent events. +func (u solanaUSDCMessageReaderAccessor) MessagesByTokenID( + ctx context.Context, + source, dest cciptypes.ChainSelector, + tokens map[cciptypes.MessageTokenID]cciptypes.RampTokenAmount, +) (map[cciptypes.MessageTokenID]cciptypes.Bytes, error) { + if len(tokens) == 0 { + return map[cciptypes.MessageTokenID]cciptypes.Bytes{}, nil + } + + u.lggr.Debugw("Searching for Solana CCTP USDC logs", "numExpected", len(tokens)) + messagesByID, err := u.chainAccessor.MessagesByTokenID(ctx, source, dest, tokens) + if err != nil { + return nil, fmt.Errorf("error fetching messages by token ID from chain accessor for chain %d: %w", source, err) + } + + return messagesByID, nil +} diff --git a/pkg/reader/usdc_reader_test.go b/pkg/reader/usdc_reader_test.go index cf18c6db11..8464f4d334 100644 --- a/pkg/reader/usdc_reader_test.go +++ b/pkg/reader/usdc_reader_test.go @@ -113,7 +113,8 @@ func Test_USDCMessageReader_New(t *testing.T) { readers[k] = v } - r, err := NewUSDCMessageReader(ctx, logger.Test(t), tc.tokensConfig, readers, mockAddrCodec) + emptyChainAccessors := make(map[cciptypes.ChainSelector]cciptypes.ChainAccessor) + r, err := NewUSDCMessageReader(ctx, logger.Test(t), tc.tokensConfig, emptyChainAccessors, readers, mockAddrCodec) if tc.errorMessage != "" { require.Error(t, err) require.Contains(t, err.Error(), tc.errorMessage) @@ -195,7 +196,9 @@ func Test_USDCMessageReader_MessagesByTokenID(t *testing.T) { } mockAddrCodec := internal.NewMockAddressCodecHex(t) - usdcReader, err := NewUSDCMessageReader(ctx, logger.Test(t), tokensConfigs, contactReaders, mockAddrCodec) + emptyChainAccessors := make(map[cciptypes.ChainSelector]cciptypes.ChainAccessor) + usdcReader, err := NewUSDCMessageReader(ctx, logger.Test(t), tokensConfigs, + emptyChainAccessors, contactReaders, mockAddrCodec) require.NoError(t, err) tt := []struct {