Skip to content

Solana USDC Reader #1022

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open

Solana USDC Reader #1022

wants to merge 34 commits into from

Conversation

winder
Copy link
Contributor

@winder winder commented Jun 16, 2025

No description provided.

@winder winder changed the title WIP - Solana USDC Reader Solana USDC Reader Jun 17, 2025
@winder winder force-pushed the will/solana-usdc-reader branch from f6c2bda to 24926a3 Compare June 24, 2025 12:37
@winder winder marked this pull request as ready for review July 7, 2025 19:51
@winder winder requested a review from a team as a code owner July 7, 2025 19:51
@winder winder requested a review from asoliman92 July 7, 2025 19:51
keyFilter, err := query.Where(
// Using same as EVM for consistency. Only used by off-chain components so name does not have to align with on-chain
consts.EventNameCCTPMessageSent,
expressions...,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic could break if we get multiple CCTP data. The default operator in the LogPoller is AND so stringing expressions together like this would make a query like nonce=1 && domain=5 && nonce=2 && domain=5. I think we probably need to use the Or comparator here

Suggested change
expressions...,
query.Or(expressions...),

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh and we should probably move the query.Confidence(primitives.Finalized) from above here so it doesn't get mixed up the with the nonce/domain queries

Suggested change
expressions...,
query.Confidence(primitives.Finalized),
expressions...,

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unresolving this since I noticed the query.Confidence(primitives.Finalized) expression wasn't moved down into the where clause

Copy link
Contributor

@mateusz-sekara mateusz-sekara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to have some e2e tests to cover that, especially to cover the full flow with CR/LogPoller for Solana


// Query the token pool contract for the MessageSent event data.
expressions := []query.Expression{query.Confidence(primitives.Finalized)}
for _, data := range cctpData {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would definitely require perf tests using database like the ones we have for evm usdc reader
https://github.com/smartcontractkit/chainlink/blob/develop/core/capabilities/ccip/ccip_integration_tests/usdcreader/usdcreader_test.go

Copy link

Metric will/solana-usdc-reader main
Coverage 69.7% 69.7%

@@ -127,6 +127,13 @@ func (p *Plugin) Observation(
"numCommitReports", numCommitReports,
"numMessages", observation.Messages.Count())

lggr.Debugw("[verbose] execute plugin got observation",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to keep it? There might be non-zero overhead to json serialization of observation with every round.

@@ -128,12 +128,16 @@ func (s *USDCAttestationClient) fetchSingleMessage(
messageHash := cciptypes.Bytes32(s.hasher.Hash(message))
body, _, err := s.client.Get(ctx, fmt.Sprintf("%s/%s/%s", apiVersion, attestationPath, messageHash.String()))
if err != nil {
s.lggr.Warnw("Failed to fetch attestation from the API", "err", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I think it might be an error level

return tokendata.ErrorAttestationStatus(err)
}
response, err := attestationFromResponse(body)
if err != nil {
s.lggr.Warnw("Failed to parse attestation from the response", "err", err, "body", body)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -20,6 +18,9 @@ import (
"github.com/smartcontractkit/chainlink-ccip/pluginconfig"
)

// USDCMessageReader retrieves each of the CCTPv1 MessageSent event created
// during the Commit phase of the USDC token transfer. The events are created
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, but MessageSent events are created when a message is sent via Router - as a part of that transaction OnRamp interacts with USDCTokenPool, which interacts with Circle contracts. Technically, it's before Commit (assuming Sol works similar to EVM)

}
case sel.FamilySolana:
// Bind the TokenPool contract, the contract re-emits the USDC MessageSent event along with other metadata.
bytesAddress, err := addrCodec.AddressStringToBytes(token.SourceMessageTransmitterAddr, chainSelector)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it's set for both families, but we will need to have time retention set for these events just to make sure we don't store them forever

https://github.com/smartcontractkit/chainlink/blob/e6d7c15c29751ccd83e3ffaddce420471fd9bbe4/core/capabilities/ccip/configs/evm/contract_reader.go#L406

keyFilter, err := query.Where(
// Using same as EVM for consistency. Only used by off-chain components so name does not have to align with on-chain
consts.EventNameCCTPMessageSent,
query.Confidence(primitives.Finalized),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, not sure how adding finalization check impacts db query performance for Solana, but it might be redundant. By the time we reach this place message is already committed so it's already finalized - at least this is how it works for EVM

@@ -212,6 +230,12 @@ func AllAvailableDomains() map[uint64]uint32 {
return destDomains
}

type evmUSDCMessageReader struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor, but if you extracted Solana to a separate file then I would extract evm implementation to usd_reader_evm.go and keep only the factory/interfaces in usdc_reader.go

@@ -0,0 +1,157 @@
package reader
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any tests for Solana? Or would that be covered/tested in Chainlink repository with e2e tests?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants