Skip to content

Commit 011f6f3

Browse files
committed
Merge branch 'atlas-v1.6.1'
2 parents 64a404d + aa39e35 commit 011f6f3

File tree

6 files changed

+368
-31
lines changed

6 files changed

+368
-31
lines changed

src/contracts/examples/fastlane-online/OuterHelpers.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ contract OuterHelpers is FastLaneOnlineInner {
3434
address public immutable SIMULATOR;
3535

3636
uint256 internal constant _BITS_FOR_INDEX = 16;
37-
uint256 internal constant _SOLVER_SIM_GAS_LIM = 4_800_000;
37+
uint256 internal constant _SOLVER_SIM_GAS_LIM = 4_850_000;
3838

3939
constructor(address atlas, address protocolGuildWallet) FastLaneOnlineInner(atlas) {
4040
CARDANO_ENGINEER_THERAPY_FUND = msg.sender;

src/contracts/helpers/Simulator.sol

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { Math } from "openzeppelin-contracts/contracts/utils/math/Math.sol";
66

77
import "../types/SolverOperation.sol";
88
import "../types/UserOperation.sol";
9-
import "../types/LockTypes.sol";
109
import "../types/DAppOperation.sol";
1110
import "../types/ConfigTypes.sol";
1211
import "../types/ValidCalls.sol";
@@ -25,8 +24,17 @@ contract Simulator is AtlasErrors, AtlasConstants {
2524
using AccountingMath for uint256;
2625
using GasAccLib for SolverOperation[];
2726

28-
uint256 internal constant _SIM_GAS_SUGGESTED_BUFFER = 30_000;
29-
uint256 internal constant _SIM_GAS_BEFORE_METACALL = 10_000;
27+
// The approx gas used between the minGasLeft check and the Atlas metacall() from this Simulator contract.
28+
uint256 internal constant _ERROR_CATCHER_GAS_BUFFER = 10_000;
29+
// The approx gas used between the `simUserOperation()` or `simSolverCall()` entrypoint functions in this Simulator
30+
// contract, and the minGasLeft check.
31+
uint256 internal constant _SIM_ENTRYPOINT_GAS_BUFFER = 50_000;
32+
33+
// An approximation of (64 / 63) ^ 2. This is the scaling to apply to a min amount of gas, such that after 2
34+
// external calls where only 63/64 of the gas is forwarded, there is still that original intended amount of gas
35+
// left. Used in _errorCatcher() below.
36+
uint256 internal constant _MIN_GAS_SCALING_FACTOR = 10_320; // 10_320 out of 10_000 = 1.032
37+
uint256 internal constant _SCALE = AccountingMath._SCALE; // 10_000 = 100%
3038

3139
address public immutable deployer;
3240
address public atlas;
@@ -49,6 +57,19 @@ contract Simulator is AtlasErrors, AtlasConstants {
4957
public
5058
view
5159
returns (uint256)
60+
{
61+
(uint256 metacallCalldataGas, uint256 metacallExecutionGas) = _estimateMetacallGasLimit(userOp, solverOps);
62+
return metacallCalldataGas + metacallExecutionGas;
63+
}
64+
65+
/// @notice Internal function to estimate the calldata and execution gas for a metacall.
66+
function _estimateMetacallGasLimit(
67+
UserOperation calldata userOp,
68+
SolverOperation[] memory solverOps
69+
)
70+
internal
71+
view
72+
returns (uint256 metacallCalldataGas, uint256 metacallExecutionGas)
5273
{
5374
DAppConfig memory dConfig = IDAppControl(userOp.control).getDAppConfig(userOp);
5475
address l2GasCalculator = IAtlas(atlas).L2_GAS_CALCULATOR();
@@ -65,18 +86,19 @@ contract Simulator is AtlasErrors, AtlasConstants {
6586
allSolversExecutionGas += Math.min(solverOps[i].gas, dConfig.solverGasLimit);
6687
}
6788

68-
uint256 metacallCalldataGas = (_SOLVER_OP_BASE_CALLDATA * solverOpsLen)
69-
+ GasAccLib.calldataGas(solverDataLenSum, l2GasCalculator)
70-
+ GasAccLib.metacallCalldataGas(nonSolverCalldataLength, l2GasCalculator);
89+
// metacallCalldataGas is first item in return tuple
90+
metacallCalldataGas = GasAccLib.calldataGas(
91+
solverDataLenSum + (_SOLVER_OP_BASE_CALLDATA * solverOpsLen), l2GasCalculator
92+
) + GasAccLib.metacallCalldataGas(nonSolverCalldataLength, l2GasCalculator);
7193

72-
uint256 metacallExecutionGas = _BASE_TX_GAS_USED + AccountingMath._FIXED_GAS_OFFSET + userOp.gas
73-
+ dConfig.dappGasLimit + allSolversExecutionGas;
94+
// metacallExecutionGas is second item in return tuple
95+
metacallExecutionGas = _BASE_TX_GAS_USED + AccountingMath._FIXED_GAS_OFFSET + userOp.gas + dConfig.dappGasLimit
96+
+ allSolversExecutionGas;
7497

98+
// If exPostBids = true, add extra execution gas to account for bid-finding.
7599
if (dConfig.callConfig.exPostBids()) {
76100
metacallExecutionGas += (solverOpsLen * _BID_FIND_OVERHEAD) + allSolversExecutionGas;
77101
}
78-
79-
return metacallCalldataGas + metacallExecutionGas;
80102
}
81103

82104
/// @notice Returns an estimation of the max amount (in native token) a winning solver could be charged, given the
@@ -148,9 +170,10 @@ contract Simulator is AtlasErrors, AtlasConstants {
148170
dAppOp.to = atlas;
149171
dAppOp.control = userOp.control;
150172

151-
uint256 estGasLim = estimateMetacallGasLimit(userOp, solverOps);
173+
(uint256 metacallCalldataGas, uint256 metacallExecutionGas) = _estimateMetacallGasLimit(userOp, solverOps);
152174

153-
(Result result, uint256 validCallsResult) = _errorCatcher(userOp, solverOps, dAppOp, estGasLim);
175+
(Result result, uint256 validCallsResult) =
176+
_errorCatcher(userOp, solverOps, dAppOp, metacallExecutionGas, metacallCalldataGas);
154177
success = uint8(result) > uint8(Result.UserOpSimFail);
155178
if (success) validCallsResult = uint256(ValidCallsResult.Valid);
156179
if (msg.value != 0) SafeTransferLib.safeTransferETH(msg.sender, msg.value);
@@ -171,9 +194,10 @@ contract Simulator is AtlasErrors, AtlasConstants {
171194
SolverOperation[] memory solverOps = new SolverOperation[](1);
172195
solverOps[0] = solverOp;
173196

174-
uint256 estGasLim = estimateMetacallGasLimit(userOp, solverOps);
197+
(uint256 metacallCalldataGas, uint256 metacallExecutionGas) = _estimateMetacallGasLimit(userOp, solverOps);
175198

176-
(Result result, uint256 solverOutcomeResult) = _errorCatcher(userOp, solverOps, dAppOp, estGasLim);
199+
(Result result, uint256 solverOutcomeResult) =
200+
_errorCatcher(userOp, solverOps, dAppOp, metacallExecutionGas, metacallCalldataGas);
177201
success = result == Result.SimulationPassed;
178202
if (success) solverOutcomeResult = 0; // discard additional error uint if solver stage was successful
179203
if (msg.value != 0) SafeTransferLib.safeTransferETH(msg.sender, msg.value);
@@ -196,9 +220,10 @@ contract Simulator is AtlasErrors, AtlasConstants {
196220
return (false, Result.Unknown, uint256(type(SolverOutcome).max) + 1);
197221
}
198222

199-
uint256 estGasLim = estimateMetacallGasLimit(userOp, solverOps);
223+
(uint256 metacallCalldataGas, uint256 metacallExecutionGas) = _estimateMetacallGasLimit(userOp, solverOps);
200224

201-
(Result result, uint256 solverOutcomeResult) = _errorCatcher(userOp, solverOps, dAppOp, estGasLim);
225+
(Result result, uint256 solverOutcomeResult) =
226+
_errorCatcher(userOp, solverOps, dAppOp, metacallExecutionGas, metacallCalldataGas);
202227
success = result == Result.SimulationPassed;
203228
if (success) solverOutcomeResult = 0; // discard additional error uint if solver stage was successful
204229
if (msg.value != 0) SafeTransferLib.safeTransferETH(msg.sender, msg.value);
@@ -209,16 +234,33 @@ contract Simulator is AtlasErrors, AtlasConstants {
209234
UserOperation memory userOp,
210235
SolverOperation[] memory solverOps,
211236
DAppOperation memory dAppOp,
212-
uint256 estGasLimit
237+
uint256 metacallExecutionGas,
238+
uint256 metacallCalldataGas
213239
)
214240
internal
215241
returns (Result result, uint256 additionalErrorCode)
216242
{
217-
if (gasleft() < estGasLimit + _SIM_GAS_BEFORE_METACALL) {
218-
revert InsufficientGasForMetacallSimulation(estGasLimit, estGasLimit + _SIM_GAS_SUGGESTED_BUFFER);
243+
// Calculate the minimum gas left at this point, given that the Atlas `metacall()` should start with
244+
// approximately `metacallExecutionGas`, and that there are still 2x external calls to be made before that
245+
// point:
246+
// - First in the `metacallSimulation()` call below.
247+
// - Second when the `metacallSimulation()` function calls `metacall()` in Atlas.
248+
//
249+
// For each external call, a max of 63/64 of the gas left is forwarded.
250+
// Therefore we should have at least [metacallExecutionGas * (64/63) ^ 2] at this point to ensure there will be
251+
// enough gas at the start of the `metacall()`
252+
uint256 minGasLeft = metacallExecutionGas * _MIN_GAS_SCALING_FACTOR / _SCALE + _ERROR_CATCHER_GAS_BUFFER;
253+
uint256 gasLeft = gasleft();
254+
255+
if (gasLeft < minGasLeft) {
256+
revert InsufficientGasForMetacallSimulation(
257+
gasLeft, // The gas left at this point that was insufficient to pass the check
258+
metacallExecutionGas + metacallCalldataGas, // Suggested gas limit for a real metacall
259+
_SIM_ENTRYPOINT_GAS_BUFFER + minGasLeft + metacallCalldataGas // Suggested gas limit for a sim call
260+
);
219261
}
220262

221-
try this.metacallSimulation{ value: userOp.value }(userOp, solverOps, dAppOp, estGasLimit) {
263+
try this.metacallSimulation{ value: userOp.value }(userOp, solverOps, dAppOp, metacallExecutionGas) {
222264
revert Unreachable();
223265
} catch (bytes memory revertData) {
224266
bytes4 errorSwitch = bytes4(revertData);
@@ -261,14 +303,20 @@ contract Simulator is AtlasErrors, AtlasConstants {
261303
UserOperation calldata userOp,
262304
SolverOperation[] calldata solverOps,
263305
DAppOperation calldata dAppOp,
264-
uint256 estGasLimit
306+
uint256 metacallExecutionGas
265307
)
266308
external
267309
payable
268310
{
269311
if (msg.sender != address(this)) revert InvalidEntryFunction();
312+
313+
// In real Atlas metacalls, when the caller is an EOA, it should include the suggested calldata gas in the gas
314+
// limit. However, in as this is a Simulator call, that calldata gas has already been deducted in the initial
315+
// `simUserOperation()` or `simSolverCall()` call. As such, we set the metacall gas limit here to just the
316+
// suggested execution gas.
317+
270318
bool auctionWon =
271-
IAtlas(atlas).metacall{ value: msg.value, gas: estGasLimit }(userOp, solverOps, dAppOp, address(0));
319+
IAtlas(atlas).metacall{ value: msg.value, gas: metacallExecutionGas }(userOp, solverOps, dAppOp, address(0));
272320

273321
// If multipleSuccessfulSolvers = true, metacall always returns auctionWon = false, even if there were some
274322
// successful solvers. So we always revert with SimulationPassed here if multipleSuccessfulSolvers = true.

src/contracts/types/AtlasErrors.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ contract AtlasErrors {
3838
error SolverSimFail(uint256 solverOutcomeResult); // uint param is result returned in `verifySolverOp`
3939
error AllocateValueSimFail();
4040
error ValidCalls(ValidCallsResult);
41-
error InsufficientGasForMetacallSimulation(uint256 estimatedMetacallGas, uint256 suggestedSimGas);
41+
error InsufficientGasForMetacallSimulation(uint256 gasLeft, uint256 estimatedMetacallGas, uint256 suggestedSimGas);
4242

4343
// Execution Environment
4444
error InvalidUser();

0 commit comments

Comments
 (0)