Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto create EE in metacall if needed #61

Closed
wants to merge 8 commits into from
Closed
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 lib/openzeppelin-contracts
11 changes: 4 additions & 7 deletions src/contracts/atlas/Atlas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,10 @@ contract Atlas is Escrow {
{
uint256 gasMarker = gasleft();

// TODO: Combine this w/ call to get executionEnvironment
DAppConfig memory dConfig = IDAppControl(userOp.control).getDAppConfig(userOp);

// Get the execution environment
address executionEnvironment = IAtlasFactory(FACTORY).getExecutionEnvironmentCustom(
userOp.from, dAppOp.control.codehash, userOp.control, dConfig.callConfig
);
// Get or create the execution environment
address executionEnvironment;
DAppConfig memory dConfig;
(executionEnvironment, dConfig) = IAtlasFactory(FACTORY).getOrCreateExecutionEnvironment(userOp);

// Gracefully return if not valid. This allows signature data to be stored, which helps prevent
// replay attacks.
Expand Down
25 changes: 20 additions & 5 deletions src/contracts/atlas/AtlasFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { IDAppControl } from "../interfaces/IDAppControl.sol";
import { Mimic } from "./Mimic.sol";
import { DAppConfig } from "src/contracts/types/DAppApprovalTypes.sol";
import { ExecutionEnvironment } from "./ExecutionEnvironment.sol";
import { UserOperation } from "../types/UserCallTypes.sol";

// TODO make sure no cases of address(this) when Atlas address is intended

Expand All @@ -29,15 +30,30 @@ contract AtlasFactory {
// ------------------ //

function createExecutionEnvironment(
address account,
address user,
address dAppControl
)
external
returns (address executionEnvironment)
{
// Must call createExecutionEnvironment on Atlas contract to properly initialize nonce tracking
require(msg.sender == atlas, "AtlasFactory: Only Atlas can create execution environments");
executionEnvironment = _setExecutionEnvironment(dAppControl, account, dAppControl.codehash);
uint32 callConfig = IDAppControl(dAppControl).callConfig();
executionEnvironment = _setExecutionEnvironment(dAppControl, user, callConfig, dAppControl.codehash);
}

function getOrCreateExecutionEnvironment(UserOperation calldata userOp)
external
returns (address executionEnvironment, DAppConfig memory dConfig)
{
// Must call getOrCreateExecutionEnvironment on Atlas contract to properly initialize nonce tracking
require(msg.sender == atlas, "AtlasFactory: Only Atlas can create execution environments");

address control = userOp.control;

dConfig = IDAppControl(control).getDAppConfig(userOp);

executionEnvironment = _setExecutionEnvironment(control, userOp.from, dConfig.callConfig, control.codehash);
}

function getExecutionEnvironment(
Expand Down Expand Up @@ -91,13 +107,12 @@ contract AtlasFactory {
function _setExecutionEnvironment(
address dAppControl,
address user,
uint32 callConfig,
bytes32 controlCodeHash
)
internal
returns (address executionEnvironment)
{
uint32 callConfig = IDAppControl(dAppControl).callConfig();

bytes memory creationCode = _getMimicCreationCode(dAppControl, callConfig, user, controlCodeHash);

executionEnvironment = address(
Expand All @@ -116,7 +131,7 @@ contract AtlasFactory {
executionEnvironment := create2(0, add(creationCode, 32), mload(creationCode), memSalt)
}

emit NewExecutionEnvironment(executionEnvironment, user, dAppControl, callConfig);
// emit NewExecutionEnvironment(executionEnvironment, user, dAppControl, callConfig);
}
}

Expand Down
11 changes: 8 additions & 3 deletions src/contracts/atlas/AtlasVerification.sol
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,14 @@ contract AtlasVerification is EIP712, DAppIntegration {
return false;
}

if (nonce == 0 && !isSimulation) {
// Allow 0 nonce for simulations to avoid unnecessary init txs
return false;
if (!isSimulation) {
if (nonce == 0) {
// Allow 0 nonce for simulations to avoid unnecessary init txs
return false;
} else if (nonce == 1) {
// Check if nonce needs to be initialized - do so if necessary.
_initializeNonce(account);
}
}

uint256 bitmapIndex = (nonce / 240) + 1; // +1 because highestFullBitmap initializes at 0
Expand Down
11 changes: 8 additions & 3 deletions src/contracts/atlas/DAppIntegration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ contract DAppIntegration {

signatories[signatoryKey] = true;

initializeNonce(msg.sender);
_initializeNonce(msg.sender);
}

function addSignatory(address controller, address signatory) external {
Expand All @@ -84,7 +84,7 @@ contract DAppIntegration {

signatories[signatoryKey] = true;

initializeNonce(signatory);
_initializeNonce(signatory);

emit NewDAppSignatory(controller, govData.governance, signatory, govData.callConfig);
}
Expand Down Expand Up @@ -125,7 +125,11 @@ contract DAppIntegration {
delete dapps[key];
}

function initializeNonce(address account) public {
function initializeNonce(address account) external {
_initializeNonce(account);
}

function _initializeNonce(address account) internal returns (bool initialized) {
if (asyncNonceBitIndex[account].LowestEmptyBitmap == uint128(0)) {
unchecked {
asyncNonceBitIndex[account].LowestEmptyBitmap = 2;
Expand All @@ -134,6 +138,7 @@ contract DAppIntegration {

// to skip the 0 nonce
asyncNonceBitmap[bitmapKey] = NonceBitmap({ highestUsedNonce: uint8(1), bitmap: 0 });
initialized = true;
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/contracts/interfaces/IAtlasFactory.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { DAppConfig } from "src/contracts/types/DAppApprovalTypes.sol";
import { UserOperation } from "../types/UserCallTypes.sol";

interface IAtlasFactory {
function createExecutionEnvironment(
address account,
Expand All @@ -9,6 +12,10 @@ interface IAtlasFactory {
external
returns (address executionEnvironment);

function getOrCreateExecutionEnvironment(UserOperation calldata userOp)
external
returns (address executionEnvironment, DAppConfig memory dConfig);

function getExecutionEnvironment(
address user,
address dAppControl
Expand Down
32 changes: 32 additions & 0 deletions test/MainTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,38 @@ contract MainTest is BaseTest {
);
}

function testExecutionEnvironmentAutoCreation() public {
uint8 v;
bytes32 r;
bytes32 s;

UserOperation memory userOp = helper.buildUserOperation(POOL_ONE, POOL_TWO, userEOA, TOKEN_ONE);
// User does not sign their own operation when bundling

SolverOperation[] memory solverOps = new SolverOperation[](1);
bytes memory solverOpData = helper.buildV2SolverOperationData(POOL_TWO, POOL_ONE);
solverOps[0] = helper.buildSolverOperation(userOp, solverOpData, solverOneEOA, address(solverOne), 2e17);
(v, r, s) = vm.sign(solverOnePK, atlasVerification.getSolverPayload(solverOps[0]));
solverOps[0].signature = abi.encodePacked(r, s, v);

DAppOperation memory dAppOp = helper.buildDAppOperation(governanceEOA, userOp, solverOps);
(v, r, s) = vm.sign(governancePK, atlasVerification.getDAppOperationPayload(dAppOp));
dAppOp.signature = abi.encodePacked(r, s, v);

// Execution environment should not exist yet
(,, bool exists) = atlasFactory.getExecutionEnvironment(userEOA, address(control));
assertFalse(exists, "ExecutionEnvironment already exists");

vm.startPrank(userEOA);
ERC20(TOKEN_ONE).approve(address(atlas), type(uint256).max);
atlas.metacall(userOp, solverOps, dAppOp);
vm.stopPrank();

// Execution environment should exist now
(,, exists) = atlasFactory.getExecutionEnvironment(userEOA, address(control));
assertTrue(exists, "ExecutionEnvironment wasn't created");
}

function testTestUserOperation() public {
uint8 v;
bytes32 r;
Expand Down
Loading