From a0dc8fff4c3b090c33c6f121597f872d227537b7 Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:28:37 +0200 Subject: [PATCH 01/21] fix: conditionally forward `returnData` to solvers --- src/contracts/atlas/Escrow.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/contracts/atlas/Escrow.sol b/src/contracts/atlas/Escrow.sol index 27c633fb..c2ae85de 100644 --- a/src/contracts/atlas/Escrow.sol +++ b/src/contracts/atlas/Escrow.sol @@ -609,8 +609,8 @@ abstract contract Escrow is AtlETH { solverOp.bidToken, bidAmount, solverOp.data, - // Only pass the returnData to solver if it came from userOp call and not from preOps call. - _activeCallConfig().needsUserReturnData() ? returnData : new bytes(0) + // Only pass the returnData (either from userOp or preOps) if the dApp requires it + _activeCallConfig().forwardReturnData() ? returnData : new bytes(0) ) ) ); From 4c9c5e3f93a47b97697841a65fecb73a7e69e6a5 Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Thu, 5 Sep 2024 10:48:55 +0200 Subject: [PATCH 02/21] chore: update Atlas license --- LICENSE | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/LICENSE b/LICENSE index 3f19c92f..e4058c4e 100644 --- a/LICENSE +++ b/LICENSE @@ -9,14 +9,14 @@ Parameters Licensor: Fastlane Labs -Licensed Work: FastLane Protocol - The Licensed Work is (c) 2023 Fastlane Labs +Licensed Work: Atlas Protocol + The Licensed Work is (c) 2024 Fastlane Labs Additional Use Grant: Any uses listed and defined at - fastlane-protocol-license.fastlane.finance + atlas-license.fastlane.finance -Change Date: The earlier of 2025-07-01 or a date specified at - fastlane-protocol-license.fastlane.finance +Change Date: The earlier of 2026-07-01 or a date specified at + atlas-license.fastlane.finance Change License: GNU General Public License v2.0 or later From a448c9864a3a6c22d400ab37c7c45e6349f074da Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:02:12 +0200 Subject: [PATCH 03/21] chore: update Atlas license --- LICENSE | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/LICENSE b/LICENSE index e4058c4e..11b0fd73 100644 --- a/LICENSE +++ b/LICENSE @@ -12,11 +12,7 @@ Licensor: Fastlane Labs Licensed Work: Atlas Protocol The Licensed Work is (c) 2024 Fastlane Labs -Additional Use Grant: Any uses listed and defined at - atlas-license.fastlane.finance - -Change Date: The earlier of 2026-07-01 or a date specified at - atlas-license.fastlane.finance +Change Date: 2026-07-01 Change License: GNU General Public License v2.0 or later From cf271a6c6535f9bfd46d037f0fb4840b29d5b19a Mon Sep 17 00:00:00 2001 From: jj1980a Date: Sat, 7 Sep 2024 10:35:52 +0400 Subject: [PATCH 04/21] base L2 gas calculator contract --- lib/openzeppelin-contracts | 2 +- .../gasCalculator/BaseGasCalculator.sol | 44 +++++++++++++++++++ src/contracts/interfaces/IL2GasCalculator.sol | 2 +- src/contracts/types/SolverOperation.sol | 4 +- 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/contracts/gasCalculator/BaseGasCalculator.sol diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index dbb6104c..bd325d56 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 +Subproject commit bd325d56b4c62c9c5c1aff048c37c6bb18ac0290 diff --git a/src/contracts/gasCalculator/BaseGasCalculator.sol b/src/contracts/gasCalculator/BaseGasCalculator.sol new file mode 100644 index 00000000..bcff04c7 --- /dev/null +++ b/src/contracts/gasCalculator/BaseGasCalculator.sol @@ -0,0 +1,44 @@ +//SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.25; + +import { IL2GasCalculator } from "src/contracts/interfaces/IL2GasCalculator.sol"; + +/// @notice Implementation: +/// https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L2/GasPriceOracle.sol +/// @notice Deployment on Base: https://basescan.org/address/0x420000000000000000000000000000000000000f +interface IGasPriceOracle { + function getL1FeeUpperBound(uint256 _unsignedTxSize) external view returns (uint256); +} + +contract BaseGasCalculator is IL2GasCalculator { + uint256 public constant _CALLDATA_LENGTH_PREMIUM = 32; // TODO: copied value from Atlas, should it be different for + // Base? + uint256 internal constant _BASE_TRANSACTION_GAS_USED = 21_000; + + address public immutable gasPriceOracle; + + constructor(address _gasPriceOracle) { + gasPriceOracle = _gasPriceOracle; + } + + /// @notice Calculate the cost of calldata in ETH on a L2 with a different fee structure than mainnet + /// @param calldataLength The length of the calldata in bytes + function getCalldataCost(uint256 calldataLength) external view override returns (uint256 calldataCost) { + // `getL1FeeUpperBound` returns the upper bound of the L1 fee in wei. It expects an unsigned transaction size in + // bytes, *not calldata length only*, which makes this function a rough estimate. + + // Base execution cost. + calldataCost = calldataLength * _CALLDATA_LENGTH_PREMIUM * tx.gasprice; + + // L1 data cost. + // `getL1FeeUpperBound` adds 68 to the size because it expects an unsigned transaction size. + // Remove 68 to the length to account for this. + calldataCost += IGasPriceOracle(gasPriceOracle).getL1FeeUpperBound(calldataLength - 68); + } + + /// @notice Gets the cost of initial gas used for a transaction with a different calldata fee than mainnet + /// @param calldataLength The length of the calldata in bytes + function initialGasUsed(uint256 calldataLength) external pure override returns (uint256 gasUsed) { + return _BASE_TRANSACTION_GAS_USED + (calldataLength * _CALLDATA_LENGTH_PREMIUM); + } +} diff --git a/src/contracts/interfaces/IL2GasCalculator.sol b/src/contracts/interfaces/IL2GasCalculator.sol index 33f7cc0b..387aab8e 100644 --- a/src/contracts/interfaces/IL2GasCalculator.sol +++ b/src/contracts/interfaces/IL2GasCalculator.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.25; interface IL2GasCalculator { /// @notice Calculate the cost of calldata in ETH on a L2 with a different fee structure than mainnet - function getCalldataCost(uint256 length) external view returns (uint256 calldataCostETH); + function getCalldataCost(uint256 calldataLength) external view returns (uint256 calldataCost); /// @notice Gets the cost of initial gas used for a transaction with a different calldata fee than mainnet function initialGasUsed(uint256 calldataLength) external view returns (uint256 gasUsed); } diff --git a/src/contracts/types/SolverOperation.sol b/src/contracts/types/SolverOperation.sol index 7e6bf94b..ffd9392e 100644 --- a/src/contracts/types/SolverOperation.sol +++ b/src/contracts/types/SolverOperation.sol @@ -6,8 +6,8 @@ bytes32 constant SOLVER_TYPEHASH = keccak256( ); // NOTE: The calldata length of this SolverOperation struct is 608 bytes when the `data` field is excluded. This value -// is stored in the `_SOLVER_OP_BASE_CALLDATA` constant in Storage.sol and must be kept up-to-date with any changes to -// this struct. +// is stored in the `_SOLVER_OP_BASE_CALLDATA` constant in AtlasConstants.sol and must be kept up-to-date with any +// changes to this struct. struct SolverOperation { address from; // Solver address address to; // Atlas address From 0eaeb46983fa41f13c35a03107c4f5f51b55d94e Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:40:16 +0200 Subject: [PATCH 05/21] chore: change Simulator imports to absolute path --- src/contracts/helpers/Simulator.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/contracts/helpers/Simulator.sol b/src/contracts/helpers/Simulator.sol index af3620bd..34d9f3f3 100644 --- a/src/contracts/helpers/Simulator.sol +++ b/src/contracts/helpers/Simulator.sol @@ -1,19 +1,19 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; -import { IAtlas } from "../interfaces/IAtlas.sol"; +import { IAtlas } from "src/contracts/interfaces/IAtlas.sol"; import { AtlasErrors } from "src/contracts/types/AtlasErrors.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; -import "../types/SolverOperation.sol"; -import "../types/UserOperation.sol"; -import "../types/LockTypes.sol"; -import "../types/DAppOperation.sol"; -import "../types/ConfigTypes.sol"; -import "../types/ValidCalls.sol"; -import "../types/EscrowTypes.sol"; +import "src/contracts/types/SolverOperation.sol"; +import "src/contracts/types/UserOperation.sol"; +import "src/contracts/types/LockTypes.sol"; +import "src/contracts/types/DAppOperation.sol"; +import "src/contracts/types/ConfigTypes.sol"; +import "src/contracts/types/ValidCalls.sol"; +import "src/contracts/types/EscrowTypes.sol"; import { Result } from "src/contracts/interfaces/ISimulator.sol"; From 120b911a0813406075dd734f1bfd6404157fac6b Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:46:32 +0200 Subject: [PATCH 06/21] chore: make all imports absolute path --- src/contracts/atlas/Atlas.sol | 3 +-- src/contracts/atlas/Escrow.sol | 2 +- src/contracts/dapp/ControlTemplate.sol | 6 +++--- src/contracts/helpers/Sorter.sol | 17 +++++++++-------- src/contracts/helpers/TxBuilder.sol | 18 +++++++++--------- .../interfaces/IAtlasVerification.sol | 12 ++++++------ src/contracts/interfaces/IDAppControl.sol | 6 +++--- .../interfaces/IExecutionEnvironment.sol | 8 ++++---- src/contracts/interfaces/ISimulator.sol | 6 +++--- src/contracts/libraries/CallBits.sol | 4 ++-- src/contracts/solver/src/TestSolver.sol | 2 +- src/contracts/solver/src/TestSolverExPost.sol | 2 +- test/helpers/DummyDAppControlBuilder.sol | 2 +- test/libraries/CallBits.t.sol | 2 +- test/libraries/CallVerification.t.sol | 2 +- test/libraries/SafetyBits.t.sol | 2 +- 16 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/contracts/atlas/Atlas.sol b/src/contracts/atlas/Atlas.sol index e709fa2f..c17d0825 100644 --- a/src/contracts/atlas/Atlas.sol +++ b/src/contracts/atlas/Atlas.sol @@ -4,8 +4,6 @@ pragma solidity 0.8.25; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { LibSort } from "solady/utils/LibSort.sol"; -import { IDAppControl } from "../interfaces/IDAppControl.sol"; - import { Escrow } from "./Escrow.sol"; import { Factory } from "./Factory.sol"; @@ -19,6 +17,7 @@ import "src/contracts/types/ValidCalls.sol"; import { CallBits } from "src/contracts/libraries/CallBits.sol"; import { SafetyBits } from "src/contracts/libraries/SafetyBits.sol"; import { IL2GasCalculator } from "src/contracts/interfaces/IL2GasCalculator.sol"; +import { IDAppControl } from "src/contracts/interfaces/IDAppControl.sol"; /// @title Atlas V1 /// @author FastLane Labs diff --git a/src/contracts/atlas/Escrow.sol b/src/contracts/atlas/Escrow.sol index 27c633fb..47ce9a5e 100644 --- a/src/contracts/atlas/Escrow.sol +++ b/src/contracts/atlas/Escrow.sol @@ -6,7 +6,7 @@ import { IExecutionEnvironment } from "src/contracts/interfaces/IExecutionEnviro import { IAtlas } from "src/contracts/interfaces/IAtlas.sol"; import { ISolverContract } from "src/contracts/interfaces/ISolverContract.sol"; import { IAtlasVerification } from "src/contracts/interfaces/IAtlasVerification.sol"; -import { IDAppControl } from "../interfaces/IDAppControl.sol"; +import { IDAppControl } from "src/contracts/interfaces/IDAppControl.sol"; import { SafeCall } from "src/contracts/libraries/SafeCall/SafeCall.sol"; import { EscrowBits } from "src/contracts/libraries/EscrowBits.sol"; diff --git a/src/contracts/dapp/ControlTemplate.sol b/src/contracts/dapp/ControlTemplate.sol index 2d76b212..df402abc 100644 --- a/src/contracts/dapp/ControlTemplate.sol +++ b/src/contracts/dapp/ControlTemplate.sol @@ -1,9 +1,9 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; -import "../types/SolverOperation.sol"; -import "../types/UserOperation.sol"; -import "../types/ConfigTypes.sol"; +import "src/contracts/types/SolverOperation.sol"; +import "src/contracts/types/UserOperation.sol"; +import "src/contracts/types/ConfigTypes.sol"; import { AtlasErrors } from "src/contracts/types/AtlasErrors.sol"; abstract contract DAppControlTemplate { diff --git a/src/contracts/helpers/Sorter.sol b/src/contracts/helpers/Sorter.sol index 60225cc2..face331c 100644 --- a/src/contracts/helpers/Sorter.sol +++ b/src/contracts/helpers/Sorter.sol @@ -1,17 +1,18 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; -import { IAtlas } from "../interfaces/IAtlas.sol"; -import { IDAppControl } from "../interfaces/IDAppControl.sol"; +import { IAtlas } from "src/contracts/interfaces/IAtlas.sol"; +import { IDAppControl } from "src/contracts/interfaces/IDAppControl.sol"; +import { IAtlasVerification } from "src/contracts/interfaces/IAtlasVerification.sol"; + import { CallBits } from "src/contracts/libraries/CallBits.sol"; import { AccountingMath } from "src/contracts/libraries/AccountingMath.sol"; -import { CallVerification } from "../libraries/CallVerification.sol"; -import { IAtlasVerification } from "../interfaces/IAtlasVerification.sol"; -import { AtlasConstants } from "../types/AtlasConstants.sol"; +import { CallVerification } from "src/contracts/libraries/CallVerification.sol"; +import { AtlasConstants } from "src/contracts/types/AtlasConstants.sol"; -import "../types/SolverOperation.sol"; -import "../types/UserOperation.sol"; -import "../types/ConfigTypes.sol"; +import "src/contracts/types/SolverOperation.sol"; +import "src/contracts/types/UserOperation.sol"; +import "src/contracts/types/ConfigTypes.sol"; contract Sorter is AtlasConstants { using CallBits for uint32; diff --git a/src/contracts/helpers/TxBuilder.sol b/src/contracts/helpers/TxBuilder.sol index dea57c90..3e70fdc2 100644 --- a/src/contracts/helpers/TxBuilder.sol +++ b/src/contracts/helpers/TxBuilder.sol @@ -1,17 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import { IDAppControl } from "../interfaces/IDAppControl.sol"; -import { IAtlas } from "../interfaces/IAtlas.sol"; -import { IAtlasVerification } from "../interfaces/IAtlasVerification.sol"; +import { IDAppControl } from "src/contracts/interfaces/IDAppControl.sol"; +import { IAtlas } from "src/contracts/interfaces/IAtlas.sol"; +import { IAtlasVerification } from "src/contracts/interfaces/IAtlasVerification.sol"; -import "../types/SolverOperation.sol"; -import "../types/UserOperation.sol"; -import "../types/ConfigTypes.sol"; -import "../types/DAppOperation.sol"; +import "src/contracts/types/SolverOperation.sol"; +import "src/contracts/types/UserOperation.sol"; +import "src/contracts/types/ConfigTypes.sol"; +import "src/contracts/types/DAppOperation.sol"; -import { CallVerification } from "../libraries/CallVerification.sol"; -import { CallBits } from "../libraries/CallBits.sol"; +import { CallVerification } from "src/contracts/libraries/CallVerification.sol"; +import { CallBits } from "src/contracts/libraries/CallBits.sol"; import "forge-std/Test.sol"; diff --git a/src/contracts/interfaces/IAtlasVerification.sol b/src/contracts/interfaces/IAtlasVerification.sol index ed4f050f..cdd1eef7 100644 --- a/src/contracts/interfaces/IAtlasVerification.sol +++ b/src/contracts/interfaces/IAtlasVerification.sol @@ -1,12 +1,12 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; -import "../types/UserOperation.sol"; -import "../types/ConfigTypes.sol"; -import "../types/DAppOperation.sol"; -import "../types/SolverOperation.sol"; -import "../types/EscrowTypes.sol"; -import "../types/ValidCalls.sol"; +import "src/contracts/types/UserOperation.sol"; +import "src/contracts/types/ConfigTypes.sol"; +import "src/contracts/types/DAppOperation.sol"; +import "src/contracts/types/SolverOperation.sol"; +import "src/contracts/types/EscrowTypes.sol"; +import "src/contracts/types/ValidCalls.sol"; interface IAtlasVerification { // AtlasVerification.sol diff --git a/src/contracts/interfaces/IDAppControl.sol b/src/contracts/interfaces/IDAppControl.sol index e3be1417..25f699e8 100644 --- a/src/contracts/interfaces/IDAppControl.sol +++ b/src/contracts/interfaces/IDAppControl.sol @@ -1,9 +1,9 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; -import "../types/UserOperation.sol"; -import "../types/SolverOperation.sol"; -import "../types/ConfigTypes.sol"; +import "src/contracts/types/UserOperation.sol"; +import "src/contracts/types/SolverOperation.sol"; +import "src/contracts/types/ConfigTypes.sol"; interface IDAppControl { function preOpsCall(UserOperation calldata userOp) external payable returns (bytes memory); diff --git a/src/contracts/interfaces/IExecutionEnvironment.sol b/src/contracts/interfaces/IExecutionEnvironment.sol index 9dcdee96..17d7ecb5 100644 --- a/src/contracts/interfaces/IExecutionEnvironment.sol +++ b/src/contracts/interfaces/IExecutionEnvironment.sol @@ -1,10 +1,10 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; -import "../types/SolverOperation.sol"; -import "../types/UserOperation.sol"; -import "../types/ConfigTypes.sol"; -import "../types/EscrowTypes.sol"; +import "src/contracts/types/SolverOperation.sol"; +import "src/contracts/types/UserOperation.sol"; +import "src/contracts/types/ConfigTypes.sol"; +import "src/contracts/types/EscrowTypes.sol"; interface IExecutionEnvironment { function preOpsWrapper(UserOperation calldata userOp) external returns (bytes memory preOpsData); diff --git a/src/contracts/interfaces/ISimulator.sol b/src/contracts/interfaces/ISimulator.sol index 97f5852f..8a92a2ce 100644 --- a/src/contracts/interfaces/ISimulator.sol +++ b/src/contracts/interfaces/ISimulator.sol @@ -1,9 +1,9 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; -import "../types/SolverOperation.sol"; -import "../types/UserOperation.sol"; -import "../types/ConfigTypes.sol"; +import "src/contracts/types/SolverOperation.sol"; +import "src/contracts/types/UserOperation.sol"; +import "src/contracts/types/ConfigTypes.sol"; import "src/contracts/types/DAppOperation.sol"; enum Result { diff --git a/src/contracts/libraries/CallBits.sol b/src/contracts/libraries/CallBits.sol index f5c1d0a6..62b46a46 100644 --- a/src/contracts/libraries/CallBits.sol +++ b/src/contracts/libraries/CallBits.sol @@ -1,9 +1,9 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; -import { IDAppControl } from "../interfaces/IDAppControl.sol"; +import { IDAppControl } from "src/contracts/interfaces/IDAppControl.sol"; -import "../types/ConfigTypes.sol"; +import "src/contracts/types/ConfigTypes.sol"; library CallBits { uint32 internal constant _ONE = uint32(1); diff --git a/src/contracts/solver/src/TestSolver.sol b/src/contracts/solver/src/TestSolver.sol index 9eb86bce..0e9a2247 100644 --- a/src/contracts/solver/src/TestSolver.sol +++ b/src/contracts/solver/src/TestSolver.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import { SolverBase } from "../SolverBase.sol"; +import { SolverBase } from "src/contracts/solver/SolverBase.sol"; // Flashbots opensource repo import { BlindBackrun } from "./BlindBackrun/BlindBackrun.sol"; diff --git a/src/contracts/solver/src/TestSolverExPost.sol b/src/contracts/solver/src/TestSolverExPost.sol index 9ae76220..2bf3cd0e 100644 --- a/src/contracts/solver/src/TestSolverExPost.sol +++ b/src/contracts/solver/src/TestSolverExPost.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.25; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; -import { SolverBase } from "../SolverBase.sol"; +import { SolverBase } from "src/contracts/solver/SolverBase.sol"; // Flashbots opensource repo import { BlindBackrun } from "./BlindBackrun/BlindBackrun.sol"; diff --git a/test/helpers/DummyDAppControlBuilder.sol b/test/helpers/DummyDAppControlBuilder.sol index 5aa25c46..d9eaafd2 100644 --- a/test/helpers/DummyDAppControlBuilder.sol +++ b/test/helpers/DummyDAppControlBuilder.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import { DummyDAppControl } from "../base/DummyDAppControl.sol"; +import { DummyDAppControl } from "test/base/DummyDAppControl.sol"; import { CallConfig } from "src/contracts/types/ConfigTypes.sol"; import { AtlasVerification } from "src/contracts/atlas/AtlasVerification.sol"; diff --git a/test/libraries/CallBits.t.sol b/test/libraries/CallBits.t.sol index 3f84e299..ffc093ca 100644 --- a/test/libraries/CallBits.t.sol +++ b/test/libraries/CallBits.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import { CallBits } from "src/contracts/libraries/CallBits.sol"; import "src/contracts/types/UserOperation.sol"; -import "../base/TestUtils.sol"; +import "test/base/TestUtils.sol"; contract CallBitsTest is Test { using CallBits for uint32; diff --git a/test/libraries/CallVerification.t.sol b/test/libraries/CallVerification.t.sol index 73e95bc8..66522419 100644 --- a/test/libraries/CallVerification.t.sol +++ b/test/libraries/CallVerification.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import { CallVerification } from "src/contracts/libraries/CallVerification.sol"; import "src/contracts/types/UserOperation.sol"; -import "../base/TestUtils.sol"; +import "test/base/TestUtils.sol"; contract CallVerificationTest is Test { using CallVerification for UserOperation; diff --git a/test/libraries/SafetyBits.t.sol b/test/libraries/SafetyBits.t.sol index 26b7650f..52ba6c97 100644 --- a/test/libraries/SafetyBits.t.sol +++ b/test/libraries/SafetyBits.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import { SafetyBits } from "src/contracts/libraries/SafetyBits.sol"; import "src/contracts/types/LockTypes.sol"; -import "../base/TestUtils.sol"; +import "test/base/TestUtils.sol"; import { CallBits } from "src/contracts/libraries/CallBits.sol"; From f5e9dd1fdb7fb6ba8aadf857d65a9c39ad4e0f38 Mon Sep 17 00:00:00 2001 From: jj1980a Date: Tue, 10 Sep 2024 12:05:52 +0400 Subject: [PATCH 07/21] contract ownable --- src/contracts/gasCalculator/BaseGasCalculator.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/contracts/gasCalculator/BaseGasCalculator.sol b/src/contracts/gasCalculator/BaseGasCalculator.sol index bcff04c7..bbdd4c3b 100644 --- a/src/contracts/gasCalculator/BaseGasCalculator.sol +++ b/src/contracts/gasCalculator/BaseGasCalculator.sol @@ -1,6 +1,7 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.25; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IL2GasCalculator } from "src/contracts/interfaces/IL2GasCalculator.sol"; /// @notice Implementation: @@ -10,14 +11,13 @@ interface IGasPriceOracle { function getL1FeeUpperBound(uint256 _unsignedTxSize) external view returns (uint256); } -contract BaseGasCalculator is IL2GasCalculator { - uint256 public constant _CALLDATA_LENGTH_PREMIUM = 32; // TODO: copied value from Atlas, should it be different for - // Base? +contract BaseGasCalculator is IL2GasCalculator, Ownable { + uint256 internal constant _CALLDATA_LENGTH_PREMIUM = 32; uint256 internal constant _BASE_TRANSACTION_GAS_USED = 21_000; address public immutable gasPriceOracle; - constructor(address _gasPriceOracle) { + constructor(address _gasPriceOracle) Ownable(msg.sender) { gasPriceOracle = _gasPriceOracle; } From f8dbebce10dcf375d3b6ebbd9ccd22595625d482 Mon Sep 17 00:00:00 2001 From: jj1980a Date: Tue, 10 Sep 2024 12:24:26 +0400 Subject: [PATCH 08/21] introduce a customizable offset value to be added to passed call data length --- .../gasCalculator/BaseGasCalculator.sol | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/contracts/gasCalculator/BaseGasCalculator.sol b/src/contracts/gasCalculator/BaseGasCalculator.sol index bbdd4c3b..a0dfc09e 100644 --- a/src/contracts/gasCalculator/BaseGasCalculator.sol +++ b/src/contracts/gasCalculator/BaseGasCalculator.sol @@ -16,9 +16,11 @@ contract BaseGasCalculator is IL2GasCalculator, Ownable { uint256 internal constant _BASE_TRANSACTION_GAS_USED = 21_000; address public immutable gasPriceOracle; + int256 public calldataLengthOffset; - constructor(address _gasPriceOracle) Ownable(msg.sender) { + constructor(address _gasPriceOracle, int256 _calldataLengthOffset) Ownable(msg.sender) { gasPriceOracle = _gasPriceOracle; + calldataLengthOffset = _calldataLengthOffset; } /// @notice Calculate the cost of calldata in ETH on a L2 with a different fee structure than mainnet @@ -33,7 +35,18 @@ contract BaseGasCalculator is IL2GasCalculator, Ownable { // L1 data cost. // `getL1FeeUpperBound` adds 68 to the size because it expects an unsigned transaction size. // Remove 68 to the length to account for this. - calldataCost += IGasPriceOracle(gasPriceOracle).getL1FeeUpperBound(calldataLength - 68); + if (calldataLength < 68) { + calldataLength = 0; + } else { + calldataLength -= 68; + } + + if (calldataLengthOffset < 0 && calldataLength < uint256(-calldataLengthOffset)) { + return calldataCost; + } + + calldataLength += uint256(calldataLengthOffset); + calldataCost += IGasPriceOracle(gasPriceOracle).getL1FeeUpperBound(calldataLength); } /// @notice Gets the cost of initial gas used for a transaction with a different calldata fee than mainnet @@ -41,4 +54,10 @@ contract BaseGasCalculator is IL2GasCalculator, Ownable { function initialGasUsed(uint256 calldataLength) external pure override returns (uint256 gasUsed) { return _BASE_TRANSACTION_GAS_USED + (calldataLength * _CALLDATA_LENGTH_PREMIUM); } + + /// @notice Sets the calldata length offset + /// @param _calldataLengthOffset The new calldata length offset + function setCalldataLengthOffset(int256 _calldataLengthOffset) external onlyOwner { + calldataLengthOffset = _calldataLengthOffset; + } } From fd33c385a0c34414e6526db61112995ae78b7a8f Mon Sep 17 00:00:00 2001 From: jj1980a Date: Tue, 10 Sep 2024 12:46:25 +0400 Subject: [PATCH 09/21] deploy script --- package.json | 2 ++ script/deploy-gas-calculator.s.sol | 46 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 script/deploy-gas-calculator.s.sol diff --git a/package.json b/package.json index 87452d74..fa096bdc 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,8 @@ "solver-deposit": "source .env && forge script script/solver-deposit.s.sol:SolverAtlasDepositScript --fork-url http://localhost:8545 --broadcast --non-interactive", "setup-demo": "npm run deploy-atlas-swap-intent-tx-builder && npm run deploy-solver && npm run solver-deposit", + "deploy-gas-calculator-base": "source .env && forge script script/deploy-gas-calculator.s.sol:DeployGasCalculatorScript --rpc-url ${BASE_RPC_URL} --broadcast --etherscan-api-key ${ETHERSCAN_API_KEY} --verify", + "atlas-addr": "echo 'ATLAS:' && jq -r '.ATLAS' deployments.json", "swap-intent-addr": "echo 'SWAP INTENT DAPP CONTROL:' && jq -r '.SWAP_INTENT_DAPP_CONTROL' deployments.json", "tx-builder-addr": "echo 'TX BUILDER:' && jq -r '.TX_BUILDER' deployments.json", diff --git a/script/deploy-gas-calculator.s.sol b/script/deploy-gas-calculator.s.sol new file mode 100644 index 00000000..8c52a0e7 --- /dev/null +++ b/script/deploy-gas-calculator.s.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.25; + +import "forge-std/Test.sol"; + +import { DeployBaseScript } from "script/base/deploy-base.s.sol"; +import { BaseGasCalculator } from "src/contracts/gasCalculator/BaseGasCalculator.sol"; + +contract DeployGasCalculatorScript is DeployBaseScript { + function run() external { + console.log("\n=== DEPLOYING GasCalculator ===\n"); + + console.log("Deploying to chain: \t\t", _getDeployChain()); + + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + + uint256 chainId = block.chainid; + string memory deploymentName; + address deploymentAddr; + + vm.startBroadcast(deployerPrivateKey); + + if (chainId == 8453 || chainId == 84_532) { + // Base or Base Sepolia + BaseGasCalculator gasCalculator = new BaseGasCalculator({ + _gasPriceOracle: address(0), // Insert gas price oracle address here + _calldataLengthOffset: 0 // Insert calldata length offset here (can be negative) + }); + deploymentName = "BASE_GAS_CALCULATOR"; + deploymentAddr = address(gasCalculator); + } else { + revert("Error: Chain ID not supported"); + } + + vm.stopBroadcast(); + + _writeAddressToDeploymentsJson(deploymentName, deploymentAddr); + + console.log("\n"); + console.log("Deployed contract: ", deploymentName); + console.log("Deployed at address: ", deploymentAddr); + console.log("\n"); + console.log("You can find a list of contract addresses from the latest deployment in deployments.json"); + } +} From 2448be065ffc5cfa94a97c8f40f07f4475b0502e Mon Sep 17 00:00:00 2001 From: jj1980a Date: Tue, 10 Sep 2024 12:51:58 +0400 Subject: [PATCH 10/21] forge update --- lib/forge-std | 2 +- lib/openzeppelin-contracts | 2 +- lib/solady | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/forge-std b/lib/forge-std index e4aef94c..1ce7535a 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd +Subproject commit 1ce7535a517406b9aec7ea1ea27c1b41376f712c diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index bd325d56..dbb6104c 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit bd325d56b4c62c9c5c1aff048c37c6bb18ac0290 +Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 diff --git a/lib/solady b/lib/solady index bb4b43b4..f833eadd 160000 --- a/lib/solady +++ b/lib/solady @@ -1 +1 @@ -Subproject commit bb4b43b44bec3c5d42604c08904bca0442e0bc78 +Subproject commit f833eadd4591da7e8e455eab779bf1d918486847 From c4de266110dd7c3de7db2f3aa6ab831e42310564 Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:05:52 +0200 Subject: [PATCH 11/21] chore: Base L2 Gas Calculator deploy script tweaks --- script/deploy-gas-calculator.s.sol | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/script/deploy-gas-calculator.s.sol b/script/deploy-gas-calculator.s.sol index 8c52a0e7..f2fcd58e 100644 --- a/script/deploy-gas-calculator.s.sol +++ b/script/deploy-gas-calculator.s.sol @@ -7,16 +7,25 @@ import { DeployBaseScript } from "script/base/deploy-base.s.sol"; import { BaseGasCalculator } from "src/contracts/gasCalculator/BaseGasCalculator.sol"; contract DeployGasCalculatorScript is DeployBaseScript { + // NOTE: Adjust the constructor parameters as needed here: + // - BASE_GAS_PRICE_ORACLE: The address of the gas price oracle contract + // - BASE_CALLDATA_LENGTH_OFFSET: The offset to be applied to the calldata length (can be negative or positive) + // ----------------------------------------------------------------------------------------------- + address constant BASE_GAS_PRICE_ORACLE = address(0x420000000000000000000000000000000000000F); + int256 constant BASE_CALLDATA_LENGTH_OFFSET = 0; // can be negative or positive + // ----------------------------------------------------------------------------------------------- + function run() external { console.log("\n=== DEPLOYING GasCalculator ===\n"); console.log("Deploying to chain: \t\t", _getDeployChain()); - uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + uint256 deployerPrivateKey = vm.envUint("GOV_PRIVATE_KEY"); address deployer = vm.addr(deployerPrivateKey); + console.log("Deployer address: \t\t", deployer); + uint256 chainId = block.chainid; - string memory deploymentName; address deploymentAddr; vm.startBroadcast(deployerPrivateKey); @@ -24,10 +33,9 @@ contract DeployGasCalculatorScript is DeployBaseScript { if (chainId == 8453 || chainId == 84_532) { // Base or Base Sepolia BaseGasCalculator gasCalculator = new BaseGasCalculator({ - _gasPriceOracle: address(0), // Insert gas price oracle address here - _calldataLengthOffset: 0 // Insert calldata length offset here (can be negative) - }); - deploymentName = "BASE_GAS_CALCULATOR"; + _gasPriceOracle: BASE_GAS_PRICE_ORACLE, + _calldataLengthOffset: BASE_CALLDATA_LENGTH_OFFSET + }); deploymentAddr = address(gasCalculator); } else { revert("Error: Chain ID not supported"); @@ -35,11 +43,14 @@ contract DeployGasCalculatorScript is DeployBaseScript { vm.stopBroadcast(); - _writeAddressToDeploymentsJson(deploymentName, deploymentAddr); + _writeAddressToDeploymentsJson("L2_GAS_CALCULATOR", deploymentAddr); console.log("\n"); - console.log("Deployed contract: ", deploymentName); - console.log("Deployed at address: ", deploymentAddr); + console.log("-------------------------------------------------------------------------------"); + console.log("| Contract | Address |"); + console.log("-------------------------------------------------------------------------------"); + console.log("| L2_GAS_CALCULATOR (Base) | ", address(deploymentAddr), " |"); + console.log("-------------------------------------------------------------------------------"); console.log("\n"); console.log("You can find a list of contract addresses from the latest deployment in deployments.json"); } From 20ac0afeba981f926dd02b444eafc94fe13a6831 Mon Sep 17 00:00:00 2001 From: jj1980a <40311124+jj1980a@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:47:18 +0400 Subject: [PATCH 12/21] Apply suggestions from code review Co-authored-by: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> --- .../gasCalculator/BaseGasCalculator.sol | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/contracts/gasCalculator/BaseGasCalculator.sol b/src/contracts/gasCalculator/BaseGasCalculator.sol index a0dfc09e..b1217ec2 100644 --- a/src/contracts/gasCalculator/BaseGasCalculator.sol +++ b/src/contracts/gasCalculator/BaseGasCalculator.sol @@ -15,16 +15,17 @@ contract BaseGasCalculator is IL2GasCalculator, Ownable { uint256 internal constant _CALLDATA_LENGTH_PREMIUM = 32; uint256 internal constant _BASE_TRANSACTION_GAS_USED = 21_000; - address public immutable gasPriceOracle; + address public immutable GAS_PRICE_ORACLE; int256 public calldataLengthOffset; - constructor(address _gasPriceOracle, int256 _calldataLengthOffset) Ownable(msg.sender) { - gasPriceOracle = _gasPriceOracle; - calldataLengthOffset = _calldataLengthOffset; + constructor(address gasPriceOracle, int256 calldataLenOffset) Ownable(msg.sender) { + GAS_PRICE_ORACLE = gasPriceOracle; + calldataLengthOffset = calldataLenOffset; } /// @notice Calculate the cost of calldata in ETH on a L2 with a different fee structure than mainnet /// @param calldataLength The length of the calldata in bytes + /// @return calldataCost The cost of the calldata in ETH function getCalldataCost(uint256 calldataLength) external view override returns (uint256 calldataCost) { // `getL1FeeUpperBound` returns the upper bound of the L1 fee in wei. It expects an unsigned transaction size in // bytes, *not calldata length only*, which makes this function a rough estimate. @@ -41,12 +42,14 @@ contract BaseGasCalculator is IL2GasCalculator, Ownable { calldataLength -= 68; } - if (calldataLengthOffset < 0 && calldataLength < uint256(-calldataLengthOffset)) { + int256 _calldataLenOffset = calldataLengthOffset; + + if (_calldataLenOffset < 0 && calldataLength < uint256(-_calldataLenOffset)) { return calldataCost; } - calldataLength += uint256(calldataLengthOffset); - calldataCost += IGasPriceOracle(gasPriceOracle).getL1FeeUpperBound(calldataLength); + calldataLength += uint256(_calldataLenOffset); + calldataCost += IGasPriceOracle(GAS_PRICE_ORACLE).getL1FeeUpperBound(calldataLength); } /// @notice Gets the cost of initial gas used for a transaction with a different calldata fee than mainnet @@ -57,7 +60,7 @@ contract BaseGasCalculator is IL2GasCalculator, Ownable { /// @notice Sets the calldata length offset /// @param _calldataLengthOffset The new calldata length offset - function setCalldataLengthOffset(int256 _calldataLengthOffset) external onlyOwner { - calldataLengthOffset = _calldataLengthOffset; + function setCalldataLengthOffset(int256 calldataLenOffset) external onlyOwner { + calldataLengthOffset = calldataLenOffset; } } From 04493e7f57844924632a2dcd64b554f1935e726f Mon Sep 17 00:00:00 2001 From: jj1980a Date: Fri, 13 Sep 2024 13:48:58 +0400 Subject: [PATCH 13/21] forge fmt --- src/contracts/gasCalculator/BaseGasCalculator.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/contracts/gasCalculator/BaseGasCalculator.sol b/src/contracts/gasCalculator/BaseGasCalculator.sol index b1217ec2..d572b8e1 100644 --- a/src/contracts/gasCalculator/BaseGasCalculator.sol +++ b/src/contracts/gasCalculator/BaseGasCalculator.sol @@ -43,7 +43,7 @@ contract BaseGasCalculator is IL2GasCalculator, Ownable { } int256 _calldataLenOffset = calldataLengthOffset; - + if (_calldataLenOffset < 0 && calldataLength < uint256(-_calldataLenOffset)) { return calldataCost; } @@ -59,7 +59,7 @@ contract BaseGasCalculator is IL2GasCalculator, Ownable { } /// @notice Sets the calldata length offset - /// @param _calldataLengthOffset The new calldata length offset + /// @param calldataLenOffset The new calldata length offset function setCalldataLengthOffset(int256 calldataLenOffset) external onlyOwner { calldataLengthOffset = calldataLenOffset; } From bc6f32a98e551921c0e86a6ba251531fc9c31e04 Mon Sep 17 00:00:00 2001 From: jj1980a Date: Fri, 13 Sep 2024 13:50:46 +0400 Subject: [PATCH 14/21] fix deploy script --- script/deploy-gas-calculator.s.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/deploy-gas-calculator.s.sol b/script/deploy-gas-calculator.s.sol index f2fcd58e..3866bbaf 100644 --- a/script/deploy-gas-calculator.s.sol +++ b/script/deploy-gas-calculator.s.sol @@ -33,8 +33,8 @@ contract DeployGasCalculatorScript is DeployBaseScript { if (chainId == 8453 || chainId == 84_532) { // Base or Base Sepolia BaseGasCalculator gasCalculator = new BaseGasCalculator({ - _gasPriceOracle: BASE_GAS_PRICE_ORACLE, - _calldataLengthOffset: BASE_CALLDATA_LENGTH_OFFSET + gasPriceOracle: BASE_GAS_PRICE_ORACLE, + calldataLenOffset: BASE_CALLDATA_LENGTH_OFFSET }); deploymentAddr = address(gasCalculator); } else { From 547588bb539130e0ea957af013fbedfd569ccc10 Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:33:39 +0200 Subject: [PATCH 15/21] feat: add easier way to control hook failure for tests --- test/base/DummyDAppControl.sol | 35 +++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/test/base/DummyDAppControl.sol b/test/base/DummyDAppControl.sol index e74653c1..e7ce0401 100644 --- a/test/base/DummyDAppControl.sol +++ b/test/base/DummyDAppControl.sol @@ -14,6 +14,13 @@ library CallConfigBuilder { } contract DummyDAppControl is DAppControl { + bool public preOpsShouldRevert; + bool public userOpShouldRevert; + bool public preSolverShouldRevert; + bool public postSolverShouldRevert; + bool public allocateValueShouldRevert; + bool public postOpsShouldRevert; + event MEVPaymentSuccess(address bidToken, uint256 bidAmount); constructor( @@ -78,7 +85,7 @@ contract DummyDAppControl is DAppControl { return; } - (bool shouldRevert) = abi.decode(data, (bool)); + bool shouldRevert = DummyDAppControl(CONTROL).allocateValueShouldRevert(); require(!shouldRevert, "_allocateValueCall revert requested"); emit MEVPaymentSuccess(bidToken, winningAmount); } @@ -97,4 +104,30 @@ contract DummyDAppControl is DAppControl { require(!shouldRevert, "userOperationCall revert requested"); return returnValue; } + + // Revert settings + + function setPreOpsShouldRevert(bool _preOpsShouldRevert) public { + preOpsShouldRevert = _preOpsShouldRevert; + } + + function setUserOpShouldRevert(bool _userOpShouldRevert) public { + userOpShouldRevert = _userOpShouldRevert; + } + + function setPreSolverShouldRevert(bool _preSolverShouldRevert) public { + preSolverShouldRevert = _preSolverShouldRevert; + } + + function setPostSolverShouldRevert(bool _postSolverShouldRevert) public { + postSolverShouldRevert = _postSolverShouldRevert; + } + + function setAllocateValueShouldRevert(bool _allocateValueShouldRevert) public { + allocateValueShouldRevert = _allocateValueShouldRevert; + } + + function setPostOpsShouldRevert(bool _postOpsShouldRevert) public { + postOpsShouldRevert = _postOpsShouldRevert; + } } From 5605e76b96c8da75b69d635b980343d8735410ed Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Fri, 13 Sep 2024 17:34:18 +0200 Subject: [PATCH 16/21] test: add `forwardReturnData` config tests --- src/contracts/solver/SolverBase.sol | 2 +- test/Escrow.t.sol | 92 +++++++++++++++++++++++------ 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/contracts/solver/SolverBase.sol b/src/contracts/solver/SolverBase.sol index 88cccac6..0ef23806 100644 --- a/src/contracts/solver/SolverBase.sol +++ b/src/contracts/solver/SolverBase.sol @@ -42,7 +42,7 @@ contract SolverBase is ISolverContract { address bidToken, uint256 bidAmount, bytes calldata solverOpData, - bytes calldata + bytes calldata forwardedData ) external payable diff --git a/test/Escrow.t.sol b/test/Escrow.t.sol index e789f019..70514406 100644 --- a/test/Escrow.t.sol +++ b/test/Escrow.t.sol @@ -191,6 +191,9 @@ contract EscrowTest is BaseTest { .withAllowAllocateValueFailure(false) // Do not allow the value allocation to fail .build() ); + + dAppControl.setAllocateValueShouldRevert(true); + executeHookCase(false, 1, AtlasErrors.AllocateValueFail.selector); } @@ -463,11 +466,6 @@ contract EscrowTest is BaseTest { .withBidAmount(bidAmount) .signAndBuild(address(atlasVerification), solverOnePK); - console.log("DApp control balance", address(gasSponsorControl).balance); - console.log("Solver balance", address(dummySolver).balance); - console.log("Bid amount (to trigger partial)", bidAmount); - console.log("Solver bonded amt", atlas.balanceOfBonded(solverOneEOA)); - uint256 expectedResult = 0; // Success expected executeSolverOperationCase(userOp, solverOps, true, true, expectedResult, false); } @@ -479,7 +477,6 @@ contract EscrowTest is BaseTest { uint256 solverOpValue = address(atlas).balance; assertTrue(solverOpValue > 0, "solverOpValue must be greater than 0"); - deal(address(solver), 10e18); // plenty of ETH to repay what solver owes (UserOperation memory userOp, SolverOperation[] memory solverOps) = executeSolverOperationInit(defaultCallConfig().build()); @@ -495,6 +492,67 @@ contract EscrowTest is BaseTest { assertTrue(success, "metacall should have succeeded"); } + function test_executeSolverOperation_ForwardReturnData_True() public { + // Checks that the solver CAN receive the data returned from the userOp phase + uint256 expectedDataValue = 123; + DummySolverContributor solver = new DummySolverContributor(address(atlas)); + assertEq(solver.forwardedData().length, 0, "solver forwardedData should start empty"); + deal(address(solver), 1 ether); // 1 ETH covers default bid (1) + 0.5 ETH gas cost + + (UserOperation memory userOp, SolverOperation[] memory solverOps) = executeSolverOperationInit( + defaultCallConfig() + .withTrackUserReturnData(true) + .withForwardReturnData(true) + .withRequireFulfillment(true) + .build() + ); + + userOp = validUserOperation(address(dAppControl)) + .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, expectedDataValue)) + .signAndBuild(address(atlasVerification), userPK); + + solverOps[0] = validSolverOperation(userOp) + .withSolver(address(solver)) + .withBidAmount(defaultBidAmount) + .signAndBuild(address(atlasVerification), solverOnePK); + + uint256 result = 0; // Success + executeSolverOperationCase(userOp, solverOps, true, true, result, false); + + uint256 forwardedData = abi.decode(solver.forwardedData(), (uint256)); + assertEq(forwardedData, expectedDataValue, "solver should have received the userOp data"); + } + + function test_executeSolverOperation_ForwardReturnData_False() public { + // Checks that the solver CANNOT receive the data returned from the userOp phase + uint256 dataValue = 123; + DummySolverContributor solver = new DummySolverContributor(address(atlas)); + assertEq(solver.forwardedData().length, 0, "solver forwardedData should start empty"); + deal(address(solver), 1 ether); // 1 ETH covers default bid (1) + 0.5 ETH gas cost + + (UserOperation memory userOp, SolverOperation[] memory solverOps) = executeSolverOperationInit( + defaultCallConfig() + .withTrackUserReturnData(true) + .withForwardReturnData(false) + .withRequireFulfillment(true) + .build() + ); + + userOp = validUserOperation(address(dAppControl)) + .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, dataValue)) + .signAndBuild(address(atlasVerification), userPK); + + solverOps[0] = validSolverOperation(userOp) + .withSolver(address(solver)) + .withBidAmount(defaultBidAmount) + .signAndBuild(address(atlasVerification), solverOnePK); + + uint256 result = 0; // Success + executeSolverOperationCase(userOp, solverOps, true, true, result, false); + assertEq(solver.forwardedData().length, 0, "solver forwardedData should still be empty"); + } + + function test_executeSolverOperation_solverOpWrapper_defaultCase() public { // Can't find a way to reach the default case (which is a good thing) vm.skip(true); @@ -547,7 +605,7 @@ contract DummySolver { } function atlasSolverCall( - address solverOpFrom, + address /* solverOpFrom */, address executionEnvironment, address, uint256 bidAmount, @@ -567,7 +625,6 @@ contract DummySolver { if (address(this).balance >= bidAmount) { SafeTransferLib.safeTransferETH(executionEnvironment, bidAmount); } - if (bidAmount == noGasPayBack) { // Don't pay gas @@ -575,10 +632,6 @@ contract DummySolver { } else if (bidAmount == partialGasPayBack) { // Only pay half of shortfall owed - expect postSolverCall hook in DAppControl to pay the rest uint256 _shortfall = IAtlas(_atlas).shortfall(); - - console.log("Solver shortfall", _shortfall); - console.log("gas price", tx.gasprice); - IAtlas(_atlas).reconcile(_shortfall / 2); return; } @@ -591,31 +644,34 @@ contract DummySolver { } contract DummySolverContributor { - address private _atlas; + address private immutable ATLAS; + bytes public forwardedData; constructor(address atlas) { - _atlas = atlas; + ATLAS = atlas; } function atlasSolverCall( - address solverOpFrom, + address /* solverOpFrom */, address executionEnvironment, address, uint256 bidAmount, bytes calldata, - bytes calldata + bytes calldata userReturnData ) external payable { + if (userReturnData.length > 0) forwardedData = userReturnData; + // Pay bid if (address(this).balance >= bidAmount) { SafeTransferLib.safeTransferETH(executionEnvironment, bidAmount); } // Pay borrowed ETH + gas used - uint256 shortfall = IAtlas(_atlas).shortfall(); - IAtlas(_atlas).reconcile{value: shortfall}(0); + uint256 shortfall = IAtlas(ATLAS).shortfall(); + IAtlas(ATLAS).reconcile{value: shortfall}(0); return; } From 4aa76469b8aa3c99f61f0f0c75308492660090d6 Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:10:00 +0200 Subject: [PATCH 17/21] test: use hookShouldRevert pattern in `Escrow.t.sol` --- test/Escrow.t.sol | 36 ++++++++++++++---------- test/base/DummyDAppControl.sol | 50 ++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/test/Escrow.t.sol b/test/Escrow.t.sol index 70514406..d6035cce 100644 --- a/test/Escrow.t.sol +++ b/test/Escrow.t.sol @@ -143,7 +143,7 @@ contract EscrowTest is BaseTest { .withAllowAllocateValueFailure(true) // Allow the value allocation to fail .build() ); - executeHookCase(false, block.timestamp * 2, noError); + executeHookCase(block.timestamp * 2, noError); } // Ensure metacall reverts with the proper error when the preOps hook reverts. @@ -154,7 +154,8 @@ contract EscrowTest is BaseTest { .withReuseUserOp(true) // Allow metacall to revert .build() ); - executeHookCase(true, 0, AtlasErrors.PreOpsFail.selector); + dAppControl.setPreOpsShouldRevert(true); + executeHookCase(0, AtlasErrors.PreOpsFail.selector); } // Ensure the user operation executes successfully. To ensure the operation's returned data is as expected, we @@ -168,7 +169,7 @@ contract EscrowTest is BaseTest { .withAllowAllocateValueFailure(true) // Allow the value allocation to fail .build() ); - executeHookCase(false, block.timestamp * 3, noError); + executeHookCase(block.timestamp * 3, noError); } // Ensure metacall reverts with the proper error when the user operation reverts. @@ -178,7 +179,8 @@ contract EscrowTest is BaseTest { .withReuseUserOp(true) // Allow metacall to revert .build() ); - executeHookCase(true, 0, AtlasErrors.UserOpFail.selector); + dAppControl.setUserOpShouldRevert(true); + executeHookCase(0, AtlasErrors.UserOpFail.selector); } // Ensure metacall reverts with the proper error when the allocateValue hook reverts. @@ -194,7 +196,7 @@ contract EscrowTest is BaseTest { dAppControl.setAllocateValueShouldRevert(true); - executeHookCase(false, 1, AtlasErrors.AllocateValueFail.selector); + executeHookCase(1, AtlasErrors.AllocateValueFail.selector); } // Ensure the postOps hook is successfully called. No return data is expected from the postOps hook, so we do not @@ -205,7 +207,7 @@ contract EscrowTest is BaseTest { .withRequirePostOps(true) // Execute the postOps hook .build() ); - executeHookCase(false, 0, noError); + executeHookCase(0, noError); } // Ensure metacall reverts with the proper error when the postOps hook reverts. @@ -219,7 +221,8 @@ contract EscrowTest is BaseTest { .withAllowAllocateValueFailure(true) // Allow the value allocation to fail .build() ); - executeHookCase(false, 1, AtlasErrors.PostOpsFail.selector); + dAppControl.setPostOpsShouldRevert(true); + executeHookCase(1, AtlasErrors.PostOpsFail.selector); } // Ensure the allocateValue hook is successfully called. No return data is expected from the allocateValue hook, so @@ -237,17 +240,16 @@ contract EscrowTest is BaseTest { vm.expectEmit(false, false, false, true, executionEnvironment); emit MEVPaymentSuccess(address(0), defaultBidAmount); - this.executeHookCase(false, 0, noError); + this.executeHookCase(0, noError); } - function executeHookCase(bool hookShouldRevert, uint256 expectedHookReturnValue, bytes4 expectedError) public { + function executeHookCase(uint256 expectedHookReturnValue, bytes4 expectedError) public { bool revertExpected = expectedError != noError; UserOperation memory userOp = validUserOperation(address(dAppControl)) .withData( abi.encodeWithSelector( dAppControl.userOperationCall.selector, - hookShouldRevert, expectedHookReturnValue ) ) @@ -385,7 +387,7 @@ contract EscrowTest is BaseTest { ); UserOperation memory userOp = validUserOperation(address(dAppControl)) - .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, 1)) + .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, 1)) .signAndBuild(address(atlasVerification), userPK); SolverOperation[] memory solverOps = new SolverOperation[](1); @@ -394,6 +396,8 @@ contract EscrowTest is BaseTest { .signAndBuild(address(atlasVerification), solverOnePK); uint256 result = (1 << uint256(SolverOutcome.PreSolverFailed)); + dAppControl.setPreSolverShouldRevert(true); + executeSolverOperationCase(userOp, solverOps, false, false, result, true); } @@ -409,7 +413,7 @@ contract EscrowTest is BaseTest { ); UserOperation memory userOp = validUserOperation(address(dAppControl)) - .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, 1)) + .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, 1)) .signAndBuild(address(atlasVerification), userPK); SolverOperation[] memory solverOps = new SolverOperation[](1); @@ -418,6 +422,8 @@ contract EscrowTest is BaseTest { .signAndBuild(address(atlasVerification), solverOnePK); uint256 result = (1 << uint256(SolverOutcome.PostSolverFailed)); + dAppControl.setPostSolverShouldRevert(true); + executeSolverOperationCase(userOp, solverOps, true, false, result, true); } @@ -508,7 +514,7 @@ contract EscrowTest is BaseTest { ); userOp = validUserOperation(address(dAppControl)) - .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, expectedDataValue)) + .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, expectedDataValue)) .signAndBuild(address(atlasVerification), userPK); solverOps[0] = validSolverOperation(userOp) @@ -539,7 +545,7 @@ contract EscrowTest is BaseTest { ); userOp = validUserOperation(address(dAppControl)) - .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, dataValue)) + .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, dataValue)) .signAndBuild(address(atlasVerification), userPK); solverOps[0] = validSolverOperation(userOp) @@ -565,7 +571,7 @@ contract EscrowTest is BaseTest { defaultAtlasWithCallConfig(callConfig); userOp = validUserOperation(address(dAppControl)) - .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, false, 0)) + .withData(abi.encodeWithSelector(dAppControl.userOperationCall.selector, 0)) .signAndBuild(address(atlasVerification), userPK); solverOps = new SolverOperation[](1); diff --git a/test/base/DummyDAppControl.sol b/test/base/DummyDAppControl.sol index e7ce0401..34341e81 100644 --- a/test/base/DummyDAppControl.sol +++ b/test/base/DummyDAppControl.sol @@ -21,6 +21,8 @@ contract DummyDAppControl is DAppControl { bool public allocateValueShouldRevert; bool public postOpsShouldRevert; + // TODO add storage vars to store input data for each hook, then test hooks were passed the correct data + event MEVPaymentSuccess(address bidToken, uint256 bidAmount); constructor( @@ -38,38 +40,49 @@ contract DummyDAppControl is DAppControl { function _checkUserOperation(UserOperation memory) internal pure virtual override { } function _preOpsCall(UserOperation calldata userOp) internal virtual override returns (bytes memory) { - if (userOp.data.length == 0) { - return new bytes(0); - } + bool shouldRevert = DummyDAppControl(CONTROL).preOpsShouldRevert(); + require(!shouldRevert, "_preOpsCall revert requested"); - (bool success, bytes memory data) = address(userOp.dapp).call(userOp.data); - require(success, "_preOpsCall reverted"); + if (userOp.data.length == 0) return new bytes(0); + + (, bytes memory data) = address(userOp.dapp).call(userOp.data); return data; } - function _postOpsCall(bool, bytes calldata data) internal pure virtual override { + function _postOpsCall(bool, bytes calldata data) internal view virtual override { + bool shouldRevert = DummyDAppControl(CONTROL).postOpsShouldRevert(); + require(!shouldRevert, "_postOpsCall revert requested"); if (data.length == 0) return; - (bool shouldRevert) = abi.decode(data, (bool)); - require(!shouldRevert, "_postOpsCall revert requested"); + // TODO store input data and check it was passed correctly in Escrow.t.sol + // (bool shouldRevert) = abi.decode(data, (bool)); + // require(!shouldRevert, "_postOpsCall revert requested"); } function _preSolverCall(SolverOperation calldata, bytes calldata returnData) internal view virtual override { + bool shouldRevert = DummyDAppControl(CONTROL).preSolverShouldRevert(); + require(!shouldRevert, "_preSolverCall revert requested"); + if (returnData.length == 0) { return; } - (bool shouldRevert) = abi.decode(returnData, (bool)); - require(!shouldRevert, "_preSolverCall revert requested"); + // TODO store input data and check it was passed correctly in Escrow.t.sol + // (bool shouldRevert) = abi.decode(returnData, (bool)); + // require(!shouldRevert, "_preSolverCall revert requested"); } - function _postSolverCall(SolverOperation calldata, bytes calldata returnData) internal pure virtual override { + function _postSolverCall(SolverOperation calldata, bytes calldata returnData) internal view virtual override { + bool shouldRevert = DummyDAppControl(CONTROL).postSolverShouldRevert(); + require(!shouldRevert, "_postSolverCall revert requested"); + if (returnData.length == 0) { return; } - (bool shouldRevert) = abi.decode(returnData, (bool)); - require(!shouldRevert, "_postSolverCall revert requested"); + // TODO store input data and check it was passed correctly in Escrow.t.sol + // (bool shouldRevert) = abi.decode(returnData, (bool)); + // require(!shouldRevert, "_postSolverCall revert requested"); } function _allocateValueCall( @@ -81,12 +94,10 @@ contract DummyDAppControl is DAppControl { virtual override { - if (data.length == 0) { - return; - } - bool shouldRevert = DummyDAppControl(CONTROL).allocateValueShouldRevert(); require(!shouldRevert, "_allocateValueCall revert requested"); + + // TODO store input data and check it was passed correctly in Escrow.t.sol emit MEVPaymentSuccess(bidToken, winningAmount); } @@ -100,8 +111,11 @@ contract DummyDAppControl is DAppControl { // Custom functions // **************************************** - function userOperationCall(bool shouldRevert, uint256 returnValue) public pure returns (uint256) { + function userOperationCall(uint256 returnValue) public view returns (uint256) { + bool shouldRevert = DummyDAppControl(CONTROL).userOpShouldRevert(); require(!shouldRevert, "userOperationCall revert requested"); + + // TODO store input data and check it was passed correctly in Escrow.t.sol return returnValue; } From ffba055fc7f2db66a33ffb84ffa73cd7b4d3a06a Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Wed, 18 Sep 2024 15:31:29 +0200 Subject: [PATCH 18/21] test: improve DummyDAppControl, Escrow tests --- test/Escrow.t.sol | 31 +++++++++++------ test/base/DummyDAppControl.sol | 61 ++++++++++++++++++++-------------- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/test/Escrow.t.sol b/test/Escrow.t.sol index d6035cce..cacbf804 100644 --- a/test/Escrow.t.sol +++ b/test/Escrow.t.sol @@ -143,7 +143,10 @@ contract EscrowTest is BaseTest { .withAllowAllocateValueFailure(true) // Allow the value allocation to fail .build() ); - executeHookCase(block.timestamp * 2, noError); + + (UserOperation memory userOp,,) = executeHookCase(block.timestamp * 2, noError); + bytes memory expectedInput = abi.encode(userOp); + assertEq(expectedInput, dAppControl.preOpsInputData(), "preOpsInputData should match expectedInput"); } // Ensure metacall reverts with the proper error when the preOps hook reverts. @@ -170,6 +173,8 @@ contract EscrowTest is BaseTest { .build() ); executeHookCase(block.timestamp * 3, noError); + bytes memory expectedInput = abi.encode(block.timestamp * 3); + assertEq(expectedInput, dAppControl.userOpInputData(), "userOpInputData should match expectedInput"); } // Ensure metacall reverts with the proper error when the user operation reverts. @@ -195,7 +200,6 @@ contract EscrowTest is BaseTest { ); dAppControl.setAllocateValueShouldRevert(true); - executeHookCase(1, AtlasErrors.AllocateValueFail.selector); } @@ -208,6 +212,8 @@ contract EscrowTest is BaseTest { .build() ); executeHookCase(0, noError); + bytes memory expectedInput = abi.encode(true, new bytes(0)); + assertEq(expectedInput, dAppControl.postOpsInputData(), "postOpsInputData should match expectedInput"); } // Ensure metacall reverts with the proper error when the postOps hook reverts. @@ -234,19 +240,22 @@ contract EscrowTest is BaseTest { .withTrackUserReturnData(true) // Track the user operation's return data .build() ); + uint256 userOpArg = 321; - vm.prank(userEOA); - address executionEnvironment = atlas.createExecutionEnvironment(userEOA, address(dAppControl)); + executeHookCase(userOpArg, noError); - vm.expectEmit(false, false, false, true, executionEnvironment); - emit MEVPaymentSuccess(address(0), defaultBidAmount); - this.executeHookCase(0, noError); + bytes memory expectedInput = abi.encode(address(0), defaultBidAmount, abi.encode(userOpArg)); + assertEq(expectedInput, dAppControl.allocateValueInputData(), "allocateValueInputData should match expectedInput"); } - function executeHookCase(uint256 expectedHookReturnValue, bytes4 expectedError) public { + function executeHookCase(uint256 expectedHookReturnValue, bytes4 expectedError) public returns( + UserOperation memory userOp, + SolverOperation[] memory solverOps, + DAppOperation memory dappOp + ) { bool revertExpected = expectedError != noError; - UserOperation memory userOp = validUserOperation(address(dAppControl)) + userOp = validUserOperation(address(dAppControl)) .withData( abi.encodeWithSelector( dAppControl.userOperationCall.selector, @@ -255,13 +264,13 @@ contract EscrowTest is BaseTest { ) .signAndBuild(address(atlasVerification), userPK); - SolverOperation[] memory solverOps = new SolverOperation[](1); + solverOps = new SolverOperation[](1); solverOps[0] = validSolverOperation(userOp) .withBidAmount(defaultBidAmount) .withData(abi.encode(expectedHookReturnValue)) .signAndBuild(address(atlasVerification), solverOnePK); - DAppOperation memory dappOp = validDAppOperation(userOp, solverOps).build(); + dappOp = validDAppOperation(userOp, solverOps).build(); if (revertExpected) { vm.expectRevert(expectedError); diff --git a/test/base/DummyDAppControl.sol b/test/base/DummyDAppControl.sol index 34341e81..42975c5c 100644 --- a/test/base/DummyDAppControl.sol +++ b/test/base/DummyDAppControl.sol @@ -22,6 +22,12 @@ contract DummyDAppControl is DAppControl { bool public postOpsShouldRevert; // TODO add storage vars to store input data for each hook, then test hooks were passed the correct data + bytes public preOpsInputData; + bytes public userOpInputData; + bytes public preSolverInputData; + bytes public postSolverInputData; + bytes public allocateValueInputData; + bytes public postOpsInputData; event MEVPaymentSuccess(address bidToken, uint256 bidAmount); @@ -43,46 +49,34 @@ contract DummyDAppControl is DAppControl { bool shouldRevert = DummyDAppControl(CONTROL).preOpsShouldRevert(); require(!shouldRevert, "_preOpsCall revert requested"); + DummyDAppControl(CONTROL).setInputData(abi.encode(userOp), 0); + console.logBytes(abi.encode(userOp)); + if (userOp.data.length == 0) return new bytes(0); (, bytes memory data) = address(userOp.dapp).call(userOp.data); return data; } - function _postOpsCall(bool, bytes calldata data) internal view virtual override { + function _postOpsCall(bool solved, bytes calldata data) internal virtual override { bool shouldRevert = DummyDAppControl(CONTROL).postOpsShouldRevert(); require(!shouldRevert, "_postOpsCall revert requested"); - if (data.length == 0) return; - // TODO store input data and check it was passed correctly in Escrow.t.sol - // (bool shouldRevert) = abi.decode(data, (bool)); - // require(!shouldRevert, "_postOpsCall revert requested"); + DummyDAppControl(CONTROL).setInputData(abi.encode(solved, data), 5); } - function _preSolverCall(SolverOperation calldata, bytes calldata returnData) internal view virtual override { + function _preSolverCall(SolverOperation calldata solverOp, bytes calldata returnData) internal virtual override { bool shouldRevert = DummyDAppControl(CONTROL).preSolverShouldRevert(); require(!shouldRevert, "_preSolverCall revert requested"); - if (returnData.length == 0) { - return; - } - - // TODO store input data and check it was passed correctly in Escrow.t.sol - // (bool shouldRevert) = abi.decode(returnData, (bool)); - // require(!shouldRevert, "_preSolverCall revert requested"); + DummyDAppControl(CONTROL).setInputData(abi.encode(solverOp, returnData), 2); } - function _postSolverCall(SolverOperation calldata, bytes calldata returnData) internal view virtual override { + function _postSolverCall(SolverOperation calldata solverOp, bytes calldata returnData) internal virtual override { bool shouldRevert = DummyDAppControl(CONTROL).postSolverShouldRevert(); require(!shouldRevert, "_postSolverCall revert requested"); - if (returnData.length == 0) { - return; - } - - // TODO store input data and check it was passed correctly in Escrow.t.sol - // (bool shouldRevert) = abi.decode(returnData, (bool)); - // require(!shouldRevert, "_postSolverCall revert requested"); + DummyDAppControl(CONTROL).setInputData(abi.encode(solverOp, returnData), 3); } function _allocateValueCall( @@ -97,8 +91,9 @@ contract DummyDAppControl is DAppControl { bool shouldRevert = DummyDAppControl(CONTROL).allocateValueShouldRevert(); require(!shouldRevert, "_allocateValueCall revert requested"); - // TODO store input data and check it was passed correctly in Escrow.t.sol - emit MEVPaymentSuccess(bidToken, winningAmount); + DummyDAppControl(CONTROL).setInputData(abi.encode(bidToken, winningAmount, data), 4); + + // emit MEVPaymentSuccess(bidToken, winningAmount); } function getBidValue(SolverOperation calldata solverOp) public view virtual override returns (uint256) { @@ -111,11 +106,12 @@ contract DummyDAppControl is DAppControl { // Custom functions // **************************************** - function userOperationCall(uint256 returnValue) public view returns (uint256) { + function userOperationCall(uint256 returnValue) public returns (uint256) { bool shouldRevert = DummyDAppControl(CONTROL).userOpShouldRevert(); require(!shouldRevert, "userOperationCall revert requested"); - // TODO store input data and check it was passed correctly in Escrow.t.sol + DummyDAppControl(CONTROL).setInputData(abi.encode(returnValue), 1); + return returnValue; } @@ -144,4 +140,19 @@ contract DummyDAppControl is DAppControl { function setPostOpsShouldRevert(bool _postOpsShouldRevert) public { postOpsShouldRevert = _postOpsShouldRevert; } + + // Called by the EE to save input data for testing after the metacall ends + function setInputData( + bytes memory inputData, + uint256 hook // 0: preOps, 1: userOp, 2: preSolver, 3: postSolver, 4: allocateValue, 5: postOps + ) + public + { + if (hook == 0) preOpsInputData = inputData; + if (hook == 1) userOpInputData = inputData; + if (hook == 2) preSolverInputData = inputData; + if (hook == 3) postSolverInputData = inputData; + if (hook == 4) allocateValueInputData = inputData; + if (hook == 5) postOpsInputData = inputData; + } } From 1c476d9ec4f8e98b2199a85ddd47e78a98f4720c Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:00:36 +0200 Subject: [PATCH 19/21] chore: remove TODO --- test/base/DummyDAppControl.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/test/base/DummyDAppControl.sol b/test/base/DummyDAppControl.sol index 42975c5c..b33af3cc 100644 --- a/test/base/DummyDAppControl.sol +++ b/test/base/DummyDAppControl.sol @@ -21,7 +21,6 @@ contract DummyDAppControl is DAppControl { bool public allocateValueShouldRevert; bool public postOpsShouldRevert; - // TODO add storage vars to store input data for each hook, then test hooks were passed the correct data bytes public preOpsInputData; bytes public userOpInputData; bytes public preSolverInputData; From c560ce5f4fd38b7e8d65d5704c9f1b589789e8b4 Mon Sep 17 00:00:00 2001 From: jj1980a Date: Wed, 25 Sep 2024 11:05:33 +0400 Subject: [PATCH 20/21] lower calldata premium value --- lib/forge-std | 2 +- lib/solady | 2 +- src/contracts/gasCalculator/BaseGasCalculator.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/forge-std b/lib/forge-std index 1ce7535a..5a802d7c 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 1ce7535a517406b9aec7ea1ea27c1b41376f712c +Subproject commit 5a802d7c10abb4bbfb3e7214c75052ef9e6a06f8 diff --git a/lib/solady b/lib/solady index f833eadd..42af395e 160000 --- a/lib/solady +++ b/lib/solady @@ -1 +1 @@ -Subproject commit f833eadd4591da7e8e455eab779bf1d918486847 +Subproject commit 42af395e631fcc9d640eddf11c57c6f1ca3f9103 diff --git a/src/contracts/gasCalculator/BaseGasCalculator.sol b/src/contracts/gasCalculator/BaseGasCalculator.sol index d572b8e1..97e404a6 100644 --- a/src/contracts/gasCalculator/BaseGasCalculator.sol +++ b/src/contracts/gasCalculator/BaseGasCalculator.sol @@ -12,7 +12,7 @@ interface IGasPriceOracle { } contract BaseGasCalculator is IL2GasCalculator, Ownable { - uint256 internal constant _CALLDATA_LENGTH_PREMIUM = 32; + uint256 internal constant _CALLDATA_LENGTH_PREMIUM = 16; uint256 internal constant _BASE_TRANSACTION_GAS_USED = 21_000; address public immutable GAS_PRICE_ORACLE; From 1dd15ac3d6c0111bde494a436825acdc60188d69 Mon Sep 17 00:00:00 2001 From: Ben Sparks <52714090+BenSparksCode@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:19:35 +0200 Subject: [PATCH 21/21] refactor: set surcharge rates in constructor --- script/deploy-atlas.s.sol | 8 +- src/contracts/atlas/AtlETH.sol | 12 ++- src/contracts/atlas/Atlas.sol | 12 ++- src/contracts/atlas/Escrow.sol | 12 ++- src/contracts/atlas/GasAccounting.sol | 28 ++++-- src/contracts/atlas/Permit69.sol | 12 ++- src/contracts/atlas/SafetyLocks.sol | 12 ++- src/contracts/atlas/Storage.sol | 8 +- src/contracts/libraries/AccountingMath.sol | 33 +++++-- test/AccountingMath.t.sol | 83 +++++++++------- test/{Atlas.t.sol => BidFinding.t.sol} | 59 +---------- test/DAppIntegration.t.sol | 5 + test/Factory.t.sol | 5 + test/GasAccounting.t.sol | 18 ++-- test/Permit69.t.sol | 85 ++++++++-------- test/SafetyLocks.t.sol | 2 +- test/Storage.t.sol | 108 +++++++++++++++++---- test/base/BaseTest.t.sol | 4 + test/base/TestAtlas.sol | 13 ++- 19 files changed, 327 insertions(+), 192 deletions(-) rename test/{Atlas.t.sol => BidFinding.t.sol} (63%) diff --git a/script/deploy-atlas.s.sol b/script/deploy-atlas.s.sol index 8531c62e..2cc7eb95 100644 --- a/script/deploy-atlas.s.sol +++ b/script/deploy-atlas.s.sol @@ -14,6 +14,10 @@ import { Sorter } from "src/contracts/helpers/Sorter.sol"; import { ExecutionEnvironment } from "src/contracts/common/ExecutionEnvironment.sol"; contract DeployAtlasScript is DeployBaseScript { + uint256 ESCROW_DURATION = 64; + uint256 ATLAS_SURCHARGE_RATE = 1_000_000; // 10% + uint256 BUNDLER_SURCHARGE_RATE = 1_000_000; // 10% + function run() external { console.log("\n=== DEPLOYING Atlas ===\n"); @@ -37,7 +41,9 @@ contract DeployAtlasScript is DeployBaseScript { ExecutionEnvironment execEnvTemplate = new ExecutionEnvironment(expectedAtlasAddr); atlas = new Atlas({ - escrowDuration: 64, + escrowDuration: ESCROW_DURATION, + atlasSurchargeRate: ATLAS_SURCHARGE_RATE, + bundlerSurchargeRate: BUNDLER_SURCHARGE_RATE, verification: expectedAtlasVerificationAddr, simulator: expectedSimulatorAddr, executionTemplate: address(execEnvTemplate), diff --git a/src/contracts/atlas/AtlETH.sol b/src/contracts/atlas/AtlETH.sol index e39120fb..d9bf8a65 100644 --- a/src/contracts/atlas/AtlETH.sol +++ b/src/contracts/atlas/AtlETH.sol @@ -11,12 +11,22 @@ import "src/contracts/types/EscrowTypes.sol"; abstract contract AtlETH is Permit69 { constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, address l2GasCalculator ) - Permit69(escrowDuration, verification, simulator, initialSurchargeRecipient, l2GasCalculator) + Permit69( + escrowDuration, + atlasSurchargeRate, + bundlerSurchargeRate, + verification, + simulator, + initialSurchargeRecipient, + l2GasCalculator + ) { } /*////////////////////////////////////////////////////////////// diff --git a/src/contracts/atlas/Atlas.sol b/src/contracts/atlas/Atlas.sol index c17d0825..909e4c56 100644 --- a/src/contracts/atlas/Atlas.sol +++ b/src/contracts/atlas/Atlas.sol @@ -28,13 +28,23 @@ contract Atlas is Escrow, Factory { constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, address l2GasCalculator, address executionTemplate ) - Escrow(escrowDuration, verification, simulator, initialSurchargeRecipient, l2GasCalculator) + Escrow( + escrowDuration, + atlasSurchargeRate, + bundlerSurchargeRate, + verification, + simulator, + initialSurchargeRecipient, + l2GasCalculator + ) Factory(executionTemplate) { } diff --git a/src/contracts/atlas/Escrow.sol b/src/contracts/atlas/Escrow.sol index db81645c..39a243f5 100644 --- a/src/contracts/atlas/Escrow.sol +++ b/src/contracts/atlas/Escrow.sol @@ -31,12 +31,22 @@ abstract contract Escrow is AtlETH { constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, address l2GasCalculator ) - AtlETH(escrowDuration, verification, simulator, initialSurchargeRecipient, l2GasCalculator) + AtlETH( + escrowDuration, + atlasSurchargeRate, + bundlerSurchargeRate, + verification, + simulator, + initialSurchargeRecipient, + l2GasCalculator + ) { if (escrowDuration == 0) revert InvalidEscrowDuration(); } diff --git a/src/contracts/atlas/GasAccounting.sol b/src/contracts/atlas/GasAccounting.sol index 63f9cdd4..ab1053b0 100644 --- a/src/contracts/atlas/GasAccounting.sol +++ b/src/contracts/atlas/GasAccounting.sol @@ -21,12 +21,22 @@ abstract contract GasAccounting is SafetyLocks { constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, address l2GasCalculator ) - SafetyLocks(escrowDuration, verification, simulator, initialSurchargeRecipient, l2GasCalculator) + SafetyLocks( + escrowDuration, + atlasSurchargeRate, + bundlerSurchargeRate, + verification, + simulator, + initialSurchargeRecipient, + l2GasCalculator + ) { } /// @notice Sets the initial accounting values for the metacall transaction. @@ -35,10 +45,10 @@ abstract contract GasAccounting is SafetyLocks { uint256 _rawClaims = (FIXED_GAS_OFFSET + gasMarker) * tx.gasprice; // Set any withdraws or deposits - _setClaims(_rawClaims.withBundlerSurcharge()); + _setClaims(_rawClaims.withSurcharge(BUNDLER_SURCHARGE_RATE)); // Atlas surcharge is based on the raw claims value. - _setFees(_rawClaims.getAtlasSurcharge()); + _setFees(_rawClaims.getSurcharge(ATLAS_SURCHARGE_RATE)); _setDeposits(msg.value); // Explicitly set writeoffs and withdrawals to 0 in case multiple metacalls in single tx. @@ -281,16 +291,16 @@ abstract contract GasAccounting is SafetyLocks { if (result.bundlersFault()) { // CASE: Solver is not responsible for the failure of their operation, so we blame the bundler // and reduce the total amount refunded to the bundler - _setWriteoffs(writeoffs() + _gasUsed.withAtlasAndBundlerSurcharges()); + _setWriteoffs(writeoffs() + _gasUsed.withSurcharges(ATLAS_SURCHARGE_RATE, BUNDLER_SURCHARGE_RATE)); } else { // CASE: Solver failed, so we calculate what they owe. - uint256 _gasUsedWithSurcharges = _gasUsed.withAtlasAndBundlerSurcharges(); + uint256 _gasUsedWithSurcharges = _gasUsed.withSurcharges(ATLAS_SURCHARGE_RATE, BUNDLER_SURCHARGE_RATE); _assign(solverOp.from, _gasUsedWithSurcharges, _gasUsedWithSurcharges, false); } } function _writeOffBidFindGasCost(uint256 gasUsed) internal { - _setWriteoffs(writeoffs() + gasUsed.withAtlasAndBundlerSurcharges()); + _setWriteoffs(writeoffs() + gasUsed.withSurcharges(ATLAS_SURCHARGE_RATE, BUNDLER_SURCHARGE_RATE)); } /// @param ctx Context struct containing relevant context information for the Atlas auction. @@ -332,9 +342,9 @@ abstract contract GasAccounting is SafetyLocks { uint256 _gasRemainder = _gasLeft * tx.gasprice; // Calculate the preadjusted netAtlasGasSurcharge - netAtlasGasSurcharge = _fees - _gasRemainder.getAtlasSurcharge(); + netAtlasGasSurcharge = _fees - _gasRemainder.getSurcharge(ATLAS_SURCHARGE_RATE); - adjustedClaims -= _gasRemainder.withBundlerSurcharge(); + adjustedClaims -= _gasRemainder.withSurcharge(BUNDLER_SURCHARGE_RATE); adjustedWithdrawals += netAtlasGasSurcharge; S_cumulativeSurcharge = _surcharge + netAtlasGasSurcharge; // Update the cumulative surcharge @@ -342,7 +352,7 @@ abstract contract GasAccounting is SafetyLocks { // gas rebate. By reducing the claims, solvers end up paying less in total. if (ctx.solverCount > 0) { // Calculate the unadjusted bundler gas surcharge - uint256 _grossBundlerGasSurcharge = adjustedClaims.withoutBundlerSurcharge(); + uint256 _grossBundlerGasSurcharge = adjustedClaims.withoutSurcharge(BUNDLER_SURCHARGE_RATE); // Calculate an estimate for how much gas should be remaining // NOTE: There is a free buffer of one SolverOperation because solverIndex starts at 0. diff --git a/src/contracts/atlas/Permit69.sol b/src/contracts/atlas/Permit69.sol index 6d6f7d0b..44f5bc45 100644 --- a/src/contracts/atlas/Permit69.sol +++ b/src/contracts/atlas/Permit69.sol @@ -26,12 +26,22 @@ import "src/contracts/types/EscrowTypes.sol"; abstract contract Permit69 is GasAccounting { constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, address l2GasCalculator ) - GasAccounting(escrowDuration, verification, simulator, initialSurchargeRecipient, l2GasCalculator) + GasAccounting( + escrowDuration, + atlasSurchargeRate, + bundlerSurchargeRate, + verification, + simulator, + initialSurchargeRecipient, + l2GasCalculator + ) { } /// @notice Verifies that the caller is an authorized Execution Environment contract. diff --git a/src/contracts/atlas/SafetyLocks.sol b/src/contracts/atlas/SafetyLocks.sol index 2b3d66b9..ad9384f3 100644 --- a/src/contracts/atlas/SafetyLocks.sol +++ b/src/contracts/atlas/SafetyLocks.sol @@ -18,12 +18,22 @@ abstract contract SafetyLocks is Storage { constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, address l2GasCalculator ) - Storage(escrowDuration, verification, simulator, initialSurchargeRecipient, l2GasCalculator) + Storage( + escrowDuration, + atlasSurchargeRate, + bundlerSurchargeRate, + verification, + simulator, + initialSurchargeRecipient, + l2GasCalculator + ) { } /// @notice Sets the Atlas lock to the specified execution environment. diff --git a/src/contracts/atlas/Storage.sol b/src/contracts/atlas/Storage.sol index 4a20052b..251e4892 100644 --- a/src/contracts/atlas/Storage.sol +++ b/src/contracts/atlas/Storage.sol @@ -18,6 +18,8 @@ contract Storage is AtlasEvents, AtlasErrors, AtlasConstants { address public immutable SIMULATOR; address public immutable L2_GAS_CALCULATOR; uint256 public immutable ESCROW_DURATION; + uint256 public immutable ATLAS_SURCHARGE_RATE; + uint256 public immutable BUNDLER_SURCHARGE_RATE; // AtlETH public constants // These constants double as interface functions for the ERC20 standard, hence the lowercase naming convention. @@ -26,8 +28,6 @@ contract Storage is AtlasEvents, AtlasErrors, AtlasConstants { uint8 public constant decimals = 18; // Gas Accounting public constants - uint256 public constant ATLAS_SURCHARGE_RATE = AccountingMath._ATLAS_SURCHARGE_RATE; - uint256 public constant BUNDLER_SURCHARGE_RATE = AccountingMath._BUNDLER_SURCHARGE_RATE; uint256 public constant SCALE = AccountingMath._SCALE; uint256 public constant FIXED_GAS_OFFSET = AccountingMath._FIXED_GAS_OFFSET; @@ -56,6 +56,8 @@ contract Storage is AtlasEvents, AtlasErrors, AtlasConstants { constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, @@ -67,6 +69,8 @@ contract Storage is AtlasEvents, AtlasErrors, AtlasConstants { SIMULATOR = simulator; L2_GAS_CALCULATOR = l2GasCalculator; ESCROW_DURATION = escrowDuration; + ATLAS_SURCHARGE_RATE = atlasSurchargeRate; + BUNDLER_SURCHARGE_RATE = bundlerSurchargeRate; // Gas Accounting // Initialized with msg.value to seed flash loan liquidity diff --git a/src/contracts/libraries/AccountingMath.sol b/src/contracts/libraries/AccountingMath.sol index d30c044e..2fd43783 100644 --- a/src/contracts/libraries/AccountingMath.sol +++ b/src/contracts/libraries/AccountingMath.sol @@ -3,27 +3,40 @@ pragma solidity 0.8.25; library AccountingMath { // Gas Accounting public constants - uint256 internal constant _ATLAS_SURCHARGE_RATE = 1_000_000; // out of 10_000_000 = 10% - uint256 internal constant _BUNDLER_SURCHARGE_RATE = 1_000_000; // out of 10_000_000 = 10% uint256 internal constant _SOLVER_GAS_LIMIT_BUFFER_PERCENTAGE = 500_000; // out of 10_000_000 = 5% uint256 internal constant _SCALE = 10_000_000; // 10_000_000 / 10_000_000 = 100% uint256 internal constant _FIXED_GAS_OFFSET = 85_000; - function withBundlerSurcharge(uint256 amount) internal pure returns (uint256 adjustedAmount) { - adjustedAmount = amount * (_SCALE + _BUNDLER_SURCHARGE_RATE) / _SCALE; + function withSurcharge(uint256 amount, uint256 surchargeRate) internal pure returns (uint256 adjustedAmount) { + adjustedAmount = amount * (_SCALE + surchargeRate) / _SCALE; } - function withoutBundlerSurcharge(uint256 amount) internal pure returns (uint256 unadjustedAmount) { - unadjustedAmount = amount * _SCALE / (_SCALE + _BUNDLER_SURCHARGE_RATE); + function withoutSurcharge(uint256 amount, uint256 surchargeRate) internal pure returns (uint256 unadjustedAmount) { + unadjustedAmount = amount * _SCALE / (_SCALE + surchargeRate); } - function withAtlasAndBundlerSurcharges(uint256 amount) internal pure returns (uint256 adjustedAmount) { - adjustedAmount = amount * (_SCALE + _ATLAS_SURCHARGE_RATE + _BUNDLER_SURCHARGE_RATE) / _SCALE; + function withSurcharges( + uint256 amount, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate + ) + internal + pure + returns (uint256 adjustedAmount) + { + adjustedAmount = amount * (_SCALE + atlasSurchargeRate + bundlerSurchargeRate) / _SCALE; } // gets the Atlas surcharge from an unadjusted amount - function getAtlasSurcharge(uint256 amount) internal pure returns (uint256 surcharge) { - surcharge = amount * _ATLAS_SURCHARGE_RATE / _SCALE; + function getSurcharge( + uint256 unadjustedAmount, + uint256 surchargeRate + ) + internal + pure + returns (uint256 surchargeAmount) + { + surchargeAmount = unadjustedAmount * surchargeRate / _SCALE; } function solverGasLimitScaledDown( diff --git a/test/AccountingMath.t.sol b/test/AccountingMath.t.sol index a9b26d18..69cb1263 100644 --- a/test/AccountingMath.t.sol +++ b/test/AccountingMath.t.sol @@ -5,68 +5,83 @@ import "forge-std/Test.sol"; import "src/contracts/libraries/AccountingMath.sol"; contract AccountingMathTest is Test { + uint256 DEFAULT_ATLAS_SURCHARGE_RATE = 1_000_000; // 10% + uint256 DEFAULT_BUNDLER_SURCHARGE_RATE = 1_000_000; // 10% + function testWithBundlerSurcharge() public { - assertEq(AccountingMath.withBundlerSurcharge(0), uint256(0)); - assertEq(AccountingMath.withBundlerSurcharge(1), uint256(1)); - assertEq(AccountingMath.withBundlerSurcharge(11), uint256(12)); - assertEq(AccountingMath.withBundlerSurcharge(100), uint256(110)); - assertEq(AccountingMath.withBundlerSurcharge(1e18), uint256(11e17)); + assertEq(AccountingMath.withSurcharge(0, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(0)); + assertEq(AccountingMath.withSurcharge(1, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(1)); + assertEq(AccountingMath.withSurcharge(11, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(12)); + assertEq(AccountingMath.withSurcharge(100, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(110)); + assertEq(AccountingMath.withSurcharge(1e18, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(11e17)); vm.expectRevert(); - AccountingMath.withBundlerSurcharge(type(uint256).max); + AccountingMath.withSurcharge(type(uint256).max, DEFAULT_BUNDLER_SURCHARGE_RATE); } function testWithoutBundlerSurcharge() public { - assertEq(AccountingMath.withoutBundlerSurcharge(0), uint256(0)); - assertEq(AccountingMath.withoutBundlerSurcharge(1), uint256(0)); - assertEq(AccountingMath.withoutBundlerSurcharge(12), uint256(10)); - assertEq(AccountingMath.withoutBundlerSurcharge(110), uint256(100)); - assertEq(AccountingMath.withoutBundlerSurcharge(11e17), uint256(1e18)); + assertEq(AccountingMath.withoutSurcharge(0, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(0)); + assertEq(AccountingMath.withoutSurcharge(1, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(0)); + assertEq(AccountingMath.withoutSurcharge(12, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(10)); + assertEq(AccountingMath.withoutSurcharge(110, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(100)); + assertEq(AccountingMath.withoutSurcharge(11e17, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(1e18)); vm.expectRevert(); - AccountingMath.withoutBundlerSurcharge(type(uint256).max); + AccountingMath.withoutSurcharge(type(uint256).max, DEFAULT_BUNDLER_SURCHARGE_RATE); } function testWithAtlasAndBundlerSurcharges() public { - assertEq(AccountingMath.withAtlasAndBundlerSurcharges(0), uint256(0)); - assertEq(AccountingMath.withAtlasAndBundlerSurcharges(1), uint256(1)); - assertEq(AccountingMath.withAtlasAndBundlerSurcharges(10), uint256(12)); - assertEq(AccountingMath.withAtlasAndBundlerSurcharges(100), uint256(120)); - assertEq(AccountingMath.withAtlasAndBundlerSurcharges(1e18), uint256(12e17)); + assertEq( + AccountingMath.withSurcharges(0, DEFAULT_ATLAS_SURCHARGE_RATE, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(0) + ); + assertEq( + AccountingMath.withSurcharges(1, DEFAULT_ATLAS_SURCHARGE_RATE, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(1) + ); + assertEq( + AccountingMath.withSurcharges(10, DEFAULT_ATLAS_SURCHARGE_RATE, DEFAULT_BUNDLER_SURCHARGE_RATE), uint256(12) + ); + assertEq( + AccountingMath.withSurcharges(100, DEFAULT_ATLAS_SURCHARGE_RATE, DEFAULT_BUNDLER_SURCHARGE_RATE), + uint256(120) + ); + assertEq( + AccountingMath.withSurcharges(1e18, DEFAULT_ATLAS_SURCHARGE_RATE, DEFAULT_BUNDLER_SURCHARGE_RATE), + uint256(12e17) + ); vm.expectRevert(); - AccountingMath.withAtlasAndBundlerSurcharges(type(uint256).max); + AccountingMath.withSurcharges(type(uint256).max, DEFAULT_ATLAS_SURCHARGE_RATE, DEFAULT_BUNDLER_SURCHARGE_RATE); } function testGetAtlasSurcharge() public { - assertEq(AccountingMath.getAtlasSurcharge(0), uint256(0)); - assertEq(AccountingMath.getAtlasSurcharge(10), uint256(1)); - assertEq(AccountingMath.getAtlasSurcharge(20), uint256(2)); - assertEq(AccountingMath.getAtlasSurcharge(30), uint256(3)); - assertEq(AccountingMath.getAtlasSurcharge(100), uint256(10)); - assertEq(AccountingMath.getAtlasSurcharge(1_000_000), uint256(100_000)); - assertEq(AccountingMath.getAtlasSurcharge(1e18), uint256(1e17)); + assertEq(AccountingMath.getSurcharge(0, DEFAULT_ATLAS_SURCHARGE_RATE), uint256(0)); + assertEq(AccountingMath.getSurcharge(10, DEFAULT_ATLAS_SURCHARGE_RATE), uint256(1)); + assertEq(AccountingMath.getSurcharge(20, DEFAULT_ATLAS_SURCHARGE_RATE), uint256(2)); + assertEq(AccountingMath.getSurcharge(30, DEFAULT_ATLAS_SURCHARGE_RATE), uint256(3)); + assertEq(AccountingMath.getSurcharge(100, DEFAULT_ATLAS_SURCHARGE_RATE), uint256(10)); + assertEq(AccountingMath.getSurcharge(1_000_000, DEFAULT_ATLAS_SURCHARGE_RATE), uint256(100_000)); + assertEq(AccountingMath.getSurcharge(1e18, DEFAULT_ATLAS_SURCHARGE_RATE), uint256(1e17)); vm.expectRevert(); - AccountingMath.getAtlasSurcharge(type(uint256).max); + AccountingMath.getSurcharge(type(uint256).max, DEFAULT_ATLAS_SURCHARGE_RATE); } function testSolverGasLimitScaledDown() public { assertEq(AccountingMath.solverGasLimitScaledDown(0, 100), uint256(0)); assertEq(AccountingMath.solverGasLimitScaledDown(50, 100), uint256(47)); // 50 * 10_000_000 / 10_500_000 assertEq(AccountingMath.solverGasLimitScaledDown(100, 200), uint256(95)); - + assertEq(AccountingMath.solverGasLimitScaledDown(200, 100), uint256(95)); assertEq(AccountingMath.solverGasLimitScaledDown(300, 200), uint256(190)); - + assertEq(AccountingMath.solverGasLimitScaledDown(100, 100), uint256(95)); assertEq(AccountingMath.solverGasLimitScaledDown(200, 200), uint256(190)); - - assertEq(AccountingMath.solverGasLimitScaledDown(1_000_000, 500_000), uint256(476_190)); // 500_000 * 10_000_000 / 10_500_000 - assertEq(AccountingMath.solverGasLimitScaledDown(1e18, 1e18), uint256(952380952380952380)); - + + assertEq(AccountingMath.solverGasLimitScaledDown(1_000_000, 500_000), uint256(476_190)); // 500_000 * 10_000_000 + // / 10_500_000 + assertEq(AccountingMath.solverGasLimitScaledDown(1e18, 1e18), uint256(952_380_952_380_952_380)); + vm.expectRevert(); assertEq(AccountingMath.solverGasLimitScaledDown(type(uint256).max, type(uint256).max), type(uint256).max); - + assertEq(AccountingMath.solverGasLimitScaledDown(1, 2), uint256(0)); // 1 * 10_000_000 / 10_500_000 assertEq(AccountingMath.solverGasLimitScaledDown(3, 3), uint256(2)); // 3 * 10_000_000 / 10_500_000 assertEq(AccountingMath.solverGasLimitScaledDown(5, 10), uint256(4)); // 5 * 10_000_000 / 10_500_000 - } } diff --git a/test/Atlas.t.sol b/test/BidFinding.t.sol similarity index 63% rename from test/Atlas.t.sol rename to test/BidFinding.t.sol index f64d010c..fb61ab6e 100644 --- a/test/Atlas.t.sol +++ b/test/BidFinding.t.sol @@ -20,38 +20,11 @@ import "src/contracts/types/LockTypes.sol"; import { LibSort } from "solady/utils/LibSort.sol"; -// These tests focus on the functions found in the Atlas.sol file -contract AtlasTest is BaseTest { - - function setUp_bidFindingIteration() public { - // super.setUp(); - - // vm.startPrank(payee); - // simulator = new Simulator(); - - // // Computes the addresses at which AtlasVerification will be deployed - // address expectedAtlasAddr = vm.computeCreateAddress(payee, vm.getNonce(payee) + 1); - // address expectedAtlasVerificationAddr = vm.computeCreateAddress(payee, vm.getNonce(payee) + 2); - // bytes32 salt = keccak256(abi.encodePacked(block.chainid, expectedAtlasAddr, "AtlasFactory 1.0")); - // ExecutionEnvironment execEnvTemplate = new ExecutionEnvironment{ salt: salt }(expectedAtlasAddr); - - // atlas = new MockAtlas({ - // escrowDuration: 64, - // verification: expectedAtlasVerificationAddr, - // simulator: address(simulator), - // executionTemplate: address(execEnvTemplate), - // surchargeRecipient: payee - // }); - // atlasVerification = new AtlasVerification(address(atlas)); - // simulator.setAtlas(address(atlas)); - // sorter = new Sorter(address(atlas)); - // vm.stopPrank(); - } - +contract BidFindingTest is BaseTest { function test_bidFindingIteration_sortingOrder() public pure { // Test order of bidsAndIndices after insertionSort - // 3 items. [200, 0, 100] --> [0, 100, 200] + // 3 items. [200, 0, 100] --> [0, 100, 200] uint256[] memory bidsAndIndices = new uint256[](3); bidsAndIndices[0] = 100; bidsAndIndices[1] = 0; @@ -80,7 +53,7 @@ contract AtlasTest is BaseTest { } function test_bidFindingIteration_packBidAndIndex() public pure { - uint256 bid = 12345; + uint256 bid = 12_345; uint256 index = 2; uint256 packed = _packBidAndIndex(bid, index); @@ -113,7 +86,6 @@ contract AtlasTest is BaseTest { assertEq(unpackedIndex, index); } - // Packs bid and index into a single uint256, replicates logic used in `_bidFindingIteration()` function _packBidAndIndex(uint256 bid, uint256 index) internal pure returns (uint256) { return uint256(bid << 16 | uint16(index)); @@ -127,29 +99,4 @@ contract AtlasTest is BaseTest { // uint256 solverOpsIndex = bidsAndIndices[i] & FIRST_16_BITS_MASK; index = packed & uint256(0xFFFF); } -} - -// MockAtlas exposes Atlas' internal functions for testing -contract MockAtlas is Atlas { - constructor( - uint256 _escrowDuration, - address _verification, - address _simulator, - address _surchargeRecipient, - address _l2GasCalculator, - address _executionTemplate - ) - Atlas(_escrowDuration, _verification, _simulator, _surchargeRecipient, _l2GasCalculator, _executionTemplate) - { } - - function bidFindingIteration( - DAppConfig calldata dConfig, - UserOperation calldata userOp, - SolverOperation[] calldata solverOps, - bytes memory returnData, - Context memory ctx - ) public returns (Context memory) { - _bidFindingIteration(ctx, dConfig, userOp, solverOps, returnData); - return ctx; - } } \ No newline at end of file diff --git a/test/DAppIntegration.t.sol b/test/DAppIntegration.t.sol index 7bbb5c08..b136b27c 100644 --- a/test/DAppIntegration.t.sol +++ b/test/DAppIntegration.t.sol @@ -16,6 +16,9 @@ contract MockDAppIntegration is DAppIntegration { } contract DAppIntegrationTest is Test { + uint256 DEFAULT_ATLAS_SURCHARGE_RATE = 1_000_000; // 10% + uint256 DEFAULT_BUNDLER_SURCHARGE_RATE = 1_000_000; // 10% + Atlas public atlas; MockDAppIntegration public dAppIntegration; DummyDAppControl public dAppControl; @@ -40,6 +43,8 @@ contract DAppIntegrationTest is Test { // Deploy the Atlas contract with correct parameters atlas = new Atlas({ escrowDuration: 64, + atlasSurchargeRate: DEFAULT_ATLAS_SURCHARGE_RATE, + bundlerSurchargeRate: DEFAULT_BUNDLER_SURCHARGE_RATE, verification: address(atlasVerification), simulator: address(0), executionTemplate: address(execEnvTemplate), diff --git a/test/Factory.t.sol b/test/Factory.t.sol index 814db49c..9da4b498 100644 --- a/test/Factory.t.sol +++ b/test/Factory.t.sol @@ -47,6 +47,9 @@ contract MockFactory is Factory, Test { } contract FactoryTest is Test { + uint256 DEFAULT_ATLAS_SURCHARGE_RATE = 1_000_000; // 10% + uint256 DEFAULT_BUNDLER_SURCHARGE_RATE = 1_000_000; // 10% + Atlas public atlas; AtlasVerification public atlasVerification; MockFactory public mockFactory; @@ -66,6 +69,8 @@ contract FactoryTest is Test { vm.startPrank(deployer); atlas = new Atlas({ escrowDuration: 64, + atlasSurchargeRate: DEFAULT_ATLAS_SURCHARGE_RATE, + bundlerSurchargeRate: DEFAULT_BUNDLER_SURCHARGE_RATE, verification: expectedAtlasVerificationAddr, simulator: address(0), executionTemplate: address(execEnvTemplate), diff --git a/test/GasAccounting.t.sol b/test/GasAccounting.t.sol index 39f62bfb..daaea648 100644 --- a/test/GasAccounting.t.sol +++ b/test/GasAccounting.t.sol @@ -30,13 +30,15 @@ contract MockGasAccounting is TestAtlas, BaseTest { constructor( uint256 _escrowDuration, + uint256 _atlasSurchargeRate, + uint256 _bundlerSurchargeRate, address _verification, address _simulator, address _surchargeRecipient, address _l2GasCalculator, address _executionTemplate ) - TestAtlas(_escrowDuration, _verification, _simulator, _surchargeRecipient, _l2GasCalculator, _executionTemplate) + TestAtlas(_escrowDuration, _atlasSurchargeRate, _bundlerSurchargeRate, _verification, _simulator, _surchargeRecipient, _l2GasCalculator, _executionTemplate) { } ///////////////////////////////////////////////////////// @@ -172,14 +174,6 @@ contract MockGasAccounting is TestAtlas, BaseTest { function getFixedGasOffset() external pure returns (uint256) { return AccountingMath._FIXED_GAS_OFFSET; } - - function getAtlasSurchargeRate() external pure returns (uint256) { - return AccountingMath._ATLAS_SURCHARGE_RATE; - } - - function getBundlerSurchargeRate() external pure returns (uint256) { - return AccountingMath._BUNDLER_SURCHARGE_RATE; - } } contract MockGasCalculator is IL2GasCalculator, Test { @@ -212,6 +206,8 @@ contract GasAccountingTest is AtlasConstants, BaseTest { // Initialize MockGasAccounting mockGasAccounting = new MockGasAccounting( DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, address(atlasVerification), address(simulator), deployer, @@ -973,7 +969,7 @@ contract GasAccountingTest is AtlasConstants, BaseTest { (gasWaterMark + mockGasAccounting.getSolverBaseGasUsed() - gasleft()) * tx.gasprice + gasUsedOffset; mockGasAccounting.handleSolverAccounting(solverOp, gasWaterMark, result, false); - uint256 expectedWriteoffs = initialWriteoffs + AccountingMath.withAtlasAndBundlerSurcharges(gasUsed); + uint256 expectedWriteoffs = initialWriteoffs + AccountingMath.withSurcharges(gasUsed, DEFAULT_ATLAS_SURCHARGE_RATE, DEFAULT_BUNDLER_SURCHARGE_RATE); // Verify writeoffs have increased assertApproxEqRel( mockGasAccounting.getWriteoffs(), @@ -1110,6 +1106,8 @@ contract GasAccountingTest is AtlasConstants, BaseTest { IL2GasCalculator gasCalculator = new MockGasCalculator(); MockGasAccounting mockL2GasAccounting = new MockGasAccounting( DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, address(atlasVerification), address(simulator), deployer, diff --git a/test/Permit69.t.sol b/test/Permit69.t.sol index b5b47f4c..2262ad3b 100644 --- a/test/Permit69.t.sol +++ b/test/Permit69.t.sol @@ -22,7 +22,6 @@ import "src/contracts/types/LockTypes.sol"; import "src/contracts/types/DAppOperation.sol"; contract Permit69Test is BaseTest { - Context ctx; address mockUser; @@ -42,10 +41,12 @@ contract Permit69Test is BaseTest { address expectedFactoryAddr = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 2); bytes32 salt = keccak256(abi.encodePacked(block.chainid, expectedFactoryAddr)); ExecutionEnvironment execEnvTemplate = new ExecutionEnvironment{ salt: salt }(expectedFactoryAddr); - + vm.startPrank(deployer); mockAtlas = new MockAtlasForPermit69Tests({ _escrowDuration: 64, + _atlasSurchargeRate: DEFAULT_ATLAS_SURCHARGE_RATE, + _bundlerSurchargeRate: DEFAULT_BUNDLER_SURCHARGE_RATE, _verification: expectedAtlasVerificationAddr, _simulator: address(0), _executionTemplate: address(execEnvTemplate), @@ -97,9 +98,7 @@ contract Permit69Test is BaseTest { vm.prank(solverOneEOA); vm.expectRevert(AtlasErrors.InvalidEnvironment.selector); - mockAtlas.transferUserERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferUserERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); } function testTransferUserERC20RevertsIfLockStateNotValid() public { @@ -115,27 +114,21 @@ contract Permit69Test is BaseTest { // Uninitialized vm.expectRevert(AtlasErrors.InvalidLockState.selector); - mockAtlas.transferUserERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferUserERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); // AllocateValue phase = ExecutionPhase.AllocateValue; mockAtlas.setContext(ctx); mockAtlas.setPhase(phase); vm.expectRevert(AtlasErrors.InvalidLockState.selector); - mockAtlas.transferUserERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferUserERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); // Releasing phase = ExecutionPhase.Uninitialized; mockAtlas.setContext(ctx); mockAtlas.setPhase(phase); vm.expectRevert(AtlasErrors.InvalidLockState.selector); - mockAtlas.transferUserERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferUserERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); vm.stopPrank(); } @@ -157,9 +150,7 @@ contract Permit69Test is BaseTest { WETH.approve(address(mockAtlas), wethTransferred); vm.prank(mockExecutionEnvAddress); - mockAtlas.transferUserERC20( - WETH_ADDRESS, solverOneEOA, wethTransferred, mockUser, mockDAppControl - ); + mockAtlas.transferUserERC20(WETH_ADDRESS, solverOneEOA, wethTransferred, mockUser, mockDAppControl); assertEq(WETH.balanceOf(mockUser), userWethBefore - wethTransferred, "User did not lose WETH"); assertEq(WETH.balanceOf(solverOneEOA), solverWethBefore + wethTransferred, "Solver did not gain WETH"); @@ -173,9 +164,7 @@ contract Permit69Test is BaseTest { vm.prank(solverOneEOA); vm.expectRevert(AtlasErrors.InvalidEnvironment.selector); - mockAtlas.transferDAppERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferDAppERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); } function testTransferDAppERC20RevertsIfLockStateNotValid() public { @@ -189,33 +178,25 @@ contract Permit69Test is BaseTest { ExecutionPhase phase = ExecutionPhase.Uninitialized; mockAtlas.setPhase(phase); vm.expectRevert(AtlasErrors.InvalidLockState.selector); - mockAtlas.transferDAppERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferDAppERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); // UserOperation phase = ExecutionPhase.UserOperation; mockAtlas.setPhase(phase); vm.expectRevert(AtlasErrors.InvalidLockState.selector); - mockAtlas.transferDAppERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferDAppERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); // SolverOperations phase = ExecutionPhase.SolverOperation; mockAtlas.setPhase(phase); vm.expectRevert(AtlasErrors.InvalidLockState.selector); - mockAtlas.transferDAppERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferDAppERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); // Releasing phase = ExecutionPhase.Uninitialized; mockAtlas.setPhase(phase); vm.expectRevert(AtlasErrors.InvalidLockState.selector); - mockAtlas.transferDAppERC20( - WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl - ); + mockAtlas.transferDAppERC20(WETH_ADDRESS, solverOneEOA, 10e18, mockUser, mockDAppControl); vm.stopPrank(); } @@ -236,9 +217,7 @@ contract Permit69Test is BaseTest { WETH.approve(address(mockAtlas), wethTransferred); vm.prank(mockExecutionEnvAddress); - mockAtlas.transferDAppERC20( - WETH_ADDRESS, solverOneEOA, wethTransferred, mockUser, mockDAppControl - ); + mockAtlas.transferDAppERC20(WETH_ADDRESS, solverOneEOA, wethTransferred, mockUser, mockDAppControl); assertEq(WETH.balanceOf(mockDAppControl), dAppWethBefore - wethTransferred, "DApp did not lose WETH"); assertEq(WETH.balanceOf(solverOneEOA), solverWethBefore + wethTransferred, "Solver did not gain WETH"); @@ -279,7 +258,7 @@ contract Permit69Test is BaseTest { function testConstantValueOfSafeDAppTransfer() public { // FIXME: fix before merging spearbit-reaudit branch vm.skip(true); - + string memory expectedBitMapString = "0000111010100000"; // Safe phases for dApp transfers are PreOps, AllocateValue, and DAppOperation // preOpsPhaseSafe = 0000 0000 0010 0000 @@ -293,8 +272,8 @@ contract Permit69Test is BaseTest { // verificationPhaseSafe = 0000 0100 0000 0000 uint8 verificationPhaseSafe = uint8(ExecutionPhase.PostOps); - uint16 expectedSafeDAppTransferBitMap = - preOpsPhaseSafe | preSolverOpsPhaseSafe | postSolverOpsPhaseSafe | allocateValuePhaseSafe | verificationPhaseSafe; + uint16 expectedSafeDAppTransferBitMap = preOpsPhaseSafe | preSolverOpsPhaseSafe | postSolverOpsPhaseSafe + | allocateValuePhaseSafe | verificationPhaseSafe; assertEq( mockAtlas.getSafeDAppTransfer(), @@ -323,13 +302,24 @@ contract Permit69Test is BaseTest { contract MockAtlasForPermit69Tests is Atlas { constructor( uint256 _escrowDuration, + uint256 _atlasSurchargeRate, + uint256 _bundlerSurchargeRate, address _verification, address _simulator, address _surchargeRecipient, address _l2GasCalculator, address _executionTemplate ) - Atlas(_escrowDuration, _verification, _simulator, _surchargeRecipient, _l2GasCalculator, _executionTemplate) + Atlas( + _escrowDuration, + _atlasSurchargeRate, + _bundlerSurchargeRate, + _verification, + _simulator, + _surchargeRecipient, + _l2GasCalculator, + _executionTemplate + ) { } // Declared in SafetyLocks.sol in the canonical Atlas system @@ -354,10 +344,7 @@ contract MockAtlasForPermit69Tests is Atlas { _environment = _activeEnvironment; } - function setLock( - address _activeEnvironment, - uint32 callConfig - ) public { + function setLock(address _activeEnvironment, uint32 callConfig) public { _setLock({ activeEnvironment: _activeEnvironment, callConfig: callConfig, @@ -376,12 +363,20 @@ contract MockAtlasForPermit69Tests is Atlas { uint32 callConfig ) public - returns (address executionEnvironment) + returns (address executionEnvironment) { return _getOrCreateExecutionEnvironment(user, control, callConfig); } - function verifyUserControlExecutionEnv(address sender, address user, address control, uint32 callConfig) internal view returns (bool) + function verifyUserControlExecutionEnv( + address sender, + address user, + address control, + uint32 callConfig + ) + internal + view + returns (bool) { return _verifyUserControlExecutionEnv(sender, user, control, callConfig); } diff --git a/test/SafetyLocks.t.sol b/test/SafetyLocks.t.sol index 5fab4b49..0cef1f73 100644 --- a/test/SafetyLocks.t.sol +++ b/test/SafetyLocks.t.sol @@ -11,7 +11,7 @@ import "src/contracts/types/ConfigTypes.sol"; import "src/contracts/types/LockTypes.sol"; contract MockSafetyLocks is SafetyLocks { - constructor() SafetyLocks(0, address(0), address(0), address(0), address(0)) { } + constructor() SafetyLocks(0, 1000000, 1000000, address(0), address(0), address(0), address(0)) { } function initializeLock( address executionEnvironment, diff --git a/test/Storage.t.sol b/test/Storage.t.sol index 536cbe69..a3e07beb 100644 --- a/test/Storage.t.sol +++ b/test/Storage.t.sol @@ -11,8 +11,6 @@ import { BaseTest } from "test/base/BaseTest.t.sol"; contract StorageTest is BaseTest { using stdStorage for StdStorage; - uint256 constant DEFAULT_ATLAS_SURCHARGE_RATE = 1_000_000; // out of 10_000_000 = 10% - uint256 constant DEFAULT_BUNDLER_SURCHARGE_RATE = 1_000_000; // out of 10_000_000 = 10% uint256 constant DEFAULT_SCALE = 10_000_000; // out of 10_000_000 = 100% uint256 constant DEFAULT_FIXED_GAS_OFFSET = 85_000; @@ -32,7 +30,9 @@ contract StorageTest is BaseTest { assertEq(atlas.decimals(), 18, "decimals set incorrectly"); assertEq(atlas.ATLAS_SURCHARGE_RATE(), DEFAULT_ATLAS_SURCHARGE_RATE, "ATLAS_SURCHARGE_RATE set incorrectly"); - assertEq(atlas.BUNDLER_SURCHARGE_RATE(), DEFAULT_BUNDLER_SURCHARGE_RATE, "BUNDLER_SURCHARGE_RATE set incorrectly"); + assertEq( + atlas.BUNDLER_SURCHARGE_RATE(), DEFAULT_BUNDLER_SURCHARGE_RATE, "BUNDLER_SURCHARGE_RATE set incorrectly" + ); assertEq(atlas.SCALE(), DEFAULT_SCALE, "SCALE set incorrectly"); assertEq(atlas.FIXED_GAS_OFFSET(), DEFAULT_FIXED_GAS_OFFSET, "FIXED_GAS_OFFSET set incorrectly"); } @@ -45,7 +45,7 @@ contract StorageTest is BaseTest { vm.deal(userEOA, depositAmount); vm.prank(userEOA); - atlas.deposit{value: depositAmount}(); + atlas.deposit{ value: depositAmount }(); assertEq(atlas.totalSupply(), startTotalSupply + depositAmount, "totalSupply did not increase correctly"); } @@ -56,14 +56,20 @@ contract StorageTest is BaseTest { vm.deal(userEOA, depositAmount); vm.prank(userEOA); - atlas.depositAndBond{value: depositAmount}(depositAmount); + atlas.depositAndBond{ value: depositAmount }(depositAmount); assertEq(atlas.bondedTotalSupply(), depositAmount, "bondedTotalSupply did not increase correctly"); } function test_storage_view_accessData() public { uint256 depositAmount = 1e18; - (uint256 bonded, uint256 lastAccessedBlock, uint256 auctionWins, uint256 auctionFails, uint256 totalGasValueUsed) = atlas.accessData(userEOA); + ( + uint256 bonded, + uint256 lastAccessedBlock, + uint256 auctionWins, + uint256 auctionFails, + uint256 totalGasValueUsed + ) = atlas.accessData(userEOA); assertEq(bonded, 0, "user bonded should start as 0"); assertEq(lastAccessedBlock, 0, "user lastAccessedBlock should start as 0"); @@ -73,7 +79,7 @@ contract StorageTest is BaseTest { vm.deal(userEOA, depositAmount); vm.prank(userEOA); - atlas.depositAndBond{value: depositAmount}(depositAmount); + atlas.depositAndBond{ value: depositAmount }(depositAmount); (bonded, lastAccessedBlock, auctionWins, auctionFails, totalGasValueUsed) = atlas.accessData(userEOA); @@ -96,7 +102,15 @@ contract StorageTest is BaseTest { } function test_storage_view_solverOpHashes() public { - MockStorage mockStorage = new MockStorage(DEFAULT_ESCROW_DURATION, address(0), address(0), address(0), address(0)); + MockStorage mockStorage = new MockStorage( + DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, + address(0), + address(0), + address(0), + address(0) + ); bytes32 testHash = keccak256(abi.encodePacked("test")); assertEq(mockStorage.solverOpHashes(testHash), false, "solverOpHashes[testHash] not false"); mockStorage.setSolverOpHash(testHash); @@ -104,7 +118,15 @@ contract StorageTest is BaseTest { } function test_storage_view_cumulativeSurcharge() public { - MockStorage mockStorage = new MockStorage(DEFAULT_ESCROW_DURATION, address(0), address(0), address(0), address(0)); + MockStorage mockStorage = new MockStorage( + DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, + address(0), + address(0), + address(0), + address(0) + ); assertEq(mockStorage.cumulativeSurcharge(), 0, "cumulativeSurcharge not 0"); mockStorage.setCumulativeSurcharge(100); assertEq(mockStorage.cumulativeSurcharge(), 100, "cumulativeSurcharge not 100"); @@ -157,7 +179,15 @@ contract StorageTest is BaseTest { function test_storage_transient_solverLockData() public { // MockStorage just used here to access AtlasConstants - MockStorage mockStorage = new MockStorage(DEFAULT_ESCROW_DURATION, address(0), address(0), address(0), address(0)); + MockStorage mockStorage = new MockStorage( + DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, + address(0), + address(0), + address(0), + address(0) + ); (address currentSolver, bool calledBack, bool fulfilled) = atlas.solverLockData(); assertEq(currentSolver, address(0), "currentSolver should start at 0"); @@ -180,7 +210,8 @@ contract StorageTest is BaseTest { assertEq(calledBack, true, "calledBack should still be true"); assertEq(fulfilled, true, "fulfilled should be true"); - testSolverLock = mockStorage.SOLVER_CALLED_BACK_MASK() | mockStorage.SOLVER_FULFILLED_MASK() | uint256(uint160(userEOA)); + testSolverLock = + mockStorage.SOLVER_CALLED_BACK_MASK() | mockStorage.SOLVER_FULFILLED_MASK() | uint256(uint160(userEOA)); atlas.setSolverLock(testSolverLock); (currentSolver, calledBack, fulfilled) = atlas.solverLockData(); @@ -247,7 +278,15 @@ contract StorageTest is BaseTest { } function test_storage_transient_solverTo() public { - MockStorage mockStorage = new MockStorage(DEFAULT_ESCROW_DURATION, address(0), address(0), address(0), address(0)); + MockStorage mockStorage = new MockStorage( + DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, + address(0), + address(0), + address(0), + address(0) + ); assertEq(mockStorage.solverTo(), address(0), "solverTo should start at 0"); mockStorage.setSolverTo(userEOA); @@ -258,7 +297,15 @@ contract StorageTest is BaseTest { } function test_storage_transient_activeEnvironment() public { - MockStorage mockStorage = new MockStorage(DEFAULT_ESCROW_DURATION, address(0), address(0), address(0), address(0)); + MockStorage mockStorage = new MockStorage( + DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, + address(0), + address(0), + address(0), + address(0) + ); assertEq(mockStorage.activeEnvironment(), address(0), "activeEnvironment should start at 0"); mockStorage.setLock(address(1), 0, 0); @@ -269,7 +316,15 @@ contract StorageTest is BaseTest { } function test_storage_transient_activeCallConfig() public { - MockStorage mockStorage = new MockStorage(DEFAULT_ESCROW_DURATION, address(0), address(0), address(0), address(0)); + MockStorage mockStorage = new MockStorage( + DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, + address(0), + address(0), + address(0), + address(0) + ); assertEq(mockStorage.activeCallConfig(), 0, "activeCallConfig should start at 0"); mockStorage.setLock(address(0), 1, 0); @@ -280,7 +335,15 @@ contract StorageTest is BaseTest { } function test_storage_transient_phase() public { - MockStorage mockStorage = new MockStorage(DEFAULT_ESCROW_DURATION, address(0), address(0), address(0), address(0)); + MockStorage mockStorage = new MockStorage( + DEFAULT_ESCROW_DURATION, + DEFAULT_ATLAS_SURCHARGE_RATE, + DEFAULT_BUNDLER_SURCHARGE_RATE, + address(0), + address(0), + address(0), + address(0) + ); assertEq(mockStorage.phase(), 0, "phase should start at 0"); mockStorage.setLock(address(0), 0, 1); @@ -293,19 +356,28 @@ contract StorageTest is BaseTest { // To test solverOpHashes() and cumulativeSurcharge() view function contract MockStorage is Storage { - // For solverLockData test uint256 public constant SOLVER_CALLED_BACK_MASK = _SOLVER_CALLED_BACK_MASK; uint256 public constant SOLVER_FULFILLED_MASK = _SOLVER_FULFILLED_MASK; constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, address l2GasCalculator ) - Storage(escrowDuration, verification, simulator, initialSurchargeRecipient, l2GasCalculator) + Storage( + escrowDuration, + atlasSurchargeRate, + bundlerSurchargeRate, + verification, + simulator, + initialSurchargeRecipient, + l2GasCalculator + ) { } function setSolverOpHash(bytes32 opHash) public { @@ -376,4 +448,4 @@ contract MockStorage is Storage { function getDeposits() external view returns (uint256) { return deposits(); } -} \ No newline at end of file +} diff --git a/test/base/BaseTest.t.sol b/test/base/BaseTest.t.sol index a4347929..f4b40272 100644 --- a/test/base/BaseTest.t.sol +++ b/test/base/BaseTest.t.sol @@ -51,6 +51,8 @@ contract BaseTest is Test { IERC20 DAI = IERC20(DAI_ADDRESS); uint256 DEFAULT_ESCROW_DURATION = 64; + uint256 DEFAULT_ATLAS_SURCHARGE_RATE = 1_000_000; // 10% + uint256 DEFAULT_BUNDLER_SURCHARGE_RATE = 1_000_000; // 10% uint256 MAINNET_FORK_BLOCK = 17_441_786; function setUp() public virtual { @@ -80,6 +82,8 @@ contract BaseTest is Test { atlas = new TestAtlas({ escrowDuration: DEFAULT_ESCROW_DURATION, + atlasSurchargeRate: DEFAULT_ATLAS_SURCHARGE_RATE, + bundlerSurchargeRate: DEFAULT_BUNDLER_SURCHARGE_RATE, verification: expectedAtlasVerificationAddr, simulator: address(simulator), executionTemplate: address(execEnvTemplate), diff --git a/test/base/TestAtlas.sol b/test/base/TestAtlas.sol index 4e325dc4..27c3da92 100644 --- a/test/base/TestAtlas.sol +++ b/test/base/TestAtlas.sol @@ -9,13 +9,24 @@ import "src/contracts/atlas/Atlas.sol"; contract TestAtlas is Atlas { constructor( uint256 escrowDuration, + uint256 atlasSurchargeRate, + uint256 bundlerSurchargeRate, address verification, address simulator, address initialSurchargeRecipient, address l2GasCalculator, address executionTemplate ) - Atlas(escrowDuration, verification, simulator, initialSurchargeRecipient, l2GasCalculator, executionTemplate) + Atlas( + escrowDuration, + atlasSurchargeRate, + bundlerSurchargeRate, + verification, + simulator, + initialSurchargeRecipient, + l2GasCalculator, + executionTemplate + ) { } // Public functions to expose internal transient helpers for testing