Skip to content

Commit

Permalink
feat: deploy and address prediction scripts (#321)
Browse files Browse the repository at this point in the history
Co-authored-by: Zer0dot <[email protected]>
  • Loading branch information
adamegyed and Zer0dot authored Dec 12, 2024
1 parent baa8650 commit d930ae7
Show file tree
Hide file tree
Showing 12 changed files with 1,028 additions and 100 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"fmt:check": "forge fmt --check && FOUNDRY_PROFILE=gas forge fmt --check",
"gas": "FOUNDRY_PROFILE=gas forge test -vv",
"gas:check": "FOUNDRY_PROFILE=gas FORGE_SNAPSHOT_CHECK=true forge test -vv",
"initcodehashes": "FOUNDRY_PROFILE=optimized-build forge script GetInitcodeHashScript",
"lcov": "forge coverage --no-match-coverage '(test)' --nmt '(testFuzz|invariant)' --report lcov",
"lint": "pnpm lint:src && pnpm lint:test && pnpm lint:gas && pnpm lint:script",
"lint:src": "solhint --max-warnings 0 -c ./config/solhint-src.json './src/**/*.sol'",
Expand Down
57 changes: 34 additions & 23 deletions script/Artifacts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,33 @@ abstract contract Artifacts {
address singleSignerValidationModule,
address webAuthnValidationModule,
address owner
) internal returns (AccountFactory) {
return new AccountFactory{salt: salt}(
entryPoint, accountImpl, semiModularImpl, singleSignerValidationModule, webAuthnValidationModule, owner
) internal returns (address) {
return address(
new AccountFactory{salt: salt}(
entryPoint,
accountImpl,
semiModularImpl,
singleSignerValidationModule,
webAuthnValidationModule,
owner
)
);
}

function _getAllowlistModuleInitcode() internal pure returns (bytes memory) {
return type(AllowlistModule).creationCode;
}

function _deployAllowlistModule(bytes32 salt) internal returns (AllowlistModule) {
return new AllowlistModule{salt: salt}();
function _deployAllowlistModule(bytes32 salt) internal returns (address) {
return address(new AllowlistModule{salt: salt}());
}

function _getExecutionInstallDelegateInitcode() internal pure returns (bytes memory) {
return type(ExecutionInstallDelegate).creationCode;
}

function _deployExecutionInstallDelegate(bytes32 salt) internal returns (ExecutionInstallDelegate) {
return new ExecutionInstallDelegate{salt: salt}();
function _deployExecutionInstallDelegate(bytes32 salt) internal returns (address) {
return address(new ExecutionInstallDelegate{salt: salt}());
}

function _getModularAccountInitcode(IEntryPoint entryPoint, ExecutionInstallDelegate executionInstallDelegate)
Expand All @@ -93,24 +100,24 @@ abstract contract Artifacts {
bytes32 salt,
IEntryPoint entryPoint,
ExecutionInstallDelegate executionInstallDelegate
) internal returns (ModularAccount) {
return new ModularAccount{salt: salt}(entryPoint, executionInstallDelegate);
) internal returns (address) {
return address(new ModularAccount{salt: salt}(entryPoint, executionInstallDelegate));
}

function _getNativeTokenLimitModuleInitcode() internal pure returns (bytes memory) {
return type(NativeTokenLimitModule).creationCode;
}

function _deployNativeTokenLimitModule(bytes32 salt) internal returns (NativeTokenLimitModule) {
return new NativeTokenLimitModule{salt: salt}();
function _deployNativeTokenLimitModule(bytes32 salt) internal returns (address) {
return address(new NativeTokenLimitModule{salt: salt}());
}

function _getPaymasterGuardModuleInitcode() internal pure returns (bytes memory) {
return type(PaymasterGuardModule).creationCode;
}

function _deployPaymasterGuardModule(bytes32 salt) internal returns (PaymasterGuardModule) {
return new PaymasterGuardModule{salt: salt}();
function _deployPaymasterGuardModule(bytes32 salt) internal returns (address) {
return address(new PaymasterGuardModule{salt: salt}());
}

function _getSemiModularAccount7702Initcode(
Expand All @@ -126,8 +133,8 @@ abstract contract Artifacts {
bytes32 salt,
IEntryPoint entryPoint,
ExecutionInstallDelegate executionInstallDelegate
) internal returns (SemiModularAccount7702) {
return new SemiModularAccount7702{salt: salt}(entryPoint, executionInstallDelegate);
) internal returns (address) {
return address(new SemiModularAccount7702{salt: salt}(entryPoint, executionInstallDelegate));
}

function _getSemiModularAccountBytecodeInitcode(
Expand All @@ -143,8 +150,8 @@ abstract contract Artifacts {
bytes32 salt,
IEntryPoint entryPoint,
ExecutionInstallDelegate executionInstallDelegate
) internal returns (SemiModularAccountBytecode) {
return new SemiModularAccountBytecode{salt: salt}(entryPoint, executionInstallDelegate);
) internal returns (address) {
return address(new SemiModularAccountBytecode{salt: salt}(entryPoint, executionInstallDelegate));
}

function _getSemiModularAccountStorageOnlyInitcode(
Expand All @@ -160,27 +167,31 @@ abstract contract Artifacts {
bytes32 salt,
IEntryPoint entryPoint,
ExecutionInstallDelegate executionInstallDelegate
) internal returns (SemiModularAccountStorageOnly) {
return new SemiModularAccountStorageOnly{salt: salt}(entryPoint, executionInstallDelegate);
) internal returns (address) {
return address(new SemiModularAccountStorageOnly{salt: salt}(entryPoint, executionInstallDelegate));
}

function _getSingleSignerValidationModuleInitcode() internal pure returns (bytes memory) {
return type(SingleSignerValidationModule).creationCode;
}

function _deploySingleSignerValidationModule(bytes32 salt) internal returns (SingleSignerValidationModule) {
return new SingleSignerValidationModule{salt: salt}();
function _deploySingleSignerValidationModule(bytes32 salt) internal returns (address) {
return address(new SingleSignerValidationModule{salt: salt}());
}

function _getTimeRangeModuleInitcode() internal pure returns (bytes memory) {
return type(TimeRangeModule).creationCode;
}

function _deployTimeRangeModule(bytes32 salt) internal returns (TimeRangeModule) {
return new TimeRangeModule{salt: salt}();
function _deployTimeRangeModule(bytes32 salt) internal returns (address) {
return address(new TimeRangeModule{salt: salt}());
}

function _getWebAuthnValidationModuleInitcode() internal pure returns (bytes memory) {
return type(WebAuthnValidationModule).creationCode;
}

function _deployWebAuthnValidationModule(bytes32 salt) internal returns (address) {
return address(new WebAuthnValidationModule{salt: salt}());
}
}
115 changes: 115 additions & 0 deletions script/DeployAccounts.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

import {console} from "forge-std/console.sol";

import {ExecutionInstallDelegate} from "../src/helpers/ExecutionInstallDelegate.sol";
import {Artifacts} from "./Artifacts.sol";
import {ScriptBase} from "./ScriptBase.sol";
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";

// Deploys the three account implementations and an execution install delegate. This requires the following env
// vars to be set:
// - ENTRY_POINT (optional)
contract DeployAccountsScript is ScriptBase, Artifacts {
// State vars for expected addresses and salts.

IEntryPoint public entryPoint;

address public expectedExecutionInstallDelegate;
uint256 public executionInstallDelegateSalt;

address public expectedModularAccountImpl;
uint256 public modularAccountImplSalt;

address public expectedSemiModularAccountBytecodeImpl;
uint256 public semiModularAccountBytecodeImplSalt;

address public expectedSemiModularAccountStorageOnlyImpl;
uint256 public semiModularAccountStorageOnlyImplSalt;

function setUp() public {
// Load the required addresses for the deployment from env vars.
entryPoint = _getEntryPoint();

expectedExecutionInstallDelegate = _getExecutionInstallDelegate();
executionInstallDelegateSalt = _getSaltOrZero("EXECUTION_INSTALL_DELEGATE");

expectedModularAccountImpl = _getModularAccountImpl();
modularAccountImplSalt = _getSaltOrZero("MODULAR_ACCOUNT_IMPL");

expectedSemiModularAccountBytecodeImpl = _getSemiModularAccountBytecodeImpl();
semiModularAccountBytecodeImplSalt = _getSaltOrZero("SEMI_MODULAR_ACCOUNT_BYTECODE_IMPL");

expectedSemiModularAccountStorageOnlyImpl = address(_getSemiModularAccountStorageOnlyImpl());
semiModularAccountStorageOnlyImplSalt = _getSaltOrZero("SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL");
}

function run() public onlyProfile("optimized-build") {
console.log("******** Deploying Account Implementations and Execution Install Delegate *********");

vm.startBroadcast();

_safeDeploy(
"Execution Install Delegate",
expectedExecutionInstallDelegate,
executionInstallDelegateSalt,
_getExecutionInstallDelegateInitcode(),
_deployExecutionInstallDelegate
);

// At this point, the delegate and entrypoint are valid, so we can safely proceed with
// using them as parameters and accessing them in wrapped functions.

_safeDeploy(
"Modular Account Impl",
expectedModularAccountImpl,
modularAccountImplSalt,
_getModularAccountInitcode(entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate)),
_wrappedDeployModularAccount
);

_safeDeploy(
"Semi Modular Account Bytecode Impl",
expectedSemiModularAccountBytecodeImpl,
semiModularAccountBytecodeImplSalt,
_getSemiModularAccountBytecodeInitcode(
entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate)
),
_wrappedDeploySemiModularAccountBytecode
);

_safeDeploy(
"Semi Modular Account Storage Only Impl",
expectedSemiModularAccountStorageOnlyImpl,
semiModularAccountStorageOnlyImplSalt,
_getSemiModularAccountStorageOnlyInitcode(
entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate)
),
_wrappedDeploySemiModularAccountStorageOnly
);

vm.stopBroadcast();

console.log("******** Account Implementations and Execution Install Delegate Deployed *********");
}

// These functions wrap the internal deployment functions to provide access to the needed state variables
// without affecting the expected signature from _safeDeploy.

function _wrappedDeployModularAccount(bytes32 salt) internal returns (address) {
return _deployModularAccount(salt, entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate));
}

function _wrappedDeploySemiModularAccountBytecode(bytes32 salt) internal returns (address) {
return _deploySemiModularAccountBytecode(
salt, entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate)
);
}

function _wrappedDeploySemiModularAccountStorageOnly(bytes32 salt) internal returns (address) {
return _deploySemiModularAccountStorageOnly(
salt, entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate)
);
}
}
134 changes: 134 additions & 0 deletions script/DeployFactory.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

import {console} from "forge-std/console.sol";

import {ModularAccount} from "../src/account/ModularAccount.sol";
import {SemiModularAccountBytecode} from "../src/account/SemiModularAccountBytecode.sol";
import {Artifacts} from "./Artifacts.sol";
import {ScriptBase} from "./ScriptBase.sol";
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";

// Deploys the Account Factory. This requires the following env vars to be set:
// - ENTRY_POINT
// - MODULAR_ACCOUNT_IMPL
// - SEMI_MODULAR_ACCOUNT_BYTECODE_IMPL
// - SINGLE_SIGNER_VALIDATION_MODULE
// - WEBAUTHN_VALIDATION_MODULE
// - FACTORY_OWNER
contract DeployFactoryScript is ScriptBase, Artifacts {
// State vars for expected addresses and salts.

address public expectedFactoryAddr;
uint256 public factorySalt;

// State vars for factory dependencies

IEntryPoint public entryPoint;
ModularAccount public modularAccountImpl;
SemiModularAccountBytecode public semiModularAccountBytecodeImpl;
address public singleSignerValidationModule;
address public webAuthnValidationModule;
address public factoryOwner;

function setUp() public {
// Load the required addresses for the factory deployment from env vars.
entryPoint = _getEntryPoint();
modularAccountImpl = ModularAccount(payable(_getModularAccountImpl()));
semiModularAccountBytecodeImpl = SemiModularAccountBytecode(payable(_getSemiModularAccountBytecodeImpl()));
singleSignerValidationModule = _getSingleSignerValidationModule();
webAuthnValidationModule = _getWebAuthnValidationModule();
factoryOwner = _getFactoryOwner();

// Load the expected address and salt from env vars.
expectedFactoryAddr = vm.envOr("ACCOUNT_FACTORY", address(0));
factorySalt = _getSaltOrZero("ACCOUNT_FACTORY");
}

function run() public onlyProfile("optimized-build") {
console.log("******** Deploying Factory *********");

vm.startBroadcast();

_safeDeploy(
"Account Factory",
expectedFactoryAddr,
factorySalt,
_getAccountFactoryInitcode(
entryPoint,
modularAccountImpl,
semiModularAccountBytecodeImpl,
singleSignerValidationModule,
webAuthnValidationModule,
factoryOwner
),
_wrappedDeployAccountFactory
);

vm.stopBroadcast();

console.log("******** Factory Deployed *********");
}

// Wrapper function to be called within _safeDeploy using the context in this contract.
function _wrappedDeployAccountFactory(bytes32 salt) internal returns (address) {
_ensureNonzeroFactoryArgs();
return _deployAccountFactory(
salt,
entryPoint,
modularAccountImpl,
semiModularAccountBytecodeImpl,
singleSignerValidationModule,
webAuthnValidationModule,
factoryOwner
);
}

function _ensureNonzeroFactoryArgs() internal view {
bool shouldRevert;
if (address(modularAccountImpl) == address(0)) {
console.log("Env Variable 'MODULAR_ACCOUNT_IMPL' not found or invalid during factory deployment");
shouldRevert = true;
} else {
console.log("Using user-defined ModularAccount at: %x", address(modularAccountImpl));
}

if (address(semiModularAccountBytecodeImpl) == address(0)) {
console.log(
"Env Variable 'SEMI_MODULAR_ACCOUNT_BYTECODE_IMPL' not found or invalid during factory deployment"
);
shouldRevert = true;
} else {
console.log(
"Using user-defined SemiModularAccountBytecode at: %x", address(semiModularAccountBytecodeImpl)
);
}

if (singleSignerValidationModule == address(0)) {
console.log(
"Env Variable 'SINGLE_SIGNER_VALIDATION_MODULE' not found or invalid during factory deployment"
);
shouldRevert = true;
} else {
console.log("Using user-defined SingleSignerValidationModule at: %x", singleSignerValidationModule);
}

if (webAuthnValidationModule == address(0)) {
console.log("Env Variable 'WEBAUTHN_VALIDATION_MODULE' not found or invalid during factory deployment");
shouldRevert = true;
} else {
console.log("Using user-defined WebAuthnValidationModule at: %x", webAuthnValidationModule);
}

if (factoryOwner == address(0)) {
console.log("Env Variable 'ACCOUNT_FACTORY_OWNER' not found or invalid during factory deployment");
shouldRevert = true;
} else {
console.log("Using user-defined factory owner at: %x", factoryOwner);
}

if (shouldRevert) {
revert("Missing or invalid env variables during factory deployment");
}
}
}
Loading

0 comments on commit d930ae7

Please sign in to comment.