@@ -3,12 +3,24 @@ pragma solidity ^0.8.13;
33
44import {IMessageTransmitter} from "../interfaces/cctp/IMessageTransmitter.sol " ;
55import {IMessageTransmitterV2} from "../interfaces/cctp/IMessageTransmitterV2.sol " ;
6+ import {IMessageHandler} from "../interfaces/cctp/IMessageHandler.sol " ;
7+ import {IMessageHandlerV2} from "../interfaces/cctp/IMessageHandlerV2.sol " ;
68import {MockToken} from "./MockToken.sol " ;
9+ import {TypedMemView} from "../libs/TypedMemView.sol " ;
10+ import {CctpMessageV1} from "../libs/CctpMessageV1.sol " ;
11+ import {CctpMessageV2} from "../libs/CctpMessageV2.sol " ;
12+ import {TypeCasts} from "../libs/TypeCasts.sol " ;
713
814contract MockCircleMessageTransmitter is
915 IMessageTransmitter ,
1016 IMessageTransmitterV2
1117{
18+ using TypedMemView for bytes ;
19+ using TypedMemView for bytes29 ;
20+ using CctpMessageV1 for bytes29 ;
21+ using CctpMessageV2 for bytes29 ;
22+ using TypeCasts for address ;
23+
1224 mapping (bytes32 => bool ) processedNonces;
1325 MockToken token;
1426 uint32 public version;
@@ -26,10 +38,66 @@ contract MockCircleMessageTransmitter is
2638 }
2739
2840 function receiveMessage (
29- bytes memory ,
41+ bytes memory message ,
3042 bytes calldata
31- ) external pure returns (bool success ) {
32- success = true ;
43+ ) external returns (bool success ) {
44+ bytes29 cctpMessage = TypedMemView.ref (message, 0 );
45+
46+ // Extract nonce and source domain to check if message was already processed
47+ uint32 sourceDomain;
48+ bytes32 nonceId;
49+ if (version == 0 ) {
50+ sourceDomain = cctpMessage._sourceDomain ();
51+ uint64 nonce = cctpMessage._nonce ();
52+ nonceId = hashSourceAndNonce (sourceDomain, nonce);
53+ } else {
54+ sourceDomain = cctpMessage._getSourceDomain ();
55+ bytes32 nonce = cctpMessage._getNonce ();
56+ // For V2, use the nonce directly as the nonceId (it's already a bytes32)
57+ nonceId = keccak256 (abi.encodePacked (sourceDomain, nonce));
58+ }
59+
60+ require (! processedNonces[nonceId], "Message already processed " );
61+ processedNonces[nonceId] = true ;
62+
63+ // Extract recipient based on version
64+ address recipient;
65+ bytes32 sender;
66+ bytes memory messageBody;
67+
68+ if (version == 0 ) {
69+ // V1
70+ recipient = _bytes32ToAddress (cctpMessage._recipient ());
71+ sender = cctpMessage._sender ();
72+ messageBody = cctpMessage._messageBody ().clone ();
73+ } else {
74+ // V2
75+ recipient = _bytes32ToAddress (cctpMessage._getRecipient ());
76+ sender = cctpMessage._getSender ();
77+ messageBody = cctpMessage._getMessageBody ().clone ();
78+ }
79+
80+ if (version == 0 ) {
81+ // V1: Call handleReceiveMessage
82+ success = IMessageHandler (recipient).handleReceiveMessage (
83+ sourceDomain,
84+ sender,
85+ messageBody
86+ );
87+ } else {
88+ // V2: Call handleReceiveUnfinalizedMessage
89+ success = IMessageHandlerV2 (recipient)
90+ .handleReceiveUnfinalizedMessage (
91+ sourceDomain,
92+ sender,
93+ 1000 , // mock finality threshold
94+ messageBody
95+ );
96+ }
97+ }
98+
99+ function _bytes32ToAddress (bytes32 _buf ) internal pure returns (address ) {
100+ return address (uint160 (uint256 (_buf)));
33101 }
34102
35103 function hashSourceAndNonce (
@@ -70,11 +138,36 @@ contract MockCircleMessageTransmitter is
70138 }
71139
72140 function sendMessage (
73- uint32 ,
74- bytes32 ,
75- bytes calldata message
141+ uint32 destinationDomain ,
142+ bytes32 recipient ,
143+ bytes calldata messageBody
76144 ) public returns (uint64 ) {
77- emit MessageSent (message);
145+ // Format a complete CCTP message for the event based on version
146+ bytes memory cctpMessage;
147+ if (version == 0 ) {
148+ cctpMessage = CctpMessageV1._formatMessage (
149+ version,
150+ 0 , // sourceDomain (mock localDomain returns 0)
151+ destinationDomain,
152+ 0 , // nonce
153+ address (this ).addressToBytes32 (),
154+ recipient,
155+ bytes32 (0 ), // destinationCaller (anyone can relay)
156+ messageBody
157+ );
158+ } else {
159+ cctpMessage = CctpMessageV2._formatMessageForRelay (
160+ version,
161+ 0 , // sourceDomain (mock localDomain returns 0)
162+ destinationDomain,
163+ address (this ).addressToBytes32 (),
164+ recipient,
165+ bytes32 (0 ), // destinationCaller (anyone can relay)
166+ 1000 , // mock finality threshold
167+ messageBody
168+ );
169+ }
170+ emit MessageSent (cctpMessage);
78171 return 0 ;
79172 }
80173
@@ -90,10 +183,21 @@ contract MockCircleMessageTransmitter is
90183 function sendMessage (
91184 uint32 destinationDomain ,
92185 bytes32 recipient ,
93- bytes32 ,
94- uint32 ,
186+ bytes32 destinationCaller ,
187+ uint32 minFinalityThreshold ,
95188 bytes calldata messageBody
96189 ) external {
97- sendMessage (destinationDomain, recipient, messageBody);
190+ // V2 sendMessage: format a complete CCTP V2 message
191+ bytes memory cctpMessage = CctpMessageV2._formatMessageForRelay (
192+ version,
193+ 0 , // sourceDomain (mock localDomain returns 0)
194+ destinationDomain,
195+ address (this ).addressToBytes32 (),
196+ recipient,
197+ destinationCaller,
198+ minFinalityThreshold,
199+ messageBody
200+ );
201+ emit MessageSent (cctpMessage);
98202 }
99203}
0 commit comments