Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/atlas/common/ExecutionEnvironment.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Base } from "./ExecutionBase.sol";

import { IAtlas } from "../interfaces/IAtlas.sol";
import { ISolverContract } from "../interfaces/ISolverContract.sol";
import { IDAppControl } from "../interfaces/IDAppControl.sol";
import { AtlasErrors } from "../types/AtlasErrors.sol";
import { CallBits } from "../libraries/CallBits.sol";
Expand Down
2 changes: 2 additions & 0 deletions src/atlas/core/AtlETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ abstract contract AtlETH is Permit69 {
address simulator,
address initialSurchargeRecipient,
address l2GasCalculator,
address taskManager,
address shMonad,
uint64 shMonadPolicyID
)
Expand All @@ -27,6 +28,7 @@ abstract contract AtlETH is Permit69 {
simulator,
initialSurchargeRecipient,
l2GasCalculator,
taskManager,
shMonad,
shMonadPolicyID
)
Expand Down
17 changes: 14 additions & 3 deletions src/atlas/core/Atlas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@ import "../types/ValidCalls.sol";
import { CallBits } from "../libraries/CallBits.sol";
import { SafetyBits } from "../libraries/SafetyBits.sol";
import { GasAccLib, GasLedger } from "../libraries/GasAccLib.sol";
import { IL2GasCalculator } from "../interfaces/IL2GasCalculator.sol";
import { IDAppControl } from "../interfaces/IDAppControl.sol";

/// @title Atlas V1.6
/// @title Atlas V1.6.3
/// @author FastLane Labs
/// @notice The Execution Abstraction protocol.
contract Atlas is Escrow, Factory {
Expand All @@ -39,6 +37,7 @@ contract Atlas is Escrow, Factory {
address initialSurchargeRecipient,
address l2GasCalculator,
address factoryLib,
address taskManager,
address shMonad,
uint64 shMonadPolicyID
)
Expand All @@ -48,6 +47,7 @@ contract Atlas is Escrow, Factory {
simulator,
initialSurchargeRecipient,
l2GasCalculator,
taskManager,
shMonad,
shMonadPolicyID
)
Expand Down Expand Up @@ -119,6 +119,8 @@ contract Atlas is Escrow, Factory {

// Gracefully return for results that need nonces to be stored and prevent replay attacks
if (uint8(_validCallsResult) >= _GRACEFUL_RETURN_THRESHOLD && !_dConfig.callConfig.allowsReuseUserOps()) {
// Refund the bundler if they sent any msg.value, then return false and end early.
if (msg.value != 0) SafeTransferLib.safeTransferETH(msg.sender, msg.value);
return false;
}

Expand Down Expand Up @@ -200,6 +202,15 @@ contract Atlas is Escrow, Factory {
emit MetacallResult(msg.sender, userOp.from, false, 0, 0);
}

// Use any spare gas to generate additional revenue / fees for the intended gas refund beneficiary by executing
// tasks on the TaskManager.
uint256 _feesSharesEarned = _allocateGasToTaskManager(address(this), 25_000);

// Boost yield with the fees earned from executing tasks on the TaskManager
if (_feesSharesEarned > 0) {
SHMONAD.boostYield(_feesSharesEarned, address(this));
}

// The environment lock is explicitly released here to allow multiple (sequential, not nested) metacalls in a
// single transaction.
_releaseLock();
Expand Down
35 changes: 20 additions & 15 deletions src/atlas/core/AtlasVerification.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ contract AtlasVerification is EIP712, NonceManager, DAppIntegration {
address atlas,
address l2GasCalculator
)
EIP712("AtlasVerification", "1.6")
EIP712("AtlasVerification", "1.6.3")
DAppIntegration(atlas, l2GasCalculator)
{ }

Expand Down Expand Up @@ -679,38 +679,43 @@ contract AtlasVerification is EIP712, NonceManager, DAppIntegration {
}

allSolversCalldataGas =
GasAccLib.calldataGas(solverDataLenSum + (_SOLVER_OP_BASE_CALLDATA * solverOps.length), L2_GAS_CALCULATOR);
GasAccLib.calldataGas(solverDataLenSum + (_SOLVER_OP_STATIC_LENGTH * solverOps.length), L2_GAS_CALCULATOR);

// NOTE: On Monad, _FIXED_GAS_OFFSET is added here to ensure we have enough gas for the whole metacall upfront.
// But it is not added to the _gasMarker tracker at the start of metacall(), because winning solver must pay all
// remaining gas in _settle() anyway - i.e. they do not get a rebate of gasleft().
uint256 metacallExecutionGas = _BASE_TX_GAS_USED + AccountingMath._FIXED_GAS_OFFSET + userOpGas
+ dConfig.dappGasLimit + allSolversExecutionGas;
uint256 metacallExecutionGas = _BASE_TX_GAS_USED + _PRE_EXECUTE_METACALL_GAS + _POST_SETTLE_METACALL_GAS
+ userOpGas + dConfig.dappGasLimit + allSolversExecutionGas;
// NOTE: On Monad, the _EXECUTE_SOLVER_OVERHEAD per solverOp is not included in the suggested gas limit.

// In both exPostBids and normal bid modes, solvers pay for their own execution gas.
allSolversGasLimit = allSolversExecutionGas;

if (dConfig.callConfig.exPostBids()) {
// Add extra execution gas for bid-finding loop of each solverOp
bidFindOverhead = (solverOpsLen * _BID_FIND_OVERHEAD) + allSolversExecutionGas;
bidFindOverhead = solverOpsLen * _BID_FIND_OVERHEAD + allSolversExecutionGas;
metacallExecutionGas += bidFindOverhead;
// NOTE: allSolversGasLimit excludes calldata in exPostBids mode.
} else {
// Solvers only pay for their calldata if exPostBids = false
allSolversGasLimit += allSolversCalldataGas;
}

uint256 _execGasUpperTolerance = _UPPER_BASE_EXEC_GAS_TOLERANCE + solverOpsLen * _TOLERANCE_PER_SOLVER;
uint256 _execGasLowerTolerance = _LOWER_BASE_EXEC_GAS_TOLERANCE + solverOpsLen * _TOLERANCE_PER_SOLVER;
// If checkMetacallGasLimit is enabled, verify that the execution gas measured by gasleft() at the start of the
// metacall is in line with the expected gas limit, based on the userOp, solverOps, and dAppOp.
if (dConfig.callConfig.checkMetacallGasLimit()) {
uint256 _execGasUpperTolerance = _UPPER_BASE_EXEC_GAS_TOLERANCE + solverOpsLen * _TOLERANCE_PER_SOLVER;
uint256 _execGasLowerTolerance = _LOWER_BASE_EXEC_GAS_TOLERANCE + solverOpsLen * _TOLERANCE_PER_SOLVER;

// Gas limit set by the bundler cannot be too high or too low. Use Simulator contract to estimate gas limit.
// If gas limit is too low, the bonded balance threshold checked may not cover all gas reimbursements.
if (metacallGasLeft < metacallExecutionGas - _execGasLowerTolerance) {
verifyCallsResult = ValidCallsResult.MetacallGasLimitTooLow;
}
// If gas limit is too high, the bonded balance threshold checked could unexpectedly price out solvers.
if (metacallGasLeft > metacallExecutionGas + _execGasUpperTolerance) {
verifyCallsResult = ValidCallsResult.MetacallGasLimitTooHigh;
// Gas limit set by the bundler cannot be too high or too low. Use Simulator contract to estimate gas limit.
// If gas limit is too low, the bonded balance threshold checked may not cover all gas reimbursements.
if (metacallGasLeft < metacallExecutionGas - _execGasLowerTolerance) {
verifyCallsResult = ValidCallsResult.MetacallGasLimitTooLow;
}
// If gas limit is too high, the bonded balance threshold checked could unexpectedly price out solvers.
if (metacallGasLeft > metacallExecutionGas + _execGasUpperTolerance) {
verifyCallsResult = ValidCallsResult.MetacallGasLimitTooHigh;
}
}

return (verifyCallsResult, allSolversGasLimit, allSolversCalldataGas, bidFindOverhead);
Expand Down
32 changes: 28 additions & 4 deletions src/atlas/core/Escrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

import { AtlETH } from "./AtlETH.sol";
import { IExecutionEnvironment } from "../interfaces/IExecutionEnvironment.sol";
import { IAtlas } from "../interfaces/IAtlas.sol";
import { ISolverContract } from "../interfaces/ISolverContract.sol";
import { IAtlasVerification } from "../interfaces/IAtlasVerification.sol";
import { IDAppControl } from "../interfaces/IDAppControl.sol";

import { SafeCall } from "../libraries/SafeCall/SafeCall.sol";
import { EscrowBits } from "../libraries/EscrowBits.sol";
Expand Down Expand Up @@ -43,6 +41,7 @@ abstract contract Escrow is AtlETH {
address simulator,
address initialSurchargeRecipient,
address l2GasCalculator,
address taskManager,
address shMonad,
uint64 shMonadPolicyID
)
Expand All @@ -52,6 +51,7 @@ abstract contract Escrow is AtlETH {
simulator,
initialSurchargeRecipient,
l2GasCalculator,
taskManager,
shMonad,
shMonadPolicyID
)
Expand Down Expand Up @@ -220,7 +220,7 @@ abstract contract Escrow is AtlETH {

if (_result.executionSuccessful()) {
// Logic done above `_handleSolverFailAccounting()` is to charge solver for gas used here
ctx.solverOutcome = uint24(_result);
ctx.solverOutcome = _result.toUint24();

// First successful solver call that paid what it bid
emit SolverTxResult(
Expand Down Expand Up @@ -256,7 +256,7 @@ abstract contract Escrow is AtlETH {
}

// If we reach this point, the solver call did not execute successfully.
ctx.solverOutcome = uint24(_result);
ctx.solverOutcome = _result.toUint24();

emit SolverTxResult(
solverOp.solver,
Expand Down Expand Up @@ -756,5 +756,29 @@ abstract contract Escrow is AtlETH {
ctx.dappGasLeft -= uint32(_gasUsed);
}

/// @notice Allocates any spare gas remaining to executing tasks on the TaskManager, which will generate fees.
/// @dev This is typically called outside of the try/catch and must therefore be careful not to revert.
/// @param feeBeneficiary The recipient of any fees generated from executing tasks.
/// @param targetGasReserve The remaining gas necessary for the calling (outer) function to complete.
/// @return feesCollected The fees generated from executing tasks.
function _allocateGasToTaskManager(address feeBeneficiary, uint256 targetGasReserve) internal returns (uint256) {
// Skip if TASK_MANAGER is not set (e.g., in tests)
if (address(TASK_MANAGER) == address(0)) {
return 0;
}

// Only call if we have enough gas for a full task
if (gasleft() * 63 / 64 < 135_000 + targetGasReserve) {
return 0;
}

// Under no circumstance should we allow a revert here to bubble up
try TASK_MANAGER.executeTasks(feeBeneficiary, targetGasReserve) returns (uint256 _feesEarned) {
return _feesEarned;
} catch {
return 0;
}
}

receive() external payable { }
}
4 changes: 2 additions & 2 deletions src/atlas/core/GasAccounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { CallBits } from "../libraries/CallBits.sol";
import { AccountingMath } from "../libraries/AccountingMath.sol";
import { GasAccLib, GasLedger, BorrowsLedger } from "../libraries/GasAccLib.sol";
import { SolverOperation } from "../types/SolverOperation.sol";
import { DAppConfig } from "../types/ConfigTypes.sol";
import { IL2GasCalculator } from "../interfaces/IL2GasCalculator.sol";
import "../types/EscrowTypes.sol";
import "../types/LockTypes.sol";

Expand All @@ -36,6 +34,7 @@ abstract contract GasAccounting is SafetyLocks {
address simulator,
address initialSurchargeRecipient,
address l2GasCalculator,
address taskManager,
address shMonad,
uint64 shMonadPolicyID
)
Expand All @@ -45,6 +44,7 @@ abstract contract GasAccounting is SafetyLocks {
simulator,
initialSurchargeRecipient,
l2GasCalculator,
taskManager,
shMonad,
shMonadPolicyID
)
Expand Down
5 changes: 2 additions & 3 deletions src/atlas/core/Permit69.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@
pragma solidity 0.8.28;

import { SafeTransferLib } from "@solady/utils/SafeTransferLib.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { GasAccounting } from "./GasAccounting.sol";

import { SAFE_USER_TRANSFER, SAFE_DAPP_TRANSFER } from "../libraries/SafetyBits.sol";
import "../types/LockTypes.sol";
import "../types/EscrowTypes.sol";

// NOTE: Permit69 only works inside of the Atlas environment - specifically
// inside of the custom ExecutionEnvironments that each user deploys when
Expand All @@ -31,6 +28,7 @@ abstract contract Permit69 is GasAccounting {
address simulator,
address initialSurchargeRecipient,
address l2GasCalculator,
address taskManager,
address shMonad,
uint64 shMonadPolicyID
)
Expand All @@ -40,6 +38,7 @@ abstract contract Permit69 is GasAccounting {
simulator,
initialSurchargeRecipient,
l2GasCalculator,
taskManager,
shMonad,
shMonadPolicyID
)
Expand Down
5 changes: 2 additions & 3 deletions src/atlas/core/SafetyLocks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ pragma solidity 0.8.28;

import { Storage } from "./Storage.sol";
import { CallBits } from "../libraries/CallBits.sol";
import "../types/SolverOperation.sol";
import "../types/UserOperation.sol";
import "../types/ConfigTypes.sol";
import "../types/EscrowTypes.sol";
import "../types/LockTypes.sol";

/// @title SafetyLocks
Expand All @@ -22,6 +19,7 @@ abstract contract SafetyLocks is Storage {
address simulator,
address initialSurchargeRecipient,
address l2GasCalculator,
address taskManager,
address shMonad,
uint64 shMonadPolicyID
)
Expand All @@ -31,6 +29,7 @@ abstract contract SafetyLocks is Storage {
simulator,
initialSurchargeRecipient,
l2GasCalculator,
taskManager,
shMonad,
shMonadPolicyID
)
Expand Down
6 changes: 4 additions & 2 deletions src/atlas/core/Storage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
pragma solidity 0.8.28;

import "../types/EscrowTypes.sol";
import "../types/LockTypes.sol";
import "../libraries/AccountingMath.sol";

import { AtlasEvents } from "../types/AtlasEvents.sol";
import { AtlasErrors } from "../types/AtlasErrors.sol";
import { AtlasConstants } from "../types/AtlasConstants.sol";
import { IAtlasVerification } from "../interfaces/IAtlasVerification.sol";
import { IShMonad } from "../../shmonad/interfaces/IShMonad.sol";
import { ITaskManager } from "../../task-manager/interfaces/ITaskManager.sol";

/// @title Storage
/// @author FastLane Labs
Expand All @@ -20,11 +20,11 @@ abstract contract Storage is AtlasEvents, AtlasErrors, AtlasConstants {
address public immutable L2_GAS_CALCULATOR;

IShMonad public immutable SHMONAD;
ITaskManager public immutable TASK_MANAGER;
uint64 public immutable POLICY_ID;

// Gas Accounting public constants
uint256 public constant SCALE = AccountingMath._SCALE;
uint256 public constant FIXED_GAS_OFFSET = AccountingMath._FIXED_GAS_OFFSET;

// Transient storage slots
uint256 internal transient t_lock; // contains activeAddress, callConfig, and phase
Expand Down Expand Up @@ -54,6 +54,7 @@ abstract contract Storage is AtlasEvents, AtlasErrors, AtlasConstants {
address simulator,
address initialSurchargeRecipient,
address l2GasCalculator,
address taskManager,
address shMonad,
uint64 shMonadPolicyID
)
Expand All @@ -64,6 +65,7 @@ abstract contract Storage is AtlasEvents, AtlasErrors, AtlasConstants {
SIMULATOR = simulator;
L2_GAS_CALCULATOR = l2GasCalculator;
POLICY_ID = shMonadPolicyID;
TASK_MANAGER = ITaskManager(taskManager);

// Check Atlas gas surcharge fits in 24 bits
if(atlasSurchargeRate > type(uint24).max) {
Expand Down
1 change: 0 additions & 1 deletion src/atlas/dapp/ControlTemplate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pragma solidity 0.8.28;

import "../types/SolverOperation.sol";
import "../types/UserOperation.sol";
import "../types/ConfigTypes.sol";
import { AtlasErrors } from "../types/AtlasErrors.sol";

abstract contract DAppControlTemplate {
Expand Down
Loading