Skip to content

Commit

Permalink
feat: split logic out to FactoryLib contract
Browse files Browse the repository at this point in the history
  • Loading branch information
BenSparksCode committed Oct 18, 2024
1 parent a4f3571 commit 5962eee
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 155 deletions.
20 changes: 16 additions & 4 deletions script/deploy-atlas.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "forge-std/Test.sol";

import { DeployBaseScript } from "./base/deploy-base.s.sol";

import { FactoryLib } from "../src/contracts/atlas/FactoryLib.sol";
import { Atlas } from "../src/contracts/atlas/Atlas.sol";
import { AtlasVerification } from "../src/contracts/atlas/AtlasVerification.sol";
import { TxBuilder } from "../src/contracts/helpers/TxBuilder.sol";
Expand All @@ -27,9 +28,9 @@ contract DeployAtlasScript is DeployBaseScript {
address deployer = vm.addr(deployerPrivateKey);

// Computes the addresses at which AtlasVerification will be deployed
address expectedAtlasAddr = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 1);
address expectedAtlasVerificationAddr = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 2);
address expectedSimulatorAddr = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 3);
address expectedAtlasAddr = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 2);
address expectedAtlasVerificationAddr = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 3);
address expectedSimulatorAddr = vm.computeCreateAddress(deployer, vm.getNonce(deployer) + 4);

address prevSimAddr = _getAddressFromDeploymentsJson("SIMULATOR");
uint256 prevSimBalance = (prevSimAddr == address(0)) ? 0 : prevSimAddr.balance;
Expand All @@ -40,13 +41,14 @@ contract DeployAtlasScript is DeployBaseScript {
vm.startBroadcast(deployerPrivateKey);

ExecutionEnvironment execEnvTemplate = new ExecutionEnvironment(expectedAtlasAddr);
FactoryLib factoryLib = new FactoryLib(address(execEnvTemplate));
atlas = new Atlas({
escrowDuration: ESCROW_DURATION,
atlasSurchargeRate: ATLAS_SURCHARGE_RATE,
bundlerSurchargeRate: BUNDLER_SURCHARGE_RATE,
verification: expectedAtlasVerificationAddr,
simulator: expectedSimulatorAddr,
executionTemplate: address(execEnvTemplate),
factoryLib: address(factoryLib),
initialSurchargeRecipient: deployer,
l2GasCalculator: address(0)
});
Expand Down Expand Up @@ -110,6 +112,16 @@ contract DeployAtlasScript is DeployBaseScript {
console.log("ERROR: Sorter deployment address is 0x0");
error = true;
}
// Check FactoryLib address set correctly in Atlas
if (address(factoryLib) != atlas.FACTORY_LIB()) {
console.log("ERROR: FactoryLib address not set correctly in Atlas");
error = true;
}
// Check ExecutionEnvironment address set correctly in FactoryLib
if (address(execEnvTemplate) != factoryLib.EXECUTION_ENV_TEMPLATE()) {
console.log("ERROR: ExecutionEnvironment address not set correctly in FactoryLib");
error = true;
}
// Check ESCROW_DURATION was not set to 0
if (atlas.ESCROW_DURATION() == 0) {
console.log("ERROR: ESCROW_DURATION was set to 0");
Expand Down
5 changes: 2 additions & 3 deletions src/contracts/atlas/Atlas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ contract Atlas is Escrow, Factory {
address simulator,
address initialSurchargeRecipient,
address l2GasCalculator,
address executionTemplate
address factoryLib
)
Escrow(
escrowDuration,
Expand All @@ -45,7 +45,7 @@ contract Atlas is Escrow, Factory {
initialSurchargeRecipient,
l2GasCalculator
)
Factory(executionTemplate)
Factory(factoryLib)
{ }

/// @notice metacall is the entrypoint function for the Atlas transactions.
Expand Down Expand Up @@ -371,7 +371,6 @@ contract Atlas is Escrow, Factory {
uint32 callConfig
)
internal
view
override
returns (bool)
{
Expand Down
112 changes: 19 additions & 93 deletions src/contracts/atlas/Factory.sol
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

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

import { IDAppControl } from "../interfaces/IDAppControl.sol";
import { Mimic } from "../common/Mimic.sol";
import { DAppConfig } from "../types/ConfigTypes.sol";
import { UserOperation } from "../types/UserOperation.sol";
import { AtlasEvents } from "../types/AtlasEvents.sol";
import { AtlasErrors } from "../types/AtlasErrors.sol";

/// @title Factory
/// @author FastLane Labs
/// @notice Provides functionality for creating and managing execution environments for DApps within the Atlas Protocol.
/// @dev This contract uses deterministic deployment to generate and manage Execution Environment instances based on
/// predefined templates.
abstract contract Factory {
address public immutable EXECUTION_ENV_TEMPLATE;
address public immutable FACTORY_LIB;
bytes32 internal immutable _FACTORY_BASE_SALT;

/// @notice Initializes a new Factory contract instance by setting the immutable salt for deterministic deployment
/// of Execution Environments and storing the execution template address.
/// @dev The Execution Environment Template must be separately deployed using the same calculated salt.
/// @param executionTemplate Address of the pre-deployed execution template contract for creating Execution
/// Environment instances.
constructor(address executionTemplate) {
EXECUTION_ENV_TEMPLATE = executionTemplate;
constructor(address factoryLib) {
FACTORY_LIB = factoryLib;
_FACTORY_BASE_SALT = keccak256(abi.encodePacked(block.chainid, address(this)));
}

Expand Down Expand Up @@ -57,7 +47,6 @@ abstract contract Factory {
address control
)
external
view
returns (address executionEnvironment, uint32 callConfig, bool exists)
{
callConfig = IDAppControl(control).CALL_CONFIG();
Expand Down Expand Up @@ -100,25 +89,13 @@ abstract contract Factory {
internal
returns (address executionEnvironment)
{
bytes memory _creationCode = _getMimicCreationCode({ user: user, control: control, callConfig: callConfig });
bytes32 _salt = _computeSalt(user, control, callConfig);

executionEnvironment = address(
uint160(
uint256(
keccak256(
abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(abi.encodePacked(_creationCode)))
)
)
)
bytes memory returnData = _delegatecallFactoryLib(
abi.encodeCall(FactoryLib.getOrCreateExecutionEnvironment, (user, control, callConfig, _salt))
);

if (executionEnvironment.code.length == 0) {
assembly {
executionEnvironment := create2(0, add(_creationCode, 32), mload(_creationCode), _salt)
}
emit AtlasEvents.ExecutionEnvironmentCreated(user, executionEnvironment);
}
return abi.decode(returnData, (address));
}

/// @notice Generates the address of a user's execution environment affected by deprecated callConfig changes in the
Expand All @@ -135,79 +112,28 @@ abstract contract Factory {
uint32 callConfig
)
internal
view
returns (address executionEnvironment)
{
bytes memory _creationCode = _getMimicCreationCode({ user: user, control: control, callConfig: callConfig });
bytes32 _salt = _computeSalt(user, control, callConfig);

executionEnvironment = address(
uint160(
uint256(
keccak256(
abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(abi.encodePacked(_creationCode)))
)
)
)
bytes memory returnData = _delegatecallFactoryLib(
abi.encodeCall(FactoryLib.getExecutionEnvironmentCustom, (user, control, callConfig, _salt))
);

return abi.decode(returnData, (address));
}

function _computeSalt(address user, address control, uint32 callConfig) internal view returns (bytes32) {
return keccak256(abi.encodePacked(_FACTORY_BASE_SALT, user, control, callConfig));
}

/// @notice Generates the creation code for the execution environment contract.
/// @param control The address of the DAppControl contract associated with the execution environment.
/// @param callConfig The configuration flags defining the behavior of the execution environment.
/// @param user The address of the user for whom the execution environment is being created, contributing to the
/// uniqueness of the creation code.
/// @return creationCode The bytecode representing the creation code of the execution environment contract.
function _getMimicCreationCode(
address user,
address control,
uint32 callConfig
)
internal
view
returns (bytes memory creationCode)
{
address _executionLib = EXECUTION_ENV_TEMPLATE;
// NOTE: Changing compiler settings or solidity versions can break this.
creationCode = type(Mimic).creationCode;

assembly {
// Insert the ExecutionEnvironment "Lib" address, into the AAAA placeholder in the creation code.
mstore(
add(creationCode, 79),
or(
and(mload(add(creationCode, 79)), not(shl(96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))),
shl(96, _executionLib)
)
)

// Insert the user address into the BBBB placeholder in the creation code.
mstore(
add(creationCode, 111),
or(
and(mload(add(creationCode, 111)), not(shl(96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))),
shl(96, user)
)
)

// Insert the control address into the CCCC placeholder in the creation code.
mstore(
add(creationCode, 132),
or(
and(mload(add(creationCode, 132)), not(shl(96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))),
shl(96, control)
)
)

// Insert the callConfig into the 2222 placeholder in the creation code.
mstore(
add(creationCode, 153),
or(and(mload(add(creationCode, 153)), not(shl(224, 0xFFFFFFFF))), shl(224, callConfig))
)
function _delegatecallFactoryLib(bytes memory data) internal returns (bytes memory) {
(bool _success, bytes memory _result) = FACTORY_LIB.delegatecall(data);
if (!_success) {
assembly {
revert(add(_result, 32), mload(_result))
}
}
return _result;
}
}
145 changes: 145 additions & 0 deletions src/contracts/atlas/FactoryLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import { Mimic } from "../common/Mimic.sol";
import { AtlasEvents } from "../types/AtlasEvents.sol";

// NOTE: Do not call these functions directly. This contract should only ever be delegatecalled by the Atlas contract.

contract FactoryLib {
address public immutable EXECUTION_ENV_TEMPLATE;

/// @notice Initializes a new Factory contract instance by setting the immutable salt for deterministic deployment
/// of Execution Environments and storing the execution template address.
/// @dev The Execution Environment Template must be separately deployed using the same calculated salt.
/// @param executionTemplate Address of the pre-deployed execution template contract for creating Execution
/// Environment instances.
constructor(address executionTemplate) {
EXECUTION_ENV_TEMPLATE = executionTemplate;
}

/// @notice Deploys a new execution environment or retrieves the address of an existing one based on the DApp
/// control, user, and configuration.
/// @dev Uses the `create2` opcode for deterministic deployment, allowing the calculation of the execution
/// environment's address before deployment. The deployment uses a combination of the DAppControl address, user
/// address, call configuration, and a unique salt to ensure the uniqueness and predictability of the environment's
/// address.
/// @param user The address of the user for whom the execution environment is being set.
/// @param control The address of the DAppControl contract providing the operational context.
/// @param callConfig CallConfig settings of the DAppControl contract.
/// @return executionEnvironment The address of the newly created or already existing execution environment.
function getOrCreateExecutionEnvironment(
address user,
address control,
uint32 callConfig,
bytes32 salt
)
public
returns (address executionEnvironment)
{
bytes memory _creationCode = _getMimicCreationCode({ user: user, control: control, callConfig: callConfig });

executionEnvironment = address(
uint160(
uint256(
keccak256(
abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(abi.encodePacked(_creationCode)))
)
)
)
);

if (executionEnvironment.code.length == 0) {
assembly {
executionEnvironment := create2(0, add(_creationCode, 32), mload(_creationCode), salt)
}
emit AtlasEvents.ExecutionEnvironmentCreated(user, executionEnvironment);
}
}

/// @notice Generates the address of a user's execution environment affected by deprecated callConfig changes in the
/// DAppControl.
/// @dev Calculates the deterministic address of the execution environment based on the user, control,
/// callConfig, and controlCodeHash, ensuring consistency across changes in callConfig.
/// @param user The address of the user for whom the execution environment's address is being generated.
/// @param control The address of the DAppControl contract associated with the execution environment.
/// @param callConfig The configuration flags defining the behavior of the execution environment.
/// @return executionEnvironment The address of the user's execution environment.
function getExecutionEnvironmentCustom(
address user,
address control,
uint32 callConfig,
bytes32 salt
)
public
view
returns (address executionEnvironment)
{
bytes memory _creationCode = _getMimicCreationCode({ user: user, control: control, callConfig: callConfig });

executionEnvironment = address(
uint160(
uint256(
keccak256(
abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(abi.encodePacked(_creationCode)))
)
)
)
);
}

/// @notice Generates the creation code for the execution environment contract.
/// @param control The address of the DAppControl contract associated with the execution environment.
/// @param callConfig The configuration flags defining the behavior of the execution environment.
/// @param user The address of the user for whom the execution environment is being created, contributing to the
/// uniqueness of the creation code.
/// @return creationCode The bytecode representing the creation code of the execution environment contract.
function _getMimicCreationCode(
address user,
address control,
uint32 callConfig
)
internal
view
returns (bytes memory creationCode)
{
address _executionLib = EXECUTION_ENV_TEMPLATE;
// NOTE: Changing compiler settings or solidity versions can break this.
creationCode = type(Mimic).creationCode;

assembly {
// Insert the ExecutionEnvironment "Lib" address, into the AAAA placeholder in the creation code.
mstore(
add(creationCode, 79),
or(
and(mload(add(creationCode, 79)), not(shl(96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))),
shl(96, _executionLib)
)
)

// Insert the user address into the BBBB placeholder in the creation code.
mstore(
add(creationCode, 111),
or(
and(mload(add(creationCode, 111)), not(shl(96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))),
shl(96, user)
)
)

// Insert the control address into the CCCC placeholder in the creation code.
mstore(
add(creationCode, 132),
or(
and(mload(add(creationCode, 132)), not(shl(96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))),
shl(96, control)
)
)

// Insert the callConfig into the 2222 placeholder in the creation code.
mstore(
add(creationCode, 153),
or(and(mload(add(creationCode, 153)), not(shl(224, 0xFFFFFFFF))), shl(224, callConfig))
)
}
}
}
Loading

0 comments on commit 5962eee

Please sign in to comment.