@@ -40,7 +40,6 @@ contract TokenBridgeCctpV1Test is Test {
4040 uint32 internal constant CCTP_VERSION_1 = 0 ;
4141 uint32 internal constant CCTP_VERSION_2 = 1 ;
4242
43- uint256 internal constant scale = 1 ;
4443 uint32 internal constant origin = 1 ;
4544 uint32 internal constant destination = 2 ;
4645 uint32 internal constant cctpOrigin = 0 ;
@@ -110,7 +109,6 @@ contract TokenBridgeCctpV1Test is Test {
110109
111110 TokenBridgeCctpV1 originImplementation = new TokenBridgeCctpV1 (
112111 address (tokenOrigin),
113- scale,
114112 address (mailboxOrigin),
115113 messageTransmitterOrigin,
116114 tokenMessengerOrigin
@@ -131,7 +129,6 @@ contract TokenBridgeCctpV1Test is Test {
131129
132130 TokenBridgeCctpV1 destinationImplementation = new TokenBridgeCctpV1 (
133131 address (tokenDestination),
134- scale,
135132 address (mailboxDestination),
136133 messageTransmitterDestination,
137134 tokenMessengerDestination
@@ -338,7 +335,6 @@ contract TokenBridgeCctpV1Test is Test {
338335 function _upgrade (TokenBridgeCctpBase bridge ) internal virtual {
339336 TokenBridgeCctpV1 newImplementation = new TokenBridgeCctpV1 (
340337 address (bridge.wrappedToken ()),
341- bridge.scale (),
342338 address (bridge.mailbox ()),
343339 bridge.messageTransmitter (),
344340 ITokenMessengerV1 (address (bridge.tokenMessenger ()))
@@ -508,7 +504,6 @@ contract TokenBridgeCctpV1Test is Test {
508504 vm.expectRevert (bytes ("Invalid TokenMessenger CCTP version " ));
509505 TokenBridgeCctpV1 v1 = new TokenBridgeCctpV1 (
510506 address (tokenOrigin),
511- scale,
512507 address (mailboxOrigin),
513508 messageTransmitterOrigin,
514509 tokenMessengerOrigin
@@ -518,7 +513,6 @@ contract TokenBridgeCctpV1Test is Test {
518513 vm.expectRevert (bytes ("Invalid messageTransmitter CCTP version " ));
519514 v1 = new TokenBridgeCctpV1 (
520515 address (tokenOrigin),
521- scale,
522516 address (mailboxOrigin),
523517 messageTransmitterOrigin,
524518 tokenMessengerOrigin
@@ -856,7 +850,6 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
856850
857851 TokenBridgeCctpV2 originImplementation = new TokenBridgeCctpV2 (
858852 address (tokenOrigin),
859- scale,
860853 address (mailboxOrigin),
861854 messageTransmitterOrigin,
862855 tokenMessengerOrigin,
@@ -879,7 +872,6 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
879872
880873 TokenBridgeCctpV2 destinationImplementation = new TokenBridgeCctpV2 (
881874 address (tokenDestination),
882- scale,
883875 address (mailboxDestination),
884876 messageTransmitterDestination,
885877 tokenMessengerDestination,
@@ -898,53 +890,69 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
898890 _setupTokenBridgesCctp (tbOrigin, tbDestination);
899891 }
900892
893+ function _setNonce (bytes memory cctpMessage , bytes32 nonce ) internal view {
894+ // length + NONCE_INDEX
895+ uint256 nonceOffset = 32 + 12 ;
896+ assembly {
897+ mstore (add (cctpMessage, nonceOffset), nonce)
898+ }
899+ }
900+
901901 function _encodeCctpBurnMessage (
902902 uint64 nonce ,
903903 uint32 sourceDomain ,
904904 bytes32 recipient ,
905905 uint256 amount ,
906906 address sender
907- ) internal view override returns (bytes memory ) {
907+ ) internal view override returns (bytes memory cctpMessage ) {
908908 bytes memory burnMessage = BurnMessageV2._formatMessageForRelay (
909909 version,
910910 address (tokenOrigin).addressToBytes32 (),
911911 recipient,
912- amount,
912+ amount + (amount * maxFee) / 10_000 ,
913913 sender.addressToBytes32 (),
914914 maxFee,
915915 bytes ("" )
916916 );
917- return
918- CctpMessageV2._formatMessageForRelay (
919- version,
920- sourceDomain,
921- cctpDestination,
922- address (tokenMessengerOrigin).addressToBytes32 (),
923- address (tokenMessengerDestination).addressToBytes32 (),
924- bytes32 (0 ),
925- minFinalityThreshold,
926- burnMessage
927- );
917+ cctpMessage = CctpMessageV2._formatMessageForRelay (
918+ version,
919+ sourceDomain,
920+ cctpDestination,
921+ address (tokenMessengerOrigin).addressToBytes32 (),
922+ address (tokenMessengerDestination).addressToBytes32 (),
923+ bytes32 (0 ),
924+ minFinalityThreshold,
925+ burnMessage
926+ );
927+ // pseudo random
928+ bytes32 nonceBytes = keccak256 (
929+ abi.encode (nonce, sender, recipient, amount)
930+ );
931+ _setNonce (cctpMessage, nonceBytes);
928932 }
929933
930934 function _encodeCctpHookMessage (
931935 bytes32 sender ,
932936 bytes32 recipient ,
933937 bytes memory message
934- ) internal view override returns (bytes memory ) {
935- return
936- CctpMessageV2._formatMessageForRelay (
937- version,
938- cctpOrigin,
939- cctpDestination,
940- sender,
941- recipient,
942- bytes32 (0 ),
943- minFinalityThreshold,
944- message
945- );
938+ ) internal view override returns (bytes memory cctpMessage ) {
939+ cctpMessage = CctpMessageV2._formatMessageForRelay (
940+ version,
941+ cctpOrigin,
942+ cctpDestination,
943+ sender,
944+ recipient,
945+ bytes32 (0 ),
946+ minFinalityThreshold,
947+ message
948+ );
949+ // pseudo random nonce
950+ bytes32 nonce = keccak256 (abi.encode (sender, recipient, message));
951+ _setNonce (cctpMessage, nonce);
946952 }
947953
954+ address constant usdc = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 ;
955+
948956 function _deploy () internal returns (TokenBridgeCctpV2) {
949957 ITokenMessengerV2 tokenMessenger = ITokenMessengerV2 (
950958 address (0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d )
@@ -955,8 +963,7 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
955963 );
956964
957965 TokenBridgeCctpV2 implementation = new TokenBridgeCctpV2 (
958- 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 ,
959- 1 ,
966+ usdc,
960967 0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D ,
961968 messageTransmitter,
962969 tokenMessenger,
@@ -1027,7 +1034,8 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
10271034 TokenBridgeCctpV2 router = _deploy ();
10281035
10291036 uint32 destination = 1 ; // ethereum
1030- router.addDomain (destination, 0 );
1037+ uint32 circleDestination = 0 ;
1038+ router.addDomain (destination, circleDestination);
10311039 router.enrollRemoteRouter (destination, ism);
10321040
10331041 Quote[] memory quotes = router.quoteTransferRemote (
@@ -1036,8 +1044,27 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
10361044 amount
10371045 );
10381046
1039- deal (quotes[1 ].token, address (this ), quotes[1 ].amount);
1040- IERC20 (quotes[1 ].token).approve (address (router), quotes[1 ].amount);
1047+ assertEq (quotes[1 ].token, usdc);
1048+ uint256 usdcQuote = quotes[1 ].amount;
1049+
1050+ deal (usdc, address (this ), usdcQuote);
1051+ IERC20 (usdc).approve (address (router), usdcQuote);
1052+
1053+ vm.expectEmit (true , true , true , true , address (router.tokenMessenger ()));
1054+ emit ITokenMessengerV2.DepositForBurn (
1055+ usdc,
1056+ usdcQuote,
1057+ address (router),
1058+ recipient,
1059+ circleDestination,
1060+ bytes32 (
1061+ 0x00000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d
1062+ ), // tokenMessengerDestination
1063+ bytes32 (0 ), // destinationCaller
1064+ maxFee,
1065+ minFinalityThreshold,
1066+ bytes ("" )
1067+ );
10411068
10421069 router.transferRemote {value: quotes[0 ].amount}(
10431070 destination,
@@ -1046,6 +1073,52 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
10461073 );
10471074 }
10481075
1076+ event MintAndWithdraw (
1077+ address indexed mintRecipient ,
1078+ uint256 amount ,
1079+ address indexed mintToken ,
1080+ uint256 feeCollected
1081+ );
1082+
1083+ function testFork_verify_tokenMessage () public {
1084+ vm.createSelectFork (vm.rpcUrl ("base " ), 32_739_842 );
1085+
1086+ TokenBridgeCctpV2 ism = _deploy ();
1087+
1088+ bytes32 hook = deployer.addressToBytes32 ();
1089+
1090+ uint32 origin = 10 ; // optimism
1091+ uint32 circleOrigin = 2 ;
1092+ ism.addDomain (origin, circleOrigin);
1093+ ism.enrollRemoteRouter (origin, hook);
1094+
1095+ // https://optimistic.etherscan.io/tx/0x4a8c5aef605bd1a79d7e4ab7b1852d246a05859a168db2b4791563877f2f3325
1096+ uint256 amount = 2 ;
1097+ uint256 fee = 1 ;
1098+ bytes
1099+ memory cctpMessage = hex "0000000100000002000000069abb52aa4e37d2ee3e521f9bc92e97581a68dadcd826fd2abaa5150de95db90e00000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d00000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000003e8000003e8000000010000000000000000000000000b2c639c533813f4aa9d7837caf62653d097ff85000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000001f825d8 " ;
1100+
1101+ // https://iris-api.circle.com/v2/messages/2?transactionHash=0x4a8c5aef605bd1a79d7e4ab7b1852d246a05859a168db2b4791563877f2f3325
1102+ bytes
1103+ memory attestation = hex "f75d61f667685827a63a857fcfae06fd9c42860c9a94175a2041a98941c874303aa44a973bf28b447ecc39e25d81a869584e9379d41f669dc526bf3b6810a0161c278bcd556ac5dd462095094af97a8773b33c00788a362c424d5569bdb4c2fb853ab4aefab3839bc8128e280f16fc09c6cfb11361061e527f5804fa6c6b130dc91b " ;
1104+
1105+ bytes memory metadata = abi.encode (cctpMessage, attestation);
1106+
1107+ bytes memory message = abi.encodePacked (
1108+ uint8 (3 ),
1109+ uint32 (0 ),
1110+ origin,
1111+ hook,
1112+ ism.localDomain (),
1113+ address (ism).addressToBytes32 (),
1114+ abi.encode (hook, amount)
1115+ );
1116+
1117+ vm.expectEmit (true , true , true , true , address (ism.tokenMessenger ()));
1118+ emit MintAndWithdraw (deployer, amount - fee, usdc, fee);
1119+ ism.verify (metadata, message);
1120+ }
1121+
10491122 function testFork_postDispatch (
10501123 bytes32 recipient ,
10511124 bytes calldata body
@@ -1103,15 +1176,16 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
11031176 amount
11041177 );
11051178
1179+ uint256 tokenQuote = quote[1 ].amount;
11061180 vm.startPrank (user);
1107- tokenOrigin.approve (address (tbOrigin), quote[ 1 ].amount );
1181+ tokenOrigin.approve (address (tbOrigin), tokenQuote );
11081182
11091183 vm.expectCall (
11101184 address (tokenMessengerOrigin),
11111185 abi.encodeCall (
11121186 ITokenMessengerV2.depositForBurn,
11131187 (
1114- amount ,
1188+ tokenQuote ,
11151189 cctpDestination,
11161190 user.addressToBytes32 (),
11171191 address (tokenOrigin),
@@ -1174,7 +1248,6 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
11741248 vm.expectRevert (bytes ("Invalid TokenMessenger CCTP version " ));
11751249 TokenBridgeCctpV2 v2 = new TokenBridgeCctpV2 (
11761250 address (tokenOrigin),
1177- scale,
11781251 address (mailboxOrigin),
11791252 messageTransmitterOrigin,
11801253 tokenMessengerOrigin,
@@ -1186,7 +1259,6 @@ contract TokenBridgeCctpV2Test is TokenBridgeCctpV1Test {
11861259 vm.expectRevert (bytes ("Invalid messageTransmitter CCTP version " ));
11871260 v2 = new TokenBridgeCctpV2 (
11881261 address (tokenOrigin),
1189- scale,
11901262 address (mailboxOrigin),
11911263 messageTransmitterOrigin,
11921264 tokenMessengerOrigin,
0 commit comments