From 534417566211e9da644d01326602bcc56afb8fdd Mon Sep 17 00:00:00 2001 From: Adam Egyed <5456061+adamegyed@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:30:52 -0800 Subject: [PATCH] feat: specialize deploy scripts per artifact (#324) --- .env.example | 8 +- .github/workflows/report.yml | 4 +- .gitignore | 4 + foundry.toml | 20 +++- gas-snapshots/LightAccount.json | 4 +- gas-snapshots/ModularAccount.json | 28 ++--- gas-snapshots/SemiModularAccount.json | 28 ++--- package.json | 4 +- script/DeployAccounts.s.sol | 76 ++++++------ script/DeployFactory.s.sol | 2 +- script/DeploySmaStorage.s.sol | 84 +++++++++++++ ...yModules.s.sol => DeployStandalones.s.sol} | 31 ++++- script/GetInitcodeHash.s.sol | 110 ++++++++++++------ script/ScriptBase.sol | 4 + test/script/DeployAccounts.s.t.sol | 34 ++---- test/script/DeployFactory.s.t.sol | 4 +- test/script/DeploySmaStorage.s.t.sol | 60 ++++++++++ ...ules.s.t.sol => DeployStandalones.s.t.sol} | 34 ++++-- 18 files changed, 385 insertions(+), 154 deletions(-) create mode 100644 script/DeploySmaStorage.s.sol rename script/{DeployModules.s.sol => DeployStandalones.s.sol} (80%) create mode 100644 test/script/DeploySmaStorage.s.t.sol rename test/script/{DeployModules.s.t.sol => DeployStandalones.s.t.sol} (73%) diff --git a/.env.example b/.env.example index 0ebd3d0d..b25c38b7 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ ENTRYPOINT= # AccountFactory's initial owner -FACTORY_OWNER= +ACCOUNT_FACTORY_OWNER= # Expected addresses @@ -17,7 +17,7 @@ WEBAUTHN_VALIDATION_MODULE= MODULAR_ACCOUNT_IMPL= SEMI_MODULAR_ACCOUNT_7702_IMPL= SEMI_MODULAR_ACCOUNT_BYTECODE_IMPL= -SEMI_MODULAR_ACCOUNT_STORAGEONLY_IMPL= +SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL= ACCOUNT_FACTORY= @@ -40,7 +40,7 @@ ACCOUNT_FACTORY_SALT= # Factory staking setup -# 1 ether required by bundlers -REQUIRED_STAKE_AMOUNT=1 +# 0.1 ether required by bundlers +REQUIRED_STAKE_AMOUNT_WEI=100000000000000000 # 1 day required by bundlers UNSTAKE_DELAY_SEC=86400 diff --git a/.github/workflows/report.yml b/.github/workflows/report.yml index 8ebbb6bb..094d5ffc 100644 --- a/.github/workflows/report.yml +++ b/.github/workflows/report.yml @@ -36,7 +36,7 @@ jobs: run: | { echo 'NEWSIZES<> "$GITHUB_OUTPUT" @@ -60,7 +60,7 @@ jobs: run: | { echo 'OLDSIZES<> "$GITHUB_OUTPUT" diff --git a/.gitignore b/.gitignore index 45fdbf3f..b8a3e872 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ # Foundry build and cache directories out/ out-optimized/ +out-optimized-standalone/ +out-optimized-sma-storage/ cache/ cache-optimized/ +cache-optimized-standalone/ +cache-optimized-sma-storage/ node_modules/ # Coverage diff --git a/foundry.toml b/foundry.toml index f2e60ca4..706d88c3 100644 --- a/foundry.toml +++ b/foundry.toml @@ -28,10 +28,26 @@ depth = 10 deny_warnings = true via_ir = true test = 'src' -optimizer_runs = 15000 +optimizer_runs = 50000 out = 'out-optimized' cache_path = 'cache-optimized' +[profile.optimized-build-standalone] +deny_warnings = true +via_ir = true +test = 'src' +optimizer_runs = 10_000_000_000 +out = 'out-optimized-standalone' +cache_path = 'cache-optimized-standalone' + +[profile.optimized-build-sma-storage] +deny_warnings = true +via_ir = true +test = 'src' +optimizer_runs = 15000 +out = 'out-optimized-sma-storage' +cache_path = 'cache-optimized-sma-storage' + [profile.optimized-test] deny_warnings = true src = 'test' @@ -58,7 +74,7 @@ depth = 32 via_ir = true deny_warnings = true test = 'gas' -optimizer_runs = 15000 +optimizer_runs = 50000 out = 'out-optimized' cache_path = 'cache-optimized' snapshots = 'gas-snapshots' diff --git a/gas-snapshots/LightAccount.json b/gas-snapshots/LightAccount.json index 23e60d5a..baf92729 100644 --- a/gas-snapshots/LightAccount.json +++ b/gas-snapshots/LightAccount.json @@ -1,7 +1,7 @@ { "Runtime_AccountCreation": "143836", - "Runtime_Erc20Transfer": "63183", + "Runtime_Erc20Transfer": "63180", "Runtime_NativeTransfer": "39356", - "UserOp_Erc20Transfer": "172435", + "UserOp_Erc20Transfer": "172432", "UserOp_NativeTransfer": "148492" } \ No newline at end of file diff --git a/gas-snapshots/ModularAccount.json b/gas-snapshots/ModularAccount.json index 66b15ed8..db3dc7c5 100644 --- a/gas-snapshots/ModularAccount.json +++ b/gas-snapshots/ModularAccount.json @@ -1,16 +1,16 @@ { - "Runtime_AccountCreation": "176256", - "Runtime_BatchTransfers": "92914", - "Runtime_Erc20Transfer": "78346", - "Runtime_InstallSessionKey_Case1": "429647", - "Runtime_NativeTransfer": "54495", - "Runtime_UseSessionKey_Case1_Counter": "78523", - "Runtime_UseSessionKey_Case1_Token": "111836", - "UserOp_BatchTransfers": "197734", - "UserOp_Erc20Transfer": "184708", - "UserOp_InstallSessionKey_Case1": "537327", - "UserOp_NativeTransfer": "160953", - "UserOp_UseSessionKey_Case1_Counter": "194454", - "UserOp_UseSessionKey_Case1_Token": "225438", - "UserOp_deferredValidation": "232882" + "Runtime_AccountCreation": "176235", + "Runtime_BatchTransfers": "92896", + "Runtime_Erc20Transfer": "78331", + "Runtime_InstallSessionKey_Case1": "429572", + "Runtime_NativeTransfer": "54483", + "Runtime_UseSessionKey_Case1_Counter": "78469", + "Runtime_UseSessionKey_Case1_Token": "111779", + "UserOp_BatchTransfers": "197713", + "UserOp_Erc20Transfer": "184690", + "UserOp_InstallSessionKey_Case1": "537249", + "UserOp_NativeTransfer": "160938", + "UserOp_UseSessionKey_Case1_Counter": "194376", + "UserOp_UseSessionKey_Case1_Token": "225357", + "UserOp_deferredValidation": "232843" } \ No newline at end of file diff --git a/gas-snapshots/SemiModularAccount.json b/gas-snapshots/SemiModularAccount.json index a6a55926..8c6f429e 100644 --- a/gas-snapshots/SemiModularAccount.json +++ b/gas-snapshots/SemiModularAccount.json @@ -1,16 +1,16 @@ { - "Runtime_AccountCreation": "97767", - "Runtime_BatchTransfers": "88746", - "Runtime_Erc20Transfer": "74226", - "Runtime_InstallSessionKey_Case1": "427850", - "Runtime_NativeTransfer": "50385", - "Runtime_UseSessionKey_Case1_Counter": "78826", - "Runtime_UseSessionKey_Case1_Token": "112139", - "UserOp_BatchTransfers": "192863", - "UserOp_Erc20Transfer": "179920", - "UserOp_InstallSessionKey_Case1": "534652", - "UserOp_NativeTransfer": "156195", - "UserOp_UseSessionKey_Case1_Counter": "194706", - "UserOp_UseSessionKey_Case1_Token": "225690", - "UserOp_deferredValidation": "228856" + "Runtime_AccountCreation": "97764", + "Runtime_BatchTransfers": "88740", + "Runtime_Erc20Transfer": "74220", + "Runtime_InstallSessionKey_Case1": "427790", + "Runtime_NativeTransfer": "50382", + "Runtime_UseSessionKey_Case1_Counter": "78772", + "Runtime_UseSessionKey_Case1_Token": "112082", + "UserOp_BatchTransfers": "192854", + "UserOp_Erc20Transfer": "179911", + "UserOp_InstallSessionKey_Case1": "534589", + "UserOp_NativeTransfer": "156189", + "UserOp_UseSessionKey_Case1_Counter": "194643", + "UserOp_UseSessionKey_Case1_Token": "225624", + "UserOp_deferredValidation": "228832" } \ No newline at end of file diff --git a/package.json b/package.json index 20f5e2a1..69b05f8b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "solhint": "^3.6.2" }, "scripts": { - "clean": "forge clean && FOUNDRY_PROFILE=optimized-build forge clean", + "clean": "forge clean && FOUNDRY_PROFILE=optimized-build forge clean && FOUNDRY_PROFILE=optimized-build-standalone forge clean", "coverage": "forge coverage --no-match-coverage '(test)' --nmt '(testFuzz|invariant)'", "fmt": "forge fmt && FOUNDRY_PROFILE=gas forge fmt", "fmt:check": "forge fmt --check && FOUNDRY_PROFILE=gas forge fmt --check", @@ -25,7 +25,7 @@ "lint:gas": "solhint --max-warnings 0 -c ./config/solhint-gas.json './gas/**/*.sol'", "lint:script": "solhint --max-warnings 0 -c ./config/solhint-script.json './script/**/*.sol'", "prep": "pnpm fmt && forge b --deny-warnings && pnpm lint && pnpm test && pnpm gas", - "sizes": "FOUNDRY_PROFILE=optimized-build forge b --sizes | grep '^|' | grep -v -e '| 17 |' -e 'Lib'", + "sizes": "FOUNDRY_PROFILE=optimized-build forge b --sizes | grep '^|' | grep -v -e '| 17 |' -e 'Lib'", "test": "forge test" } } diff --git a/script/DeployAccounts.s.sol b/script/DeployAccounts.s.sol index cd5cefba..fdd3baf1 100644 --- a/script/DeployAccounts.s.sol +++ b/script/DeployAccounts.s.sol @@ -16,8 +16,7 @@ contract DeployAccountsScript is ScriptBase, Artifacts { IEntryPoint public entryPoint; - address public expectedExecutionInstallDelegate; - uint256 public executionInstallDelegateSalt; + address public executionInstallDelegate; address public expectedModularAccountImpl; uint256 public modularAccountImplSalt; @@ -25,15 +24,14 @@ contract DeployAccountsScript is ScriptBase, Artifacts { address public expectedSemiModularAccountBytecodeImpl; uint256 public semiModularAccountBytecodeImplSalt; - address public expectedSemiModularAccountStorageOnlyImpl; - uint256 public semiModularAccountStorageOnlyImplSalt; + address public expectedSemiModularAccount7702Impl; + uint256 public semiModularAccount7702ImplSalt; function setUp() public { // Load the required addresses for the deployment from env vars. entryPoint = _getEntryPoint(); - expectedExecutionInstallDelegate = _getExecutionInstallDelegate(); - executionInstallDelegateSalt = _getSaltOrZero("EXECUTION_INSTALL_DELEGATE"); + executionInstallDelegate = _getExecutionInstallDelegate(); expectedModularAccountImpl = _getModularAccountImpl(); modularAccountImplSalt = _getSaltOrZero("MODULAR_ACCOUNT_IMPL"); @@ -41,22 +39,16 @@ contract DeployAccountsScript is ScriptBase, Artifacts { expectedSemiModularAccountBytecodeImpl = _getSemiModularAccountBytecodeImpl(); semiModularAccountBytecodeImplSalt = _getSaltOrZero("SEMI_MODULAR_ACCOUNT_BYTECODE_IMPL"); - expectedSemiModularAccountStorageOnlyImpl = address(_getSemiModularAccountStorageOnlyImpl()); - semiModularAccountStorageOnlyImplSalt = _getSaltOrZero("SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL"); + expectedSemiModularAccount7702Impl = _getSemiModularAccount7702Impl(); + semiModularAccount7702ImplSalt = _getSaltOrZero("SEMI_MODULAR_ACCOUNT_7702_IMPL"); } function run() public onlyProfile("optimized-build") { - console.log("******** Deploying Account Implementations and Execution Install Delegate *********"); + console.log("******** Deploying Account Implementations *********"); - vm.startBroadcast(); + _ensureNonzeroArgs(); - _safeDeploy( - "Execution Install Delegate", - expectedExecutionInstallDelegate, - executionInstallDelegateSalt, - _getExecutionInstallDelegateInitcode(), - _deployExecutionInstallDelegate - ); + vm.startBroadcast(); // 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. @@ -65,7 +57,7 @@ contract DeployAccountsScript is ScriptBase, Artifacts { "Modular Account Impl", expectedModularAccountImpl, modularAccountImplSalt, - _getModularAccountInitcode(entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate)), + _getModularAccountInitcode(entryPoint, ExecutionInstallDelegate(executionInstallDelegate)), _wrappedDeployModularAccount ); @@ -73,43 +65,53 @@ contract DeployAccountsScript is ScriptBase, Artifacts { "Semi Modular Account Bytecode Impl", expectedSemiModularAccountBytecodeImpl, semiModularAccountBytecodeImplSalt, - _getSemiModularAccountBytecodeInitcode( - entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate) - ), + _getSemiModularAccountBytecodeInitcode(entryPoint, ExecutionInstallDelegate(executionInstallDelegate)), _wrappedDeploySemiModularAccountBytecode ); _safeDeploy( - "Semi Modular Account Storage Only Impl", - expectedSemiModularAccountStorageOnlyImpl, - semiModularAccountStorageOnlyImplSalt, - _getSemiModularAccountStorageOnlyInitcode( - entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate) - ), - _wrappedDeploySemiModularAccountStorageOnly + "Semi Modular Account 7702 Impl", + expectedSemiModularAccount7702Impl, + semiModularAccount7702ImplSalt, + _getSemiModularAccount7702Initcode(entryPoint, ExecutionInstallDelegate(executionInstallDelegate)), + _wrappedDeploySemiModularAccount7702 ); vm.stopBroadcast(); - console.log("******** Account Implementations and Execution Install Delegate Deployed *********"); + console.log("******** Account Implementations 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)); + return _deployModularAccount(salt, entryPoint, ExecutionInstallDelegate(executionInstallDelegate)); } function _wrappedDeploySemiModularAccountBytecode(bytes32 salt) internal returns (address) { - return _deploySemiModularAccountBytecode( - salt, entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate) - ); + return + _deploySemiModularAccountBytecode(salt, entryPoint, ExecutionInstallDelegate(executionInstallDelegate)); } - function _wrappedDeploySemiModularAccountStorageOnly(bytes32 salt) internal returns (address) { - return _deploySemiModularAccountStorageOnly( - salt, entryPoint, ExecutionInstallDelegate(expectedExecutionInstallDelegate) - ); + function _wrappedDeploySemiModularAccount7702(bytes32 salt) internal returns (address) { + return _deploySemiModularAccount7702(salt, entryPoint, ExecutionInstallDelegate(executionInstallDelegate)); + } + + function _ensureNonzeroArgs() internal view { + bool shouldRevert; + + if (address(executionInstallDelegate) == address(0)) { + console.log( + "Env Variable 'EXECUTION_INSTALL_DELEGATE' not found or invalid during accounts deployment." + ); + shouldRevert = true; + } else { + console.log("Using user-defined ExecutionInstallDelegate at: %x", executionInstallDelegate); + } + + if (shouldRevert) { + revert("Missing or invalid env variables during factory deployment"); + } } } diff --git a/script/DeployFactory.s.sol b/script/DeployFactory.s.sol index 4d3ad511..7b26d411 100644 --- a/script/DeployFactory.s.sol +++ b/script/DeployFactory.s.sol @@ -45,7 +45,7 @@ contract DeployFactoryScript is ScriptBase, Artifacts { factorySalt = _getSaltOrZero("ACCOUNT_FACTORY"); } - function run() public onlyProfile("optimized-build") { + function run() public onlyProfile("optimized-build-standalone") { console.log("******** Deploying Factory *********"); vm.startBroadcast(); diff --git a/script/DeploySmaStorage.s.sol b/script/DeploySmaStorage.s.sol new file mode 100644 index 00000000..3af1d627 --- /dev/null +++ b/script/DeploySmaStorage.s.sol @@ -0,0 +1,84 @@ +// 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 DeploySmaStorageScript is ScriptBase, Artifacts { + // State vars for expected addresses and salts. + + IEntryPoint public entryPoint; + + address public executionInstallDelegate; + + address public expectedSemiModularAccountStorageOnlyImpl; + uint256 public semiModularAccountStorageOnlyImplSalt; + + function setUp() public { + // Load the required addresses for the deployment from env vars. + entryPoint = _getEntryPoint(); + + executionInstallDelegate = _getExecutionInstallDelegate(); + + expectedSemiModularAccountStorageOnlyImpl = _getSemiModularAccountStorageOnlyImpl(); + semiModularAccountStorageOnlyImplSalt = _getSaltOrZero("SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL"); + } + + function run() public onlyProfile("optimized-build-sma-storage") { + console.log("******** Deploying SMA-Storage Implementation *********"); + + _ensureNonzeroArgs(); + + vm.startBroadcast(); + + // 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( + "Semi Modular Account Storage Only Impl", + expectedSemiModularAccountStorageOnlyImpl, + semiModularAccountStorageOnlyImplSalt, + _getSemiModularAccountStorageOnlyInitcode( + entryPoint, ExecutionInstallDelegate(executionInstallDelegate) + ), + _wrappedDeploySemiModularAccountStorageOnly + ); + + vm.stopBroadcast(); + + console.log("******** SMA-Storage Implementation Deployed *********"); + } + + // These functions wrap the internal deployment functions to provide access to the needed state variables + // without affecting the expected signature from _safeDeploy. + + function _wrappedDeploySemiModularAccountStorageOnly(bytes32 salt) internal returns (address) { + return _deploySemiModularAccountStorageOnly( + salt, entryPoint, ExecutionInstallDelegate(executionInstallDelegate) + ); + } + + function _ensureNonzeroArgs() internal view { + bool shouldRevert; + + if (address(executionInstallDelegate) == address(0)) { + console.log( + "Env Variable 'EXECUTION_INSTALL_DELEGATE' not found or invalid during accounts deployment." + ); + shouldRevert = true; + } else { + console.log("Using user-defined ExecutionInstallDelegate at: %x", executionInstallDelegate); + } + + if (shouldRevert) { + revert("Missing or invalid env variables during factory deployment"); + } + } +} diff --git a/script/DeployModules.s.sol b/script/DeployStandalones.s.sol similarity index 80% rename from script/DeployModules.s.sol rename to script/DeployStandalones.s.sol index 101be129..5574918a 100644 --- a/script/DeployModules.s.sol +++ b/script/DeployStandalones.s.sol @@ -6,14 +6,17 @@ import {console} from "forge-std/console.sol"; import {Artifacts} from "./Artifacts.sol"; import {ScriptBase} from "./ScriptBase.sol"; -// Deploys all standalone modules. +// Deploys all standalone contracts. +// Modules: // - AllowlistModule // - NativeTokenLimitModule // - PaymasterGuardModule // - SingleSignerValidationModule // - TimeRangeModule // - WebAuthnValidationModule -contract DeployModulesScript is ScriptBase, Artifacts { +// Account-internal: +// - ExecutionInstallDelegate +contract DeployStandalonesScript is ScriptBase, Artifacts { // State vars for expected addresses and salts. address public expectedAllowlistModuleAddr; @@ -34,6 +37,9 @@ contract DeployModulesScript is ScriptBase, Artifacts { address public expectedWebAuthnValidationModuleAddr; uint256 public webAuthnValidationModuleSalt; + address public expectedExecutionInstallDelegate; + uint256 public executionInstallDelegateSalt; + function setUp() public { // Load the expected addresses and salts from env vars. @@ -54,9 +60,12 @@ contract DeployModulesScript is ScriptBase, Artifacts { expectedWebAuthnValidationModuleAddr = vm.envOr("WEBAUTHN_VALIDATION_MODULE", address(0)); webAuthnValidationModuleSalt = _getSaltOrZero("WEBAUTHN_VALIDATION_MODULE"); + + expectedExecutionInstallDelegate = _getExecutionInstallDelegate(); + executionInstallDelegateSalt = _getSaltOrZero("EXECUTION_INSTALL_DELEGATE"); } - function run() public onlyProfile("optimized-build") { + function run() public onlyProfile("optimized-build-standalone") { console.log("******** Deploying Modules *********"); vm.startBroadcast(); @@ -109,8 +118,20 @@ contract DeployModulesScript is ScriptBase, Artifacts { _deployWebAuthnValidationModule ); - vm.stopBroadcast(); - console.log("******** Modules Deployed *********"); + + console.log("******** Deploying Execution Install Delegate *********"); + + _safeDeploy( + "Execution Install Delegate", + expectedExecutionInstallDelegate, + executionInstallDelegateSalt, + _getExecutionInstallDelegateInitcode(), + _deployExecutionInstallDelegate + ); + + console.log("******** Execution Install Delegate Deployed *********"); + + vm.stopBroadcast(); } } diff --git a/script/GetInitcodeHash.s.sol b/script/GetInitcodeHash.s.sol index 99cd8550..408dfb7e 100644 --- a/script/GetInitcodeHash.s.sol +++ b/script/GetInitcodeHash.s.sol @@ -32,53 +32,89 @@ import {ScriptBase} from "./ScriptBase.sol"; // - AccountFactory contract GetInitcodeHashScript is ScriptBase, Artifacts { - function run() public view onlyProfile("optimized-build") { - console.log("******** Calculating Initcode Hashes *********"); - - console.log("Artifact initcode hashes with no dependencies:"); - console.log("- AllowlistModule: %x", uint256(keccak256(_getAllowlistModuleInitcode()))); - console.log("- ExecutionInstallDelegate: %x", uint256(keccak256(_getExecutionInstallDelegateInitcode()))); - console.log("- NativeTokenLimitModule: %x", uint256(keccak256(_getNativeTokenLimitModuleInitcode()))); - console.log("- PaymasterGuardModule: %x", uint256(keccak256(_getPaymasterGuardModuleInitcode()))); - console.log( - "- SingleSignerValidationModule: %x", uint256(keccak256(_getSingleSignerValidationModuleInitcode())) - ); - console.log("- TimeRangeModule: %x", uint256(keccak256(_getTimeRangeModuleInitcode()))); - console.log("- WebAuthnValidationModule: %x", uint256(keccak256(_getWebAuthnValidationModuleInitcode()))); - - console.log("Artifact initcode hashes with dependencies on EntryPoint and ExecutionInstallDelegate:"); - IEntryPoint entryPoint = _getEntryPoint(); - - ExecutionInstallDelegate executionInstallDelegate = - ExecutionInstallDelegate(_getExecutionInstallDelegate()); + function run() public view { + string memory actualProfile = vm.envOr(string("FOUNDRY_PROFILE"), string("")); + console.log(string.concat("Running script with the `", actualProfile, "` profile.")); - if (address(executionInstallDelegate) == address(0)) { - console.log( - "Env Variable 'EXECUTION_INSTALL_DELEGATE' not found or invalid, skipping reporting " - "initcode hashes for ModularAccount, SemiModularAccount7702, SemiModularAccountBytecode, " - "and SemiModularAccountStorageOnly" - ); - } else { - console.log("Using user-defined ExecutionInstallDelegate at: %x", address(executionInstallDelegate)); + console.log("******** Calculating Initcode Hashes *********"); + if (keccak256(bytes(actualProfile)) == keccak256(bytes("optimized-build-standalone"))) { + console.log("Artifact initcode hashes with no dependencies:"); + console.log("- AllowlistModule: %x", uint256(keccak256(_getAllowlistModuleInitcode()))); console.log( - "- ModularAccount: %x", - uint256(keccak256(_getModularAccountInitcode(entryPoint, executionInstallDelegate))) - ); - console.log( - "- SemiModularAccount7702: %x", - uint256(keccak256(_getSemiModularAccount7702Initcode(entryPoint, executionInstallDelegate))) + "- ExecutionInstallDelegate: %x", uint256(keccak256(_getExecutionInstallDelegateInitcode())) ); + console.log("- NativeTokenLimitModule: %x", uint256(keccak256(_getNativeTokenLimitModuleInitcode()))); + console.log("- PaymasterGuardModule: %x", uint256(keccak256(_getPaymasterGuardModuleInitcode()))); console.log( - "- SemiModularAccountBytecode: %x", - uint256(keccak256(_getSemiModularAccountBytecodeInitcode(entryPoint, executionInstallDelegate))) + "- SingleSignerValidationModule: %x", + uint256(keccak256(_getSingleSignerValidationModuleInitcode())) ); + console.log("- TimeRangeModule: %x", uint256(keccak256(_getTimeRangeModuleInitcode()))); console.log( - "- SemiModularAccountStorageOnly: %x", - uint256(keccak256(_getSemiModularAccountStorageOnlyInitcode(entryPoint, executionInstallDelegate))) + "- WebAuthnValidationModule: %x", uint256(keccak256(_getWebAuthnValidationModuleInitcode())) ); + + _logFactoryInitcodeHash(); } + if ( + keccak256(bytes(actualProfile)) == keccak256(bytes("optimized-build")) + || keccak256(bytes(actualProfile)) == keccak256(bytes("optimized-build-sma-storage")) + ) { + console.log("Artifact initcode hashes with dependencies on EntryPoint and ExecutionInstallDelegate:"); + IEntryPoint entryPoint = _getEntryPoint(); + + ExecutionInstallDelegate executionInstallDelegate = + ExecutionInstallDelegate(_getExecutionInstallDelegate()); + + if (address(executionInstallDelegate) == address(0)) { + console.log( + "Env Variable 'EXECUTION_INSTALL_DELEGATE' not found or invalid, skipping reporting " + "initcode hashes for ModularAccount, SemiModularAccount7702, SemiModularAccountBytecode, " + "and SemiModularAccountStorageOnly" + ); + } else { + console.log( + "Using user-defined ExecutionInstallDelegate at: %x", address(executionInstallDelegate) + ); + + if (keccak256(bytes(actualProfile)) == keccak256(bytes("optimized-build-sma-storage"))) { + console.log( + "- SemiModularAccountStorageOnly: %x", + uint256( + keccak256( + _getSemiModularAccountStorageOnlyInitcode(entryPoint, executionInstallDelegate) + ) + ) + ); + } + + if (keccak256(bytes(actualProfile)) == keccak256(bytes("optimized-build"))) { + console.log( + "- ModularAccount: %x", + uint256(keccak256(_getModularAccountInitcode(entryPoint, executionInstallDelegate))) + ); + console.log( + "- SemiModularAccount7702: %x", + uint256( + keccak256(_getSemiModularAccount7702Initcode(entryPoint, executionInstallDelegate)) + ) + ); + console.log( + "- SemiModularAccountBytecode: %x", + uint256( + keccak256(_getSemiModularAccountBytecodeInitcode(entryPoint, executionInstallDelegate)) + ) + ); + } + } + } + } + + function _logFactoryInitcodeHash() internal view { + IEntryPoint entryPoint = _getEntryPoint(); + console.log( "Artifact initcode hashes with dependency on EntryPoint, ModularAccount impl, " "SemiModularAccountBytecode impl, SingleSignerValidationModule, " diff --git a/script/ScriptBase.sol b/script/ScriptBase.sol index ca434389..a8a0d270 100644 --- a/script/ScriptBase.sol +++ b/script/ScriptBase.sol @@ -45,6 +45,10 @@ abstract contract ScriptBase is Script { return vm.envOr("SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL", address(0)); } + function _getSemiModularAccount7702Impl() internal view returns (address) { + return vm.envOr("SEMI_MODULAR_ACCOUNT_7702_IMPL", address(0)); + } + function _getExecutionInstallDelegate() internal view returns (address) { return vm.envOr("EXECUTION_INSTALL_DELEGATE", address(0)); } diff --git a/test/script/DeployAccounts.s.t.sol b/test/script/DeployAccounts.s.t.sol index e64b222a..07f088c8 100644 --- a/test/script/DeployAccounts.s.t.sol +++ b/test/script/DeployAccounts.s.t.sol @@ -3,14 +3,13 @@ pragma solidity ^0.8.26; import {Test} from "forge-std/Test.sol"; -import {ExecutionManifest} from "@erc6900/reference-implementation/interfaces/IExecutionModule.sol"; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {DeployAccountsScript} from "../../script/DeployAccounts.s.sol"; import {ModularAccount} from "../../src/account/ModularAccount.sol"; + +import {SemiModularAccount7702} from "../../src/account/SemiModularAccount7702.sol"; import {SemiModularAccountBytecode} from "../../src/account/SemiModularAccountBytecode.sol"; -import {SemiModularAccountStorageOnly} from "../../src/account/SemiModularAccountStorageOnly.sol"; -import {ExecutionInstallDelegate} from "../../src/helpers/ExecutionInstallDelegate.sol"; contract DeployAccountsTest is Test { DeployAccountsScript internal _deployAccountsScript; @@ -19,7 +18,7 @@ contract DeployAccountsTest is Test { address public executionInstallDelegate; address public modularAccountImpl; address public semiModularAccountBytecodeImpl; - address public semiModularAccountStorageOnlyImpl; + address public semiModularAccount7702Impl; function setUp() public { _deployAccountsScript = new DeployAccountsScript(); @@ -28,9 +27,7 @@ contract DeployAccountsTest is Test { entryPoint = makeAddr("Entrypoint"); - executionInstallDelegate = Create2.computeAddress( - zeroSalt, keccak256(type(ExecutionInstallDelegate).creationCode), CREATE2_FACTORY - ); + executionInstallDelegate = makeAddr("ExecutionInstallDelegate"); modularAccountImpl = Create2.computeAddress( zeroSalt, @@ -50,12 +47,11 @@ contract DeployAccountsTest is Test { CREATE2_FACTORY ); - semiModularAccountStorageOnlyImpl = Create2.computeAddress( + semiModularAccount7702Impl = Create2.computeAddress( zeroSalt, keccak256( bytes.concat( - type(SemiModularAccountStorageOnly).creationCode, - abi.encode(entryPoint, executionInstallDelegate) + type(SemiModularAccount7702).creationCode, abi.encode(entryPoint, executionInstallDelegate) ) ), CREATE2_FACTORY @@ -65,20 +61,19 @@ contract DeployAccountsTest is Test { vm.setEnv("EXECUTION_INSTALL_DELEGATE", vm.toString(executionInstallDelegate)); vm.setEnv("MODULAR_ACCOUNT_IMPL", vm.toString(modularAccountImpl)); vm.setEnv("SEMI_MODULAR_ACCOUNT_BYTECODE_IMPL", vm.toString(semiModularAccountBytecodeImpl)); - vm.setEnv("SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL", vm.toString(semiModularAccountStorageOnlyImpl)); + vm.setEnv("SEMI_MODULAR_ACCOUNT_7702_IMPL", vm.toString(semiModularAccount7702Impl)); string memory zeroSaltString = vm.toString(zeroSalt); - vm.setEnv("EXECUTION_INSTALL_DELEGATE_SALT", zeroSaltString); vm.setEnv("MODULAR_ACCOUNT_IMPL_SALT", zeroSaltString); vm.setEnv("SEMI_MODULAR_ACCOUNT_BYTECODE_IMPL_SALT", zeroSaltString); - vm.setEnv("SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL_SALT", zeroSaltString); + vm.setEnv("SEMI_MODULAR_ACCOUNT_7702_IMPL_SALT", zeroSaltString); // Spoof as though the profile is set to "optimized-build". vm.setEnv("FOUNDRY_PROFILE", "optimized-build"); } - function test_deployFactoryScript() public { + function test_deployAccountsScript() public { _deployAccountsScript.setUp(); _deployAccountsScript.run(); @@ -90,15 +85,6 @@ contract DeployAccountsTest is Test { "alchemy.sma-bytecode.1.0.0" ); - assertEq( - SemiModularAccountStorageOnly(payable(semiModularAccountStorageOnlyImpl)).accountId(), - "alchemy.sma-storage.1.0.0" - ); - - // Check that the delegate's in the right place by checking that `installExecution()` can only be called - // via delegatecall. - ExecutionManifest memory manifest; - vm.expectRevert(ExecutionInstallDelegate.OnlyDelegateCall.selector); - ExecutionInstallDelegate(executionInstallDelegate).installExecution(address(0), manifest, ""); + assertEq(SemiModularAccount7702(payable(semiModularAccount7702Impl)).accountId(), "alchemy.sma-7702.1.0.0"); } } diff --git a/test/script/DeployFactory.s.t.sol b/test/script/DeployFactory.s.t.sol index 694e857c..687cf614 100644 --- a/test/script/DeployFactory.s.t.sol +++ b/test/script/DeployFactory.s.t.sol @@ -65,8 +65,8 @@ contract DeployFactoryTest is Test { vm.setEnv("ACCOUNT_FACTORY_SALT", zeroSaltString); - // Spoof as though the profile is set to "optimized-build". - vm.setEnv("FOUNDRY_PROFILE", "optimized-build"); + // Spoof as though the profile is set to "optimized-build-standalone". + vm.setEnv("FOUNDRY_PROFILE", "optimized-build-standalone"); } function test_deployFactoryScript() public { diff --git a/test/script/DeploySmaStorage.s.t.sol b/test/script/DeploySmaStorage.s.t.sol new file mode 100644 index 00000000..9395ac72 --- /dev/null +++ b/test/script/DeploySmaStorage.s.t.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.26; + +import {Test} from "forge-std/Test.sol"; + +import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; + +import {DeploySmaStorageScript} from "../../script/DeploySmaStorage.s.sol"; +import {SemiModularAccountStorageOnly} from "../../src/account/SemiModularAccountStorageOnly.sol"; + +contract DeploySmaStorageTest is Test { + DeploySmaStorageScript internal _deploySmaStorageScript; + + address public entryPoint; + address public executionInstallDelegate; + address public semiModularAccountStorageOnlyImpl; + + function setUp() public { + _deploySmaStorageScript = new DeploySmaStorageScript(); + + bytes32 zeroSalt = bytes32(0); + + entryPoint = makeAddr("Entrypoint"); + + executionInstallDelegate = makeAddr("ExecutionInstallDelegate"); + + semiModularAccountStorageOnlyImpl = Create2.computeAddress( + zeroSalt, + keccak256( + bytes.concat( + type(SemiModularAccountStorageOnly).creationCode, + abi.encode(entryPoint, executionInstallDelegate) + ) + ), + CREATE2_FACTORY + ); + + vm.setEnv("ENTRYPOINT", vm.toString(entryPoint)); + vm.setEnv("EXECUTION_INSTALL_DELEGATE", vm.toString(executionInstallDelegate)); + vm.setEnv("SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL", vm.toString(semiModularAccountStorageOnlyImpl)); + + string memory zeroSaltString = vm.toString(zeroSalt); + + vm.setEnv("SEMI_MODULAR_ACCOUNT_STORAGE_ONLY_IMPL_SALT", zeroSaltString); + + // Spoof as though the profile is set to "optimized-build". + vm.setEnv("FOUNDRY_PROFILE", "optimized-build-sma-storage"); + } + + function test_deploySmaStorageScript() public { + _deploySmaStorageScript.setUp(); + + _deploySmaStorageScript.run(); + + assertEq( + SemiModularAccountStorageOnly(payable(semiModularAccountStorageOnlyImpl)).accountId(), + "alchemy.sma-storage.1.0.0" + ); + } +} diff --git a/test/script/DeployModules.s.t.sol b/test/script/DeployStandalones.s.t.sol similarity index 73% rename from test/script/DeployModules.s.t.sol rename to test/script/DeployStandalones.s.t.sol index 7aa49c37..b367250a 100644 --- a/test/script/DeployModules.s.t.sol +++ b/test/script/DeployStandalones.s.t.sol @@ -3,9 +3,12 @@ pragma solidity ^0.8.26; import {Test} from "forge-std/Test.sol"; +import {ExecutionManifest} from "@erc6900/reference-implementation/interfaces/IExecutionModule.sol"; import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; -import {DeployModulesScript} from "../../script/DeployModules.s.sol"; +import {DeployStandalonesScript} from "../../script/DeployStandalones.s.sol"; + +import {ExecutionInstallDelegate} from "../../src/helpers/ExecutionInstallDelegate.sol"; import {AllowlistModule} from "../../src/modules/permissions/AllowlistModule.sol"; import {NativeTokenLimitModule} from "../../src/modules/permissions/NativeTokenLimitModule.sol"; import {PaymasterGuardModule} from "../../src/modules/permissions/PaymasterGuardModule.sol"; @@ -13,8 +16,8 @@ import {TimeRangeModule} from "../../src/modules/permissions/TimeRangeModule.sol import {SingleSignerValidationModule} from "../../src/modules/validation/SingleSignerValidationModule.sol"; import {WebAuthnValidationModule} from "../../src/modules/validation/WebAuthnValidationModule.sol"; -contract DeployModulesTest is Test { - DeployModulesScript internal _deployModulesScript; +contract DeployStandalonesTest is Test { + DeployStandalonesScript internal _deployStandalonesScript; AllowlistModule internal _allowlistModule; NativeTokenLimitModule internal _nativeTokenLimitModule; @@ -22,9 +25,10 @@ contract DeployModulesTest is Test { SingleSignerValidationModule internal _singleSignerValidationModule; TimeRangeModule internal _timeRangeModule; WebAuthnValidationModule internal _webAuthnValidationModule; + ExecutionInstallDelegate internal _executionInstallDelegate; function setUp() public { - _deployModulesScript = new DeployModulesScript(); + _deployStandalonesScript = new DeployStandalonesScript(); bytes32 zeroSalt = bytes32(0); @@ -56,12 +60,19 @@ contract DeployModulesTest is Test { ) ); + _executionInstallDelegate = ExecutionInstallDelegate( + Create2.computeAddress( + zeroSalt, keccak256(type(ExecutionInstallDelegate).creationCode), CREATE2_FACTORY + ) + ); + vm.setEnv("ALLOWLIST_MODULE", vm.toString(address(_allowlistModule))); vm.setEnv("NATIVE_TOKEN_LIMIT_MODULE", vm.toString(address(_nativeTokenLimitModule))); vm.setEnv("PAYMASTER_GUARD_MODULE", vm.toString(address(_paymasterGuardModule))); vm.setEnv("SINGLE_SIGNER_VALIDATION_MODULE", vm.toString(address(_singleSignerValidationModule))); vm.setEnv("TIME_RANGE_MODULE", vm.toString(address(_timeRangeModule))); vm.setEnv("WEBAUTHN_VALIDATION_MODULE", vm.toString(address(_webAuthnValidationModule))); + vm.setEnv("EXECUTION_INSTALL_DELEGATE", vm.toString(address(_executionInstallDelegate))); string memory zeroSaltString = vm.toString(zeroSalt); @@ -71,15 +82,16 @@ contract DeployModulesTest is Test { vm.setEnv("SINGLE_SIGNER_VALIDATION_MODULE_SALT", zeroSaltString); vm.setEnv("TIME_RANGE_MODULE_SALT", zeroSaltString); vm.setEnv("WEBAUTHN_VALIDATION_MODULE_SALT", zeroSaltString); + vm.setEnv("EXECUTION_INSTALL_DELEGATE_SALT", zeroSaltString); // Spoof as though the profile is set to "optimized-build". - vm.setEnv("FOUNDRY_PROFILE", "optimized-build"); + vm.setEnv("FOUNDRY_PROFILE", "optimized-build-standalone"); } - function test_deployModulesScript() public { - _deployModulesScript.setUp(); + function test_deployStandalonesScript() public { + _deployStandalonesScript.setUp(); - _deployModulesScript.run(); + _deployStandalonesScript.run(); // Ensure that the right modules were deployed to the expected addresses. assertEq(_allowlistModule.moduleId(), "alchemy.allowlist-module.1.0.0"); @@ -88,5 +100,11 @@ contract DeployModulesTest is Test { assertEq(_singleSignerValidationModule.moduleId(), "alchemy.single-signer-validation-module.1.0.0"); assertEq(_timeRangeModule.moduleId(), "alchemy.time-range-module.1.0.0"); assertEq(_webAuthnValidationModule.moduleId(), "alchemy.webauthn-validation-module.1.0.0"); + + // Check that the delegate's in the right place by checking that `installExecution()` can only be called + // via delegatecall. + ExecutionManifest memory manifest; + vm.expectRevert(ExecutionInstallDelegate.OnlyDelegateCall.selector); + ExecutionInstallDelegate(_executionInstallDelegate).installExecution(address(0), manifest, ""); } }