Skip to content

Commit a44029d

Browse files
committed
new tests for more coverage
1 parent b96edae commit a44029d

File tree

3 files changed

+47
-57
lines changed

3 files changed

+47
-57
lines changed

evm/src/ExecutorQuoterRouter.sol

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ contract ExecutorQuoterRouter is IExecutorQuoterRouter {
3333
error GovernanceExpired(uint64 expiryTime);
3434
error NotAnEvmAddress(bytes32);
3535

36+
struct ExecutionParams {
37+
uint16 dstChain;
38+
bytes32 dstAddr;
39+
address refundAddr;
40+
address quoterAddr;
41+
}
42+
3643
constructor(address _executor) {
3744
EXECUTOR = IExecutor(_executor);
3845
OUR_CHAIN = EXECUTOR.ourChain();
@@ -112,25 +119,34 @@ contract ExecutorQuoterRouter is IExecutorQuoterRouter {
112119
bytes calldata requestBytes,
113120
bytes calldata relayInstructions
114121
) external payable {
115-
IExecutorQuoter implementation = quoterContract[quoterAddr];
116-
(uint256 requiredPayment, bytes32 payeeAddress, bytes32 quoteBody) =
117-
implementation.requestExecutionQuote(dstChain, dstAddr, refundAddr, requestBytes, relayInstructions);
122+
ExecutionParams memory params =
123+
ExecutionParams({dstChain: dstChain, dstAddr: dstAddr, refundAddr: refundAddr, quoterAddr: quoterAddr});
124+
_requestExecutionInternal(params, requestBytes, relayInstructions);
125+
}
126+
127+
function _requestExecutionInternal(
128+
ExecutionParams memory params,
129+
bytes calldata requestBytes,
130+
bytes calldata relayInstructions
131+
) private {
132+
IExecutorQuoter implementation = quoterContract[params.quoterAddr];
133+
(uint256 requiredPayment, bytes32 payeeAddress, bytes32 quoteBody) = implementation.requestExecutionQuote(
134+
params.dstChain, params.dstAddr, params.refundAddr, requestBytes, relayInstructions
135+
);
118136
if (msg.value < requiredPayment) {
119137
revert Underpaid(msg.value, requiredPayment);
120138
}
121139
if (msg.value > requiredPayment) {
122-
(bool refundSuccessful,) = payable(refundAddr).call{value: msg.value - requiredPayment}("");
140+
(bool refundSuccessful,) = payable(params.refundAddr).call{value: msg.value - requiredPayment}("");
123141
if (!refundSuccessful) {
124-
revert RefundFailed(refundAddr);
142+
revert RefundFailed(params.refundAddr);
125143
}
126144
}
145+
bytes memory signedQuote = abi.encodePacked(
146+
QUOTE_PREFIX, params.quoterAddr, payeeAddress, OUR_CHAIN, params.dstChain, EXPIRY_TIME, quoteBody
147+
);
127148
EXECUTOR.requestExecution{value: requiredPayment}(
128-
dstChain,
129-
dstAddr,
130-
refundAddr,
131-
abi.encodePacked(QUOTE_PREFIX, quoterAddr, payeeAddress, OUR_CHAIN, dstChain, EXPIRY_TIME, quoteBody),
132-
requestBytes,
133-
relayInstructions
149+
params.dstChain, params.dstAddr, params.refundAddr, signedQuote, requestBytes, relayInstructions
134150
);
135151
// this must emit a message in this function in order to verify off-chain that this contract generated the quote
136152
// the implementation is the only data available in this context that is not available from the executor event

evm/test/ExecutorQuoter.t.sol

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ contract ExecutorQuoterTest is Test {
4646
update2.update = packUint64(quote2.baseFee, quote2.dstGasPrice, quote2.srcPrice, quote2.dstPrice);
4747
ExecutorQuoter.OnChainQuoteBody memory quote3;
4848
quote3.baseFee = 27971;
49-
quote3.dstGasPrice = 1000078;
49+
quote3.dstGasPrice = 100000000;
5050
quote3.srcPrice = 35751300000000;
5151
quote3.dstPrice = 35751300000000;
5252
ExecutorQuoter.Update memory update3;
@@ -185,7 +185,7 @@ contract ExecutorQuoterTest is Test {
185185
}
186186

187187
/// @notice Test that max uint128 msgValue is handled without overflow.
188-
function test_requestQuote_maxMsgValue() public {
188+
function test_requestQuote_maxMsgValue() public view {
189189
uint128 maxMsgValue = type(uint128).max;
190190
bytes memory relayInstructions = RelayInstructions.encodeGas(250000, maxMsgValue);
191191

@@ -266,50 +266,6 @@ contract ExecutorQuoterTest is Test {
266266
assertGt(quote, uint256(type(uint128).max), "Quote should be greater than a single max uint128");
267267
}
268268

269-
/// @notice Compare individual quotes vs combined to verify addition.
270-
function test_requestQuote_verifyAddition() public {
271-
uint128 gasLimit = 250000;
272-
uint128 msgValue = 1e18; // 1 ETH worth
273-
uint128 dropoff = 2e18; // 2 ETH worth
274-
275-
// Get quote with just gas
276-
bytes memory gasOnly = RelayInstructions.encodeGas(gasLimit, 0);
277-
uint256 quoteGasOnly = executorQuoter.requestQuote(
278-
DST_CHAIN,
279-
DST_ADDR,
280-
UPDATER,
281-
ExecutorMessages.makeVAAv1Request(10002, bytes32(uint256(uint160(address(this)))), 1),
282-
gasOnly
283-
);
284-
285-
// Get quote with gas + msgValue
286-
bytes memory gasAndMsg = RelayInstructions.encodeGas(gasLimit, msgValue);
287-
uint256 quoteGasAndMsg = executorQuoter.requestQuote(
288-
DST_CHAIN,
289-
DST_ADDR,
290-
UPDATER,
291-
ExecutorMessages.makeVAAv1Request(10002, bytes32(uint256(uint160(address(this)))), 1),
292-
gasAndMsg
293-
);
294-
295-
// Get quote with gas + msgValue + dropoff
296-
bytes memory all = abi.encodePacked(
297-
RelayInstructions.encodeGas(gasLimit, msgValue),
298-
RelayInstructions.encodeGasDropOffInstructions(dropoff, bytes32(uint256(uint160(address(this)))))
299-
);
300-
uint256 quoteAll = executorQuoter.requestQuote(
301-
DST_CHAIN,
302-
DST_ADDR,
303-
UPDATER,
304-
ExecutorMessages.makeVAAv1Request(10002, bytes32(uint256(uint160(address(this)))), 1),
305-
all
306-
);
307-
308-
// Verify monotonic increase
309-
assertGt(quoteGasAndMsg, quoteGasOnly, "Adding msgValue should increase quote");
310-
assertGt(quoteAll, quoteGasAndMsg, "Adding dropoff should increase quote");
311-
}
312-
313269
/// @notice Test quote calculation with zero prices (division by zero protection).
314270
function test_requestQuote_zeroPrices() public {
315271
// Set up a quote with zero srcPrice (would cause division by zero)

evm/test/ExecutorQuoterRouter.t.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,24 @@ contract ExecutorQuoterRouterTest is Test {
211211
);
212212
}
213213

214+
/// @notice Test that ecrecover returning address(0) is caught.
215+
/// This happens when v is not 27 or 28, causing ecrecover to return address(0).
216+
function test_updateQuoterContractEcrecoverReturnsZero() public {
217+
// Build a governance message with an invalid v value (not 27 or 28)
218+
// This will cause ecrecover to return address(0)
219+
bytes memory govBody =
220+
abi.encodePacked(hex"45473031", OUR_CHAIN, testQuoter, UPDATE_IMPLEMENTATION, SENDER_ADDRESS, EXPIRY);
221+
bytes32 digest = keccak256(govBody);
222+
(, bytes32 r, bytes32 s) = vm.sign(testQuoterPk, digest);
223+
224+
// Corrupt v to make it invalid (use 0 instead of 27/28)
225+
// This causes ecrecover to return address(0)
226+
bytes memory badGov = abi.encodePacked(govBody, r, s, uint8(0));
227+
228+
vm.expectRevert(abi.encodeWithSelector(ExecutorQuoterRouter.InvalidSignature.selector));
229+
executorQuoterRouter.updateQuoterContract(badGov);
230+
}
231+
214232
function test_updateQuoterContractQuoterMismatch() public {
215233
(address alice,) = makeAddrAndKey("alice");
216234
(, uint256 bobPk) = makeAddrAndKey("bob");

0 commit comments

Comments
 (0)