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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[profile.default]
bytecode_hash = "none"
optimizer_runs = 20
optimizer_runs = 18
timeout = 30000
block_gas_limit = 300000000
gas_limit = 3000000000
Expand Down
4 changes: 4 additions & 0 deletions script/base/deploy-base.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ contract DeployBaseScript is Script {
return "UNICHAIN";
} else if (chainId == 1301) {
return "UNICHAIN SEPOLIA";
} else if (chainId == 98_866) {
return "PLUME";
} else if (chainId == 98_867) {
return "PLUME TESTNET";
} else {
revert(string.concat("Error: Chain ID not recognized: ", vm.toString(chainId)));
}
Expand Down
7 changes: 4 additions & 3 deletions src/contracts/atlas/Atlas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { CallBits } from "../libraries/CallBits.sol";
import { SafetyBits } from "../libraries/SafetyBits.sol";
import { GasAccLib, GasLedger } from "../libraries/GasAccLib.sol";

/// @title Atlas V1.6.2
/// @title Atlas V1.6.3
/// @author FastLane Labs
/// @notice The Execution Abstraction protocol.
contract Atlas is Escrow, Factory {
Expand Down Expand Up @@ -65,8 +65,7 @@ contract Atlas is Escrow, Factory {
// is deducted from this _gasMarker, resulting in actual execution gas used + calldata gas costs + buffer.
// The calldata component is added below, only if exPostBids = false.
uint256 _gasLeft = gasleft();
uint256 _gasMarker = _gasLeft + _BASE_TX_GAS_USED + _POST_SETTLE_METACALL_GAS; // This part is only execution
// gas.
uint256 _gasMarker = _gasLeft + _BASE_TX_GAS_USED + _POST_SETTLE_METACALL_GAS; // This is only execution gas.

DAppConfig memory _dConfig;
bool _isSimulation = msg.sender == SIMULATOR;
Expand Down Expand Up @@ -106,6 +105,8 @@ contract Atlas is Escrow, Factory {

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

Expand Down
2 changes: 1 addition & 1 deletion src/contracts/atlas/AtlasVerification.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract AtlasVerification is EIP712, NonceManager, DAppIntegration {
address atlas,
address l2GasCalculator
)
EIP712("AtlasVerification", "1.6.2")
EIP712("AtlasVerification", "1.6.3")
DAppIntegration(atlas, l2GasCalculator)
{ }

Expand Down
4 changes: 3 additions & 1 deletion src/contracts/libraries/SafeBlockNumber.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ library SafeBlockNumber {
uint256 internal constant ARBITRUM_ONE_CHAIN_ID = 42_161;
uint256 internal constant ARBITRUM_NOVA_CHAIN_ID = 42_170;
uint256 internal constant ARBITRUM_SEPOLIA_CHAIN_ID = 421_614;
uint256 internal constant PLUME_CHAIN_ID = 98_866;
uint256 internal constant PLUME_TESTNET_CHAIN_ID = 98_867;

function get() internal view returns (uint256) {
uint256 chainId = block.chainid;
if (
chainId == ARBITRUM_ONE_CHAIN_ID || chainId == ARBITRUM_NOVA_CHAIN_ID
|| chainId == ARBITRUM_SEPOLIA_CHAIN_ID
|| chainId == ARBITRUM_SEPOLIA_CHAIN_ID || chainId == PLUME_CHAIN_ID || chainId == PLUME_TESTNET_CHAIN_ID
) {
// Arbitrum One or Nova chain
return ARB_SYS.arbBlockNumber();
Expand Down
147 changes: 147 additions & 0 deletions test/Atlas.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import "forge-std/Test.sol";

import { DAppControl } from "../src/contracts/dapp/DAppControl.sol";
import { UserOperation } from "../src/contracts/types/UserOperation.sol";
import { SolverOperation } from "../src/contracts/types/SolverOperation.sol";
import { DAppOperation } from "../src/contracts/types/DAppOperation.sol";
import { CallConfig } from "../src/contracts/types/ConfigTypes.sol";
import { CallVerification } from "../src/contracts/libraries/CallVerification.sol";

import { BaseTest } from "./base/BaseTest.t.sol";

// Tests for logic in the Atlas.sol contract
contract AtlasTest is BaseTest {
Sig sig;
TestDAppControl dappControl;

function setUp() public override {
// Run the base setup
super.setUp();

vm.startPrank(governanceEOA);
dappControl = new TestDAppControl(address(atlas));
atlasVerification.initializeGovernance(address(dappControl));
vm.stopPrank();
}

function test_Atlas_BundlerRefundedIfMetacallFails() public {
UserOperation memory userOp = _buildUserOp();
DAppOperation memory dAppOp = _buildDAppOp(userOp);

// Changing UserOp after DAppOp is signed will invalidate the metacall
userOp.data = new bytes(12345);

deal(userEOA, 1e18);
uint256 bundlerBalanceBefore = address(userEOA).balance;

// User is bundler, sends 1 ETH value with metacall
vm.startPrank(userEOA);
bool success = atlas.metacall{value: 1e18}(
userOp,
new SolverOperation[](0),
dAppOp,
userEOA
);

// Metacall should fail, return false instead of reverting, and refund the bundler
assertEq(success, false, "Metacall should fail");
assertEq(
address(userEOA).balance,
bundlerBalanceBefore,
"Bundler should be refunded, no balance change"
);
}

// ---------------------------------------------------- //
// Helpers //
// ---------------------------------------------------- //

function _buildUserOp() internal view returns (UserOperation memory) {
return UserOperation({
from: userEOA,
to: address(atlas),
value: 0,
gas: 1_000_000,
maxFeePerGas: tx.gasprice,
nonce: 1,
deadline: block.timestamp + 2,
dapp: address(dappControl),
control: address(dappControl),
callConfig: dappControl.CALL_CONFIG(),
dappGasLimit: dappControl.getDAppGasLimit(),
solverGasLimit: dappControl.getSolverGasLimit(),
bundlerSurchargeRate: dappControl.getBundlerSurchargeRate(),
sessionKey: address(0),
data: "",
signature: new bytes(0)
});
}

function _buildDAppOp(UserOperation memory userOp) internal returns (DAppOperation memory dAppOp) {
dAppOp = DAppOperation({
from: governanceEOA,
to: address(atlas),
nonce: 1,
deadline: block.timestamp + 2,
control: address(dappControl),
bundler: address(0),
userOpHash: atlasVerification.getUserOperationHash(userOp),
callChainHash: CallVerification.getCallChainHash(userOp, new SolverOperation[](0)),
signature: new bytes(0)
});
(sig.v, sig.r, sig.s) = vm.sign(governancePK, atlasVerification.getDAppOperationPayload(dAppOp));
dAppOp.signature = abi.encodePacked(sig.r, sig.s, sig.v);
}
}


contract TestDAppControl is DAppControl {
constructor(address atlas) DAppControl(
atlas,
msg.sender,
CallConfig({
userNoncesSequential: false,
dappNoncesSequential: false,
requirePreOps: false,
trackPreOpsReturnData: false,
trackUserReturnData: false,
delegateUser: false,
requirePreSolver: false,
requirePostSolver: false,
zeroSolvers: true,
reuseUserOp: false, // makes metacall return false instead of revert
userAuctioneer: true,
solverAuctioneer: false,
unknownAuctioneer: false,
verifyCallChainHash: true,
forwardReturnData: false,
requireFulfillment: false,
trustedOpHash: false,
invertBidValue: false,
exPostBids: false,
multipleSuccessfulSolvers: false,
checkMetacallGasLimit: false
})) {}

function _allocateValueCall(
bool solved,
address bidToken,
uint256 bidAmount,
bytes calldata data
) internal virtual override {}

function getBidFormat(
UserOperation calldata
) public view virtual override returns (address bidToken) {
return address(0);
}

function getBidValue(
SolverOperation calldata solverOp
) public view virtual override returns (uint256) {
return solverOp.bidAmount;
}
}
2 changes: 1 addition & 1 deletion test/AtlasVerification.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1901,7 +1901,7 @@ contract AtlasVerificationValidCallsTest is AtlasVerificationBase {

function testGetDomainSeparatorInAtlasVerification() public view {
bytes32 hashedName = keccak256(bytes("AtlasVerification"));
bytes32 hashedVersion = keccak256(bytes("1.6.2"));
bytes32 hashedVersion = keccak256(bytes("1.6.3"));
bytes32 typeHash = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
Expand Down