Skip to content

Commit ff04006

Browse files
authored
LL: Fix encoded redeemer message length (#139)
Co-authored-by: gator-boi <gator-boi@users.noreply.github.com>
1 parent f9d747b commit ff04006

File tree

26 files changed

+267
-79
lines changed

26 files changed

+267
-79
lines changed

evm/forge/tests/MatchingEngine.t.sol

+4-12
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,7 @@ contract MatchingEngineTest is Test {
199199
MatchingEngine proxy =
200200
MatchingEngine(address(new ERC1967Proxy(address(implementation), "")));
201201

202-
vm.expectRevert(
203-
abi.encodeWithSignature("LengthMismatch(uint256,uint256)", 0, 40)
204-
);
202+
vm.expectRevert(abi.encodeWithSignature("LengthMismatch(uint256,uint256)", 0, 40));
205203
proxy.initialize(new bytes(0));
206204
}
207205

@@ -223,9 +221,7 @@ contract MatchingEngineTest is Test {
223221
MatchingEngine proxy =
224222
MatchingEngine(address(new ERC1967Proxy(address(implementation), "")));
225223

226-
vm.expectRevert(
227-
abi.encodeWithSignature("LengthMismatch(uint256,uint256)", 41, 40)
228-
);
224+
vm.expectRevert(abi.encodeWithSignature("LengthMismatch(uint256,uint256)", 41, 40));
229225
proxy.initialize(abi.encodePacked(makeAddr("ownerAssistant"), FEE_RECIPIENT, uint8(69)));
230226
}
231227

@@ -247,9 +243,7 @@ contract MatchingEngineTest is Test {
247243
MatchingEngine proxy =
248244
MatchingEngine(address(new ERC1967Proxy(address(implementation), "")));
249245

250-
vm.expectRevert(
251-
abi.encodeWithSignature("InvalidAddress()")
252-
);
246+
vm.expectRevert(abi.encodeWithSignature("InvalidAddress()"));
253247
proxy.initialize(abi.encodePacked(makeAddr("ownerAssistant"), address(0)));
254248
}
255249

@@ -271,9 +265,7 @@ contract MatchingEngineTest is Test {
271265
MatchingEngine proxy =
272266
MatchingEngine(address(new ERC1967Proxy(address(implementation), "")));
273267

274-
vm.expectRevert(
275-
abi.encodeWithSignature("InvalidAddress()")
276-
);
268+
vm.expectRevert(abi.encodeWithSignature("InvalidAddress()"));
277269
proxy.initialize(abi.encodePacked(address(0), FEE_RECIPIENT));
278270
}
279271

evm/forge/tests/TokenRouter.t.sol

+56-9
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,7 @@ contract TokenRouterTest is Test {
172172
);
173173
TokenRouter proxy = TokenRouter(address(new ERC1967Proxy(address(implementation), "")));
174174

175-
vm.expectRevert(
176-
abi.encodeWithSignature("LengthMismatch(uint256,uint256)", 0, 20)
177-
);
175+
vm.expectRevert(abi.encodeWithSignature("LengthMismatch(uint256,uint256)", 0, 20));
178176
proxy.initialize(new bytes(0));
179177
}
180178

@@ -193,9 +191,7 @@ contract TokenRouterTest is Test {
193191
);
194192
TokenRouter proxy = TokenRouter(address(new ERC1967Proxy(address(implementation), "")));
195193

196-
vm.expectRevert(
197-
abi.encodeWithSignature("LengthMismatch(uint256,uint256)", 40, 20)
198-
);
194+
vm.expectRevert(abi.encodeWithSignature("LengthMismatch(uint256,uint256)", 40, 20));
199195
proxy.initialize(abi.encodePacked(makeAddr("ownerAssistant"), makeAddr("hole")));
200196
}
201197

@@ -214,9 +210,7 @@ contract TokenRouterTest is Test {
214210
);
215211
TokenRouter proxy = TokenRouter(address(new ERC1967Proxy(address(implementation), "")));
216212

217-
vm.expectRevert(
218-
abi.encodeWithSignature("InvalidAddress()")
219-
);
213+
vm.expectRevert(abi.encodeWithSignature("InvalidAddress()"));
220214
proxy.initialize(abi.encodePacked(address(0)));
221215
}
222216

@@ -917,6 +911,30 @@ contract TokenRouterTest is Test {
917911
);
918912
}
919913

914+
function testCannotPlaceMarketOrderPayloadTooLarge() public {
915+
bytes memory encoded;
916+
917+
for (uint256 i = 0; i < 501; i++) {
918+
encoded = abi.encodePacked(encoded, hex"01");
919+
}
920+
921+
vm.expectRevert(
922+
abi.encodeWithSignature(
923+
"MaxPayloadSizeExceeded(uint256,uint256)",
924+
encoded.length,
925+
router.getMaxPayloadSize()
926+
)
927+
);
928+
router.placeMarketOrder(
929+
10, // amountIn.
930+
0, // minAmountOut
931+
2, // targetChain
932+
TEST_REDEEMER,
933+
encoded, // redeemerMessage
934+
address(this) // refundAddress
935+
);
936+
}
937+
920938
function testCannotPlaceMarketOrderErrUnsupportedChain() public {
921939
uint64 amountIn = 69;
922940
uint16 targetChain = 2;
@@ -1136,6 +1154,35 @@ contract TokenRouterTest is Test {
11361154
);
11371155
}
11381156

1157+
function testCannotPlaceFastMarketOrderPayloadTooLarge() public {
1158+
bytes memory encoded;
1159+
1160+
for (uint256 i = 0; i < 501; i++) {
1161+
encoded = abi.encodePacked(encoded, hex"01");
1162+
}
1163+
1164+
bytes memory encodedSignature = abi.encodeWithSignature(
1165+
"placeFastMarketOrder(uint64,uint64,uint16,bytes32,bytes,address,uint64,uint32)",
1166+
6900000, // amountIn.
1167+
0, // minAmountOut
1168+
ARB_CHAIN, // targetChain
1169+
TEST_REDEEMER,
1170+
encoded,
1171+
address(this), // refundAddress.
1172+
router.getMinFee() + 1,
1173+
0 // deadline
1174+
);
1175+
expectRevert(
1176+
address(router),
1177+
encodedSignature,
1178+
abi.encodeWithSignature(
1179+
"MaxPayloadSizeExceeded(uint256,uint256)",
1180+
encoded.length,
1181+
router.getMaxPayloadSize()
1182+
)
1183+
);
1184+
}
1185+
11391186
function testCannotPlaceFastMarketOrderErrInvalidRedeemerAddress() public {
11401187
bytes memory encodedSignature = abi.encodeWithSignature(
11411188
"placeFastMarketOrder(uint64,uint64,uint16,bytes32,bytes,address,uint64,uint32)",

evm/src/MatchingEngine/MatchingEngine.sol

+5-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ contract MatchingEngine is MatchingEngineFastOrders, MatchingEngineAdmin {
6969

7070
function _migrate() internal override {}
7171

72-
function _parseInitData(bytes memory initData) internal pure returns (address ownerAssistant, address feeRecipient) {
72+
function _parseInitData(bytes memory initData)
73+
internal
74+
pure
75+
returns (address ownerAssistant, address feeRecipient)
76+
{
7377
uint256 offset = 0;
7478

7579
(ownerAssistant, offset) = initData.asAddressUnchecked(offset);

evm/src/MatchingEngine/assets/Errors.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ error ErrDeadlineExceeded();
4444

4545
error ErrCallerNotDeployer(address deployer, address caller);
4646

47-
error InvalidInitDataLength(uint256 actual, uint256 expected);
47+
error InvalidInitDataLength(uint256 actual, uint256 expected);

evm/src/TokenRouter/assets/Errors.sol

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ error ErrInvalidMaxFee(uint64 maxFee, uint64 minimumReuiredFee);
4444

4545
error ErrCallerNotDeployer(address deployer, address caller);
4646

47-
error InvalidInitDataLength(uint256 actual, uint256 expected);
47+
error InvalidInitDataLength(uint256 actual, uint256 expected);
48+
49+
error MaxPayloadSizeExceeded(uint256 actualSize, uint256 maxSize);

evm/src/TokenRouter/assets/PlaceMarketOrder.sol

+8
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ abstract contract PlaceMarketOrder is IPlaceMarketOrder, Admin, State {
116116
revert ErrInsufficientAmount(0, 0);
117117
}
118118

119+
if (redeemerMessage.length > MAX_REDEEMER_PAYLOAD_SIZE) {
120+
revert MaxPayloadSizeExceeded(redeemerMessage.length, MAX_REDEEMER_PAYLOAD_SIZE);
121+
}
122+
119123
Endpoint memory endpoint = _verifyTarget(targetChain, redeemer);
120124

121125
SafeERC20.safeTransferFrom(_orderToken, msg.sender, address(this), amountIn);
@@ -153,6 +157,10 @@ abstract contract PlaceMarketOrder is IPlaceMarketOrder, Admin, State {
153157
revert ErrFastTransferNotSupported();
154158
}
155159

160+
if (redeemerMessage.length > MAX_REDEEMER_PAYLOAD_SIZE) {
161+
revert MaxPayloadSizeExceeded(redeemerMessage.length, MAX_REDEEMER_PAYLOAD_SIZE);
162+
}
163+
156164
// Verify the `amountIn` and specified auction price.
157165
FastTransferParameters memory fastParams = getFastTransferParametersState();
158166

evm/src/TokenRouter/assets/State.sol

+8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ abstract contract State is ITokenRouterState, WormholeCctpTokenMessenger {
3434
uint24 constant MAX_BPS_FEE = 1000000; // 10,000.00 bps (100%)
3535
uint64 constant MIN_FAST_TRANSFER_AMOUNT = 100000000; // $100
3636

37+
// Maximum redeemer payload size.
38+
uint256 constant MAX_REDEEMER_PAYLOAD_SIZE = 500;
39+
3740
constructor(
3841
address token_,
3942
address wormhole_,
@@ -155,4 +158,9 @@ abstract contract State is ITokenRouterState, WormholeCctpTokenMessenger {
155158
function getMaxFastTransferAmount() external view returns (uint64) {
156159
return getFastTransferParametersState().maxAmount;
157160
}
161+
162+
/// @inheritdoc ITokenRouterState
163+
function getMaxPayloadSize() external pure returns (uint256) {
164+
return MAX_REDEEMER_PAYLOAD_SIZE;
165+
}
158166
}

evm/src/interfaces/ITokenRouterState.sol

+5
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,9 @@ interface ITokenRouterState {
114114
* is only paid in the a fast transfer auction does not occur.
115115
*/
116116
function getBaseFee() external view returns (uint64);
117+
118+
/**
119+
* @notice Returns the maximum payload size for the redeemer message.
120+
*/
121+
function getMaxPayloadSize() external pure returns (uint256);
117122
}

evm/src/shared/Messages.sol

+15-13
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ library Messages {
5757
fill.sourceChain,
5858
fill.orderSender,
5959
fill.redeemer,
60-
_encodeBytes(fill.redeemerMessage)
60+
_encodeRedeemerMessage(fill.redeemerMessage)
6161
);
6262
}
6363

@@ -67,7 +67,7 @@ library Messages {
6767
(fill.sourceChain, offset) = encoded.asUint16Unchecked(offset);
6868
(fill.orderSender, offset) = encoded.asBytes32Unchecked(offset);
6969
(fill.redeemer, offset) = encoded.asBytes32Unchecked(offset);
70-
(fill.redeemerMessage, offset) = _decodeBytes(encoded, offset);
70+
(fill.redeemerMessage, offset) = _decodeRedeemerMessage(encoded, offset);
7171

7272
_checkLength(encoded, offset);
7373
}
@@ -84,7 +84,7 @@ library Messages {
8484
order.maxFee,
8585
order.initAuctionFee,
8686
order.deadline,
87-
_encodeBytes(order.redeemerMessage)
87+
_encodeRedeemerMessage(order.redeemerMessage)
8888
);
8989
}
9090

@@ -105,7 +105,7 @@ library Messages {
105105
(order.maxFee, offset) = encoded.asUint64Unchecked(offset);
106106
(order.initAuctionFee, offset) = encoded.asUint64Unchecked(offset);
107107
(order.deadline, offset) = encoded.asUint32Unchecked(offset);
108-
(order.redeemerMessage, offset) = _decodeBytes(encoded, offset);
108+
(order.redeemerMessage, offset) = _decodeRedeemerMessage(encoded, offset);
109109

110110
_checkLength(encoded, offset);
111111
}
@@ -117,7 +117,7 @@ library Messages {
117117
fastFill.fill.sourceChain,
118118
fastFill.fill.orderSender,
119119
fastFill.fill.redeemer,
120-
_encodeBytes(fastFill.fill.redeemerMessage)
120+
_encodeRedeemerMessage(fastFill.fill.redeemerMessage)
121121
);
122122
}
123123

@@ -133,7 +133,7 @@ library Messages {
133133
(fastFill.fill.sourceChain, offset) = encoded.asUint16Unchecked(offset);
134134
(fastFill.fill.orderSender, offset) = encoded.asBytes32Unchecked(offset);
135135
(fastFill.fill.redeemer, offset) = encoded.asBytes32Unchecked(offset);
136-
(fastFill.fill.redeemerMessage, offset) = _decodeBytes(encoded, offset);
136+
(fastFill.fill.redeemerMessage, offset) = _decodeRedeemerMessage(encoded, offset);
137137

138138
_checkLength(encoded, offset);
139139
}
@@ -161,20 +161,22 @@ library Messages {
161161

162162
// ---------------------------------------- private -------------------------------------------
163163

164-
function _decodeBytes(bytes memory encoded, uint256 startOffset)
164+
function _decodeRedeemerMessage(bytes memory encoded, uint256 startOffset)
165165
private
166166
pure
167167
returns (bytes memory payload, uint256 offset)
168168
{
169-
uint32 payloadLength;
170-
(payloadLength, offset) = encoded.asUint32Unchecked(startOffset);
169+
uint16 payloadLength;
170+
(payloadLength, offset) = encoded.asUint16Unchecked(startOffset);
171171
(payload, offset) = encoded.sliceUnchecked(offset, payloadLength);
172172
}
173173

174-
function _encodeBytes(bytes memory payload) private pure returns (bytes memory encoded) {
175-
// Casting payload.length to uint32 is safe because you'll be hard-pressed
176-
// to allocate 4 GB of EVM memory in a single transaction.
177-
encoded = abi.encodePacked(uint32(payload.length), payload);
174+
function _encodeRedeemerMessage(bytes memory payload)
175+
private
176+
pure
177+
returns (bytes memory encoded)
178+
{
179+
encoded = abi.encodePacked(uint16(payload.length), payload);
178180
}
179181

180182
function _checkLength(bytes memory encoded, uint256 expected) private pure {

evm/ts/src/messages.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ export class Fill {
166166
const sourceChain = buf.readUInt16BE(0);
167167
const orderSender = buf.subarray(2, 34);
168168
const redeemer = buf.subarray(34, 66);
169-
const redeemerMsgLen = buf.readUInt32BE(66);
170-
const redeemerMessage = buf.subarray(70, 70 + redeemerMsgLen);
169+
const redeemerMsgLen = buf.readUInt16BE(66);
170+
const redeemerMessage = buf.subarray(68, 68 + redeemerMsgLen);
171171

172172
return new Fill(sourceChain, orderSender, redeemer, redeemerMessage);
173173
}
@@ -205,9 +205,8 @@ export class FastFill {
205205
const sourceChain = buf.readUInt16BE(8);
206206
const orderSender = buf.subarray(10, 42);
207207
const redeemer = buf.subarray(42, 74);
208-
const redeemerMsgLen = buf.readUInt32BE(74);
209-
const endMessage = 70 + redeemerMsgLen;
210-
const redeemerMessage = buf.subarray(78, endMessage);
208+
const redeemerMsgLen = buf.readUInt16BE(74);
209+
const redeemerMessage = buf.subarray(76, 76 + redeemerMsgLen);
211210

212211
return new FastFill(sourceChain, orderSender, redeemer, redeemerMessage, fillAmount);
213212
}
@@ -265,8 +264,8 @@ export class FastMarketOrder {
265264
const maxFee = buf.readBigUInt64BE(114);
266265
const initAuctionFee = buf.readBigUInt64BE(122);
267266
const deadline = buf.readUInt32BE(130);
268-
const redeemerMsgLen = buf.readUInt32BE(134);
269-
const redeemerMessage = buf.subarray(134, 134 + redeemerMsgLen);
267+
const redeemerMsgLen = buf.readUInt16BE(134);
268+
const redeemerMessage = buf.subarray(136, 136 + redeemerMsgLen);
270269
return new FastMarketOrder(
271270
amountIn,
272271
minAmountOut,

solana/programs/matching-engine/src/events/auction_updated.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct AuctionUpdated {
99
pub vaa: Option<Pubkey>,
1010
pub source_chain: u16,
1111
pub target_protocol: MessageProtocol,
12-
pub redeemer_message_len: u32,
12+
pub redeemer_message_len: u16,
1313

1414
pub end_slot: u64,
1515
pub best_offer_token: Pubkey,

solana/programs/matching-engine/src/processor/auction/execute_fast_order/local.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ pub struct ExecuteFastOrderLocal<'info> {
6262
.unwrap()
6363
.to_fast_market_order_unchecked();
6464
65-
// It is safe to convert u32 to usize here.
66-
order.redeemer_message_len().try_into().unwrap()
65+
order.redeemer_message_len().into()
6766
})
6867
.ok_or(MatchingEngineError::FastFillTooLarge)?,
6968
seeds = [

solana/programs/matching-engine/src/processor/auction/prepare_order_response/cctp.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ pub struct PrepareOrderResponseCctp<'info> {
7474
.fast_market_order()
7575
.ok_or(MatchingEngineError::InvalidPayloadId)?;
7676
77-
// This is safe to unwrap because the length will not exceed u32.
78-
order.redeemer_message_len().try_into().unwrap()
77+
order.redeemer_message_len().into()
7978
}),
8079
seeds = [
8180
PreparedOrderResponse::SEED_PREFIX,

solana/programs/matching-engine/src/state/auction.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub struct AuctionInfo {
8282
pub offer_price: u64,
8383

8484
/// Length of the redeemer message, which may impact the expense to execute the auction.
85-
pub redeemer_message_len: u32,
85+
pub redeemer_message_len: u16,
8686

8787
/// If the destination asset is not equal to the asset used for auctions, this will be some
8888
/// value specifying its custody token bump and amount out.

solana/programs/token-router/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ cfg_if::cfg_if! {
2828
}
2929

3030
const PREPARED_CUSTODY_TOKEN_SEED_PREFIX: &[u8] = b"prepared-custody";
31+
const MAX_REDEEMER_MESSAGE_SIZE: usize = 500;
3132

3233
#[program]
3334
pub mod token_router {

0 commit comments

Comments
 (0)