Skip to content

Commit c43e1bf

Browse files
committed
evm: calldata ftw
1 parent 3f5bcc5 commit c43e1bf

10 files changed

+99
-155
lines changed

evm/forge/tests/CircleIntegration.t.sol

+13-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
88
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
99

1010
import {IWormhole} from "src/interfaces/IWormhole.sol";
11-
import {ICircleIntegration, RedeemParameters} from "src/interfaces/ICircleIntegration.sol";
11+
import {ICircleIntegration} from "src/interfaces/ICircleIntegration.sol";
1212

1313
import {Utils} from "src/libraries/Utils.sol";
1414
import {Deposit, WormholeCctpMessages} from "src/libraries/WormholeCctpMessages.sol";
@@ -264,7 +264,8 @@ contract CircleIntegrationTest is Test {
264264
uint32 remoteDomain = 1;
265265
uint16 emitterChain = 6;
266266

267-
RedeemParameters memory redeemParams = circleIntegration.craftRedeemParameters(
267+
ICircleIntegration.RedeemParameters memory redeemParams = circleIntegration
268+
.craftRedeemParameters(
268269
CraftedCctpMessageParams({
269270
remoteDomain: remoteDomain,
270271
nonce: 2 ** 64 - 1,
@@ -292,7 +293,8 @@ contract CircleIntegrationTest is Test {
292293

293294
(uint16 emitterChain, uint32 remoteDomain) = _registerEmitterAndDomain();
294295

295-
RedeemParameters memory redeemParams = circleIntegration.craftRedeemParameters(
296+
ICircleIntegration.RedeemParameters memory redeemParams = circleIntegration
297+
.craftRedeemParameters(
296298
CraftedCctpMessageParams({
297299
remoteDomain: remoteDomain,
298300
nonce: 2 ** 64 - 1,
@@ -317,7 +319,8 @@ contract CircleIntegrationTest is Test {
317319

318320
(uint16 emitterChain, uint32 remoteDomain) = _registerEmitterAndDomain();
319321

320-
RedeemParameters memory redeemParams = circleIntegration.craftRedeemParameters(
322+
ICircleIntegration.RedeemParameters memory redeemParams = circleIntegration
323+
.craftRedeemParameters(
321324
CraftedCctpMessageParams({
322325
remoteDomain: remoteDomain,
323326
nonce: 2 ** 64 - 1,
@@ -340,7 +343,8 @@ contract CircleIntegrationTest is Test {
340343
function test_CannotRedeemTokensWithPayloadInvalidMessagePair() public {
341344
(uint16 emitterChain, uint32 remoteDomain) = _registerEmitterAndDomain();
342345

343-
RedeemParameters memory redeemParams1 = circleIntegration.craftRedeemParameters(
346+
ICircleIntegration.RedeemParameters memory redeemParams1 = circleIntegration
347+
.craftRedeemParameters(
344348
CraftedCctpMessageParams({
345349
remoteDomain: remoteDomain,
346350
nonce: 2 ** 64 - 2,
@@ -353,7 +357,8 @@ contract CircleIntegrationTest is Test {
353357
abi.encodePacked("Somebody set up us the bomb")
354358
);
355359

356-
RedeemParameters memory redeemParams2 = circleIntegration.craftRedeemParameters(
360+
ICircleIntegration.RedeemParameters memory redeemParams2 = circleIntegration
361+
.craftRedeemParameters(
357362
CraftedCctpMessageParams({
358363
remoteDomain: remoteDomain,
359364
nonce: 2 ** 64 - 1,
@@ -393,7 +398,8 @@ contract CircleIntegrationTest is Test {
393398
payload: abi.encodePacked("Somebody set up us the bomb")
394399
});
395400

396-
RedeemParameters memory redeemParams = circleIntegration.craftRedeemParameters(
401+
ICircleIntegration.RedeemParameters memory redeemParams = circleIntegration
402+
.craftRedeemParameters(
397403
CraftedCctpMessageParams({
398404
remoteDomain: expected.sourceDomain,
399405
nonce: expected.nonce,

evm/forge/tests/gas/CircleIntegrationComparison.t.sol

+14-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
88
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
99

1010
import {IWormhole} from "src/interfaces/IWormhole.sol";
11-
import {ICircleIntegration, RedeemParameters} from "src/interfaces/ICircleIntegration.sol";
11+
import {ICircleIntegration} from "src/interfaces/ICircleIntegration.sol";
1212

1313
import {Utils} from "src/libraries/Utils.sol";
1414
import {Deposit, WormholeCctpMessages} from "src/libraries/WormholeCctpMessages.sol";
@@ -211,7 +211,8 @@ contract CircleIntegrationComparison is Test {
211211

212212
(uint16 emitterChain, uint32 remoteDomain) = _registerEmitterAndDomain(circleIntegration);
213213

214-
RedeemParameters memory redeemParams = circleIntegration.craftRedeemParameters(
214+
ICircleIntegration.RedeemParameters memory redeemParams = circleIntegration
215+
.craftRedeemParameters(
215216
CraftedCctpMessageParams({
216217
remoteDomain: remoteDomain,
217218
nonce: 2 ** 64 - 1,
@@ -228,7 +229,9 @@ contract CircleIntegrationComparison is Test {
228229
address(inheritedContract).toUniversalAddress() // destinationCaller
229230
);
230231

231-
inheritedContract.redeemUsdc(redeemParams);
232+
inheritedContract.redeemUsdc(
233+
redeemParams.cctpMessage, redeemParams.cctpAttestation, redeemParams.encodedVaa
234+
);
232235
}
233236

234237
function test_Composed__RedeemUsdc(uint256 amount, bytes32 fromAddress, bytes32 data) public {
@@ -238,7 +241,8 @@ contract CircleIntegrationComparison is Test {
238241

239242
(uint16 emitterChain, uint32 remoteDomain) = _registerEmitterAndDomain(circleIntegration);
240243

241-
RedeemParameters memory redeemParams = circleIntegration.craftRedeemParameters(
244+
ICircleIntegration.RedeemParameters memory redeemParams = circleIntegration
245+
.craftRedeemParameters(
242246
CraftedCctpMessageParams({
243247
remoteDomain: remoteDomain,
244248
nonce: 2 ** 64 - 1,
@@ -263,7 +267,8 @@ contract CircleIntegrationComparison is Test {
263267

264268
(uint16 emitterChain, uint32 remoteDomain) = _registerEmitterAndDomain(circleIntegration);
265269

266-
RedeemParameters memory redeemParams = circleIntegration.craftRedeemParameters(
270+
ICircleIntegration.RedeemParameters memory redeemParams = circleIntegration
271+
.craftRedeemParameters(
267272
CraftedCctpMessageParams({
268273
remoteDomain: remoteDomain,
269274
nonce: 2 ** 64 - 1,
@@ -289,7 +294,8 @@ contract CircleIntegrationComparison is Test {
289294
(uint16 emitterChain, uint32 remoteDomain) =
290295
_registerEmitterAndDomain(forkedCircleIntegration);
291296

292-
RedeemParameters memory redeemParams = forkedCircleIntegration.craftRedeemParameters(
297+
ICircleIntegration.RedeemParameters memory redeemParams = forkedCircleIntegration
298+
.craftRedeemParameters(
293299
CraftedCctpMessageParams({
294300
remoteDomain: remoteDomain,
295301
nonce: 2 ** 64 - 1,
@@ -317,7 +323,8 @@ contract CircleIntegrationComparison is Test {
317323
(uint16 emitterChain, uint32 remoteDomain) =
318324
_registerEmitterAndDomain(forkedCircleIntegration);
319325

320-
RedeemParameters memory redeemParams = forkedCircleIntegration.craftRedeemParameters(
326+
ICircleIntegration.RedeemParameters memory redeemParams = forkedCircleIntegration
327+
.craftRedeemParameters(
321328
CraftedCctpMessageParams({
322329
remoteDomain: remoteDomain,
323330
nonce: 2 ** 64 - 1,

evm/forge/tests/helpers/libraries/CircleIntegrationOverride.sol

+4-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {IWormhole} from "src/interfaces/IWormhole.sol";
88
import {BytesParsing} from "src/libraries/BytesParsing.sol";
99
import {Utils} from "src/libraries/Utils.sol";
1010
import {Deposit, WormholeCctpMessages} from "src/libraries/WormholeCctpMessages.sol";
11-
import {RedeemParameters} from "src/libraries/WormholeCctpStructs.sol";
1211

1312
import "forge-std/Test.sol";
1413
import "forge-std/console.sol";
@@ -246,7 +245,7 @@ library CircleIntegrationOverride {
246245
bytes memory payload,
247246
bytes32 messageSender,
248247
bytes32 destinationCaller
249-
) internal view returns (RedeemParameters memory params) {
248+
) internal view returns (ICircleIntegration.RedeemParameters memory params) {
250249
params = _craftRedeemParameters(
251250
circleIntegration,
252251
cctpParams,
@@ -265,7 +264,7 @@ library CircleIntegrationOverride {
265264
bytes32 fromAddress,
266265
bytes memory payload,
267266
bytes32 messageSender
268-
) internal view returns (RedeemParameters memory params) {
267+
) internal view returns (ICircleIntegration.RedeemParameters memory params) {
269268
params = _craftRedeemParameters(
270269
circleIntegration,
271270
cctpParams,
@@ -283,7 +282,7 @@ library CircleIntegrationOverride {
283282
CraftedVaaParams memory vaaParams,
284283
bytes32 fromAddress,
285284
bytes memory payload
286-
) internal view returns (RedeemParameters memory params) {
285+
) internal view returns (ICircleIntegration.RedeemParameters memory params) {
287286
params = _craftRedeemParameters(
288287
circleIntegration,
289288
cctpParams,
@@ -316,7 +315,7 @@ library CircleIntegrationOverride {
316315
bytes memory payload,
317316
bytes32 messageSender,
318317
bytes32 destinationCaller
319-
) private view returns (RedeemParameters memory params) {
318+
) private view returns (ICircleIntegration.RedeemParameters memory params) {
320319
CctpTokenBurnMessage memory burnMsg;
321320
(burnMsg, params.cctpMessage, params.cctpAttestation) = _craftCctpTokenBurnMessage(
322321
circleIntegration,

evm/forge/tests/integrations/ComposingWithCircleIntegration.sol

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {ICircleIntegration} from "src/interfaces/ICircleIntegration.sol";
99
import {IWormhole} from "src/interfaces/IWormhole.sol";
1010

1111
import {Utils} from "src/libraries/Utils.sol";
12-
import {RedeemParameters} from "src/libraries/WormholeCctpStructs.sol";
1312

1413
contract ComposingWithCircleIntegration {
1514
using SafeERC20 for IERC20;
@@ -50,7 +49,7 @@ contract ComposingWithCircleIntegration {
5049
);
5150
}
5251

53-
function redeemUsdc(RedeemParameters calldata params) public {
52+
function redeemUsdc(ICircleIntegration.RedeemParameters calldata params) public {
5453
ICircleIntegration.DepositWithPayload memory deposit =
5554
_circleIntegration.redeemTokensWithPayload(params);
5655

evm/forge/tests/integrations/InheritingWormholeCctp.sol

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ pragma solidity ^0.8.19;
55
import {IWormhole} from "src/interfaces/IWormhole.sol";
66

77
import {Utils} from "src/libraries/Utils.sol";
8-
import {RedeemParameters} from "src/libraries/WormholeCctpStructs.sol";
98

109
import {WormholeCctp} from "src/contracts/WormholeCctp.sol";
1110

@@ -44,8 +43,12 @@ contract InheritingWormholeCctp is WormholeCctp {
4443
);
4544
}
4645

47-
function redeemUsdc(RedeemParameters calldata params) public {
48-
verifyVaaAndMint(params);
46+
function redeemUsdc(
47+
bytes calldata encodedCctpMessage,
48+
bytes calldata cctpAttestation,
49+
bytes calldata encodedVaa
50+
) public {
51+
verifyVaaAndMint(encodedCctpMessage, cctpAttestation, encodedVaa);
4952
}
5053

5154
function myBffDomain() public pure returns (uint32 domain) {

evm/src/contracts/CircleIntegration/Logic.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {ICircleIntegration} from "src/interfaces/ICircleIntegration.sol";
1010

1111
import {Utils} from "src/libraries/Utils.sol";
1212
import {Deposit, WormholeCctpMessages} from "src/libraries/WormholeCctpMessages.sol";
13-
import {RedeemParameters} from "src/libraries/WormholeCctpStructs.sol";
1413

1514
import {Governance} from "./Governance.sol";
1615
import {
@@ -68,7 +67,8 @@ abstract contract Logic is ICircleIntegration, Governance {
6867
bytes32 emitter;
6968
bytes32 vaaHash;
7069
Deposit memory depositHeader;
71-
(chain, emitter, vaaHash, depositHeader, deposit.payload) = verifyVaaAndMintLegacy(params);
70+
(chain, emitter, vaaHash, depositHeader, deposit.payload) =
71+
verifyVaaAndMintLegacy(params.cctpMessage, params.cctpAttestation, params.encodedVaa);
7272

7373
// NOTE: Reverting with Error(string) comes from the old implementation, so we preserve it.
7474
require(emitter != 0 && emitter == getRegisteredEmitters()[chain], "unknown emitter");

evm/src/contracts/WormholeCctp.sol

+50-22
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {ITokenMinter} from "src/interfaces/ITokenMinter.sol";
1212

1313
import {Utils} from "src/libraries/Utils.sol";
1414
import {Deposit, WormholeCctpMessages} from "src/libraries/WormholeCctpMessages.sol";
15-
import {RedeemParameters, RedeemedDeposit} from "src/libraries/WormholeCctpStructs.sol";
1615

1716
abstract contract WormholeCctp {
1817
using Utils for address;
@@ -21,7 +20,19 @@ abstract contract WormholeCctp {
2120

2221
error InvalidVaa();
2322
error CallerNotMintRecipient(bytes32, bytes32);
24-
error CctpVaaMismatch();
23+
error CctpVaaMismatch(uint32, uint32, uint64);
24+
25+
struct RedeemedDeposit {
26+
bytes32 token;
27+
uint256 amount;
28+
bytes32 burnSource;
29+
uint32 vaaTimestamp;
30+
uint32 vaaNonce;
31+
uint16 emitterChain;
32+
bytes32 emitterAddress;
33+
uint64 vaaSequence;
34+
bytes32 vaaHash;
35+
}
2536

2637
/**
2738
* @notice Emitted when Circle-supported assets have been minted to the mintRecipient
@@ -95,10 +106,11 @@ abstract contract WormholeCctp {
95106
);
96107
}
97108

98-
function verifyVaaAndMint(RedeemParameters calldata params)
99-
internal
100-
returns (RedeemedDeposit memory redeemed, bytes memory payload)
101-
{
109+
function verifyVaaAndMint(
110+
bytes calldata encodedCctpMessage,
111+
bytes calldata cctpAttestation,
112+
bytes calldata encodedVaa
113+
) internal returns (RedeemedDeposit memory redeemed, bytes memory payload) {
102114
Deposit memory deposit;
103115
(
104116
redeemed.vaaTimestamp,
@@ -110,7 +122,9 @@ abstract contract WormholeCctp {
110122
deposit,
111123
payload
112124
) = _verifyVaaAndMint(
113-
params,
125+
encodedCctpMessage,
126+
cctpAttestation,
127+
encodedVaa,
114128
true // revertCustomErrors
115129
);
116130

@@ -120,7 +134,11 @@ abstract contract WormholeCctp {
120134
redeemed.burnSource = deposit.burnSource;
121135
}
122136

123-
function verifyVaaAndMintLegacy(RedeemParameters calldata params)
137+
function verifyVaaAndMintLegacy(
138+
bytes calldata encodedCctpMessage,
139+
bytes calldata cctpAttestation,
140+
bytes calldata encodedVaa
141+
)
124142
internal
125143
returns (
126144
uint16 emitterChain,
@@ -131,7 +149,9 @@ abstract contract WormholeCctp {
131149
)
132150
{
133151
(,, emitterChain, emitterAddress,, vaaHash, deposit, payload) = _verifyVaaAndMint(
134-
params,
152+
encodedCctpMessage,
153+
cctpAttestation,
154+
encodedVaa,
135155
false // revertCustomErrors
136156
);
137157
}
@@ -148,7 +168,12 @@ abstract contract WormholeCctp {
148168

149169
// private
150170

151-
function _verifyVaaAndMint(RedeemParameters calldata params, bool revertCustomErrors)
171+
function _verifyVaaAndMint(
172+
bytes calldata encodedCctpMessage,
173+
bytes calldata cctpAttestation,
174+
bytes calldata encodedVaa,
175+
bool revertCustomErrors
176+
)
152177
private
153178
returns (
154179
uint32 vaaTimestamp,
@@ -163,7 +188,7 @@ abstract contract WormholeCctp {
163188
{
164189
// Parse and Verify VAA.
165190
(IWormhole.VM memory vaa, bool valid, string memory reason) =
166-
_wormhole.parseAndVerifyVM(params.encodedVaa);
191+
_wormhole.parseAndVerifyVM(encodedVaa);
167192

168193
// Confirm that the core layer verified the message.
169194
if (!valid) {
@@ -191,36 +216,39 @@ abstract contract WormholeCctp {
191216

192217
// Confirm that the caller passed the correct message pair.
193218
{
194-
bytes memory encodedCctpMessage = params.cctpMessage;
195-
196219
uint32 sourceDomain;
197220
uint32 targetDomain;
198221
uint64 nonce;
199222

200-
// NOTE: First four bytes is the CCTP message version.
201223
assembly ("memory-safe") {
202-
// source domain is 4 bytes after the version
203-
sourceDomain := mload(add(encodedCctpMessage, 8))
204-
// target domain is 4 bytes after the source domain
205-
targetDomain := mload(add(encodedCctpMessage, 12))
206-
// nonce is 8 bytes after the target version
207-
nonce := mload(add(encodedCctpMessage, 20))
224+
// NOTE: First four bytes is the CCTP message version.
225+
let ptr := calldataload(encodedCctpMessage.offset)
226+
227+
// NOTE: There is no need to mask here because the types defined outside of this
228+
// YUL block will already perform big-endian masking.
229+
230+
// Source domain is bytes 4..8, so shift 24 bytes to the right.
231+
sourceDomain := shr(192, ptr)
232+
// Target domain is bytes 8..12, so shift 20 bytes to the right.
233+
targetDomain := shr(160, ptr)
234+
// Nonce is bytes 12..20, so shift 12 bytes to the right.
235+
nonce := shr(96, ptr)
208236
}
209237

210238
if (
211239
deposit.sourceCctpDomain != sourceDomain || deposit.targetCctpDomain != targetDomain
212240
|| deposit.cctpNonce != nonce
213241
) {
214242
if (revertCustomErrors) {
215-
revert CctpVaaMismatch();
243+
revert CctpVaaMismatch(sourceDomain, targetDomain, nonce);
216244
} else {
217245
_revertBuiltIn("invalid message pair");
218246
}
219247
}
220248
}
221249

222250
// Call the circle bridge to mint tokens to the recipient.
223-
_messageTransmitter.receiveMessage(params.cctpMessage, params.cctpAttestation);
251+
_messageTransmitter.receiveMessage(encodedCctpMessage, cctpAttestation);
224252

225253
// Overwrite the token address in the deposit with the local token address. We should trust
226254
// that this getter will not return the zero address because the Token Minter will have

0 commit comments

Comments
 (0)