Skip to content

feat: finalize *tokenless* HubPool CCTP messages #2270

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 23 commits into
base: master
Choose a base branch
from

Conversation

grasphoper
Copy link
Contributor

@grasphoper grasphoper commented May 24, 2025

Current implementation of CCTP finalizer is only tracking DepositForBurn events, and thus is not considering other types of CCTP messages. CCTP can send tokenless messages via MessageTransmitter contract, which emits MessageSent(bytes message) event.

We use HubPool events TokensRelayed and MessageRelayed to find relevant tx hashes to search for such MessageTransmitter MessageSent events in addition to all DepositForBurn events.

Signed-off-by: Ihor Farion <[email protected]>
Signed-off-by: Ihor Farion <[email protected]>
Signed-off-by: Ihor Farion <[email protected]>
@grasphoper grasphoper changed the title feat: finalize *tokenless* HubPool CCTP messages in addition to all DepositForBurn messages feat: finalize *tokenless* HubPool CCTP messages May 24, 2025
@grasphoper grasphoper marked this pull request as ready for review May 28, 2025 16:02
@grasphoper
Copy link
Contributor Author

Tested the following:

  • equality between new code and old code in finding CCTP DepositForBurn events
  • v1 crosschain transfer: send, finalize
  • v1 crosschain message relay: send, finalize
  • v2 crosschain transfer: send, finalize
  • v2 crosschain message relay: send, finalize

@grasphoper grasphoper requested a review from pxrl May 28, 2025 20:21
Comment on lines +347 to +368
let lastMessageSentEventIdx = -1;
receipt.logs.forEach((log, i) => {
if (_isMessageSentEvent(log)) {
if (lastMessageSentEventIdx == -1) {
lastMessageSentEventIdx = i;
} else {
_addCommonMessageEventIfRelevant(receipt.logs[lastMessageSentEventIdx]);
lastMessageSentEventIdx = i;
}
} else {
const depositForBurnVersion = _getDepositForBurnVersion(log);
if (depositForBurnVersion == -1) {
// Skip non-`DepositForBurn` events
return;
}
if (lastMessageSentEventIdx == -1) {
throw new Error(
"DepositForBurn event found without corresponding MessageSent event. " +
"Each DepositForBurn event must have a preceding MessageSent event in the same transaction. " +
`Transaction: ${receipt.transactionHash}, DepositForBurn log index: ${i}`
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you comment on what this loop is doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. Here, we go one by one over a receipts that we "suspect might have CCTP messages for finalization".

It's important to understand what we're looking for in the logs. It's either of 2 things:

  • MessageSent + DepositForBurn == a single USDC transfer from one chain to another (1)
  • solo MessageSent with no DepositForBurn -> a single crosschain message (2)

Then for each receipt we traverse all logs in order, and keep the latest encountered MessageSent index in lastMessageSentEventIdx . Then if we encounter next MessageSent -> the one stored in lastMessageSentEventIdx was (2). If we encounter DepositForBurn, then we bundle stored lastMessageSentEventIdx with it to receive (1)

Copy link
Contributor

Choose a reason for hiding this comment

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

Is it fair to expect that a DepositForBurn event always follows a MessageSent event? If so, I'm wondering if we could first identify all DepositForBurn events and then use their logIndex fields for a subsequent pass over MessageSent events. If a MessageSent event has a logIndex that immediately precedes a DepositForBurn event then it'd be excluded. Could that work? (Motivation for this would be a bit less nesting, at the expense of additional loops - but I think it should be possible to do that in quite compact and concise code).

Also curious about some lines like 351 & 354 - it seems lastMessageSentEventIdx = i is performed unconditionally because it sits on either side of the if statement. Is that intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If a MessageSent event has a logIndex that immediately precedes a DepositForBurn event then it'd be excluded. Could that work?

I'm a bit hesitant to do that beacuse I'm not 100% it's always the "one-before" index. Probably should be the case, but prev. impl. just relied smth like findLast(messageSent, starting_from_depositFrom_idx), so I kept that logic

Copy link
Contributor Author

Choose a reason for hiding this comment

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

lastMessageSentEventIdx = i is performed unconditionally because it sits on either side of the if statement. Is that intended?

It is yeah:

  • if lastMessageSentEventIdx == -1: we don't have any preceding "unmatched"(not a part of MessageSent + DepositForBurn sequence) MessageSent events. We want to "remember" this event to process it later, so set lastMessageSentEventIdx = i
  • else: we do already have a preceding MessageSent event that is "unmatched". This means that event at lastMessageSentEventIdx is a 'solo event' -> tokenless message. We process it by calling _addCommonMessageEventIfRelevant and update our lastMessageSentEventIdx to point at our current MessageSent event instead

Does this make sense?

Signed-off-by: Ihor Farion <[email protected]>
@grasphoper grasphoper requested a review from pxrl May 28, 2025 22:40
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.

2 participants