Skip to content

Commit

Permalink
feat: Use solady ERC1967 proxy for MA
Browse files Browse the repository at this point in the history
  • Loading branch information
adamegyed committed Sep 24, 2024
1 parent 75a63b9 commit 90b5482
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
188327
177463
2 changes: 1 addition & 1 deletion .forge-snapshots/ModularAccount_Runtime_Erc20Transfer.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
84200
84150
Original file line number Diff line number Diff line change
@@ -1 +1 @@
60112
60062
2 changes: 1 addition & 1 deletion .forge-snapshots/ModularAccount_UserOp_Erc20Transfer.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
193983
193921
2 changes: 1 addition & 1 deletion .forge-snapshots/ModularAccount_UserOp_NativeTransfer.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
169723
169673
23 changes: 11 additions & 12 deletions gas/modular-account/ModularAccount.gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,18 @@ contract ModularAccountGasTest is ModularAccountBenchmarkBase("ModularAccount")
// Also assert that the event emitted by the factory is correct
Vm.Log[] memory logs = vm.getRecordedLogs();

assertEq(logs.length, 5);
assertEq(logs.length, 4);
// Logs:
// 0: ERC1967Proxy `Upgraded`
// 1: SingleSignerValidationModule `SignerTransferred` (anonymous)
// 2: ModularAccount `ValidationInstalled`
// 3: ModularAccount `Initialized`
// 4: AccountFactory `ModularAccountDeployed`

assertEq(logs[4].topics.length, 3);
assertEq(logs[4].topics[0], AccountFactory.ModularAccountDeployed.selector);
assertEq(logs[4].topics[1], bytes32(uint256(uint160(accountAddress))));
assertEq(logs[4].topics[2], bytes32(uint256(uint160(owner1))));
assertEq(keccak256(logs[4].data), keccak256(abi.encodePacked(salt)));
// 0: SingleSignerValidationModule `SignerTransferred` (anonymous)
// 1: ModularAccount `ValidationInstalled`
// 2: ModularAccount `Initialized`
// 3: AccountFactory `ModularAccountDeployed`

assertEq(logs[3].topics.length, 3);
assertEq(logs[3].topics[0], AccountFactory.ModularAccountDeployed.selector);
assertEq(logs[3].topics[1], bytes32(uint256(uint160(accountAddress))));
assertEq(logs[3].topics[2], bytes32(uint256(uint160(owner1))));
assertEq(keccak256(logs[3].data), keccak256(abi.encodePacked(salt)));

_snap(RUNTIME, "AccountCreation", gasUsed);
}
Expand Down
28 changes: 13 additions & 15 deletions src/factory/AccountFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ pragma solidity ^0.8.26;
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";

import {ModularAccount} from "../account/ModularAccount.sol";
import {SemiModularAccount} from "../account/SemiModularAccount.sol";
Expand All @@ -16,7 +14,6 @@ import {LibClone} from "solady/utils/LibClone.sol";
contract AccountFactory is Ownable {
ModularAccount public immutable ACCOUNT_IMPL;
SemiModularAccount public immutable SEMI_MODULAR_ACCOUNT_IMPL;
bytes32 private immutable _PROXY_BYTECODE_HASH;
IEntryPoint public immutable ENTRY_POINT;
address public immutable SINGLE_SIGNER_VALIDATION_MODULE;

Expand All @@ -31,8 +28,6 @@ contract AccountFactory is Ownable {
address owner
) Ownable(owner) {
ENTRY_POINT = _entryPoint;
_PROXY_BYTECODE_HASH =
keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(address(_accountImpl), "")));
ACCOUNT_IMPL = _accountImpl;
SEMI_MODULAR_ACCOUNT_IMPL = _semiModularImpl;
SINGLE_SIGNER_VALIDATION_MODULE = _singleSignerValidationModule;
Expand All @@ -47,24 +42,25 @@ contract AccountFactory is Ownable {
*/
function createAccount(address owner, uint256 salt, uint32 entityId) external returns (ModularAccount) {
bytes32 combinedSalt = getSalt(owner, salt, entityId);
address addr = Create2.computeAddress(combinedSalt, _PROXY_BYTECODE_HASH);

// LibClone short-circuits if it's already deployed.
(bool alreadyDeployed, address instance) =
LibClone.createDeterministicERC1967(address(ACCOUNT_IMPL), combinedSalt);

// short circuit if exists
if (addr.code.length == 0) {
bytes memory pluginInstallData = abi.encode(entityId, owner);
// not necessary to check return addr since next call will fail if so
new ERC1967Proxy{salt: combinedSalt}(address(ACCOUNT_IMPL), "");
if (!alreadyDeployed) {
bytes memory moduleInstallData = abi.encode(entityId, owner);
// point proxy to actual implementation and init plugins
ModularAccount(payable(addr)).initializeWithValidation(
ModularAccount(payable(instance)).initializeWithValidation(
ValidationConfigLib.pack(SINGLE_SIGNER_VALIDATION_MODULE, entityId, true, true, true),
new bytes4[](0),
pluginInstallData,
moduleInstallData,
new bytes[](0)
);
emit ModularAccountDeployed(addr, owner, salt);
emit ModularAccountDeployed(instance, owner, salt);
}

return ModularAccount(payable(addr));
return ModularAccount(payable(instance));
}

function createSemiModularAccount(address owner, uint256 salt) external returns (SemiModularAccount) {
Expand Down Expand Up @@ -100,7 +96,9 @@ contract AccountFactory is Ownable {
* Calculate the counterfactual address of this account as it would be returned by createAccount()
*/
function getAddress(address owner, uint256 salt, uint32 entityId) external view returns (address) {
return Create2.computeAddress(getSalt(owner, salt, entityId), _PROXY_BYTECODE_HASH);
return LibClone.predictDeterministicAddressERC1967(
address(ACCOUNT_IMPL), getSalt(owner, salt, entityId), address(this)
);
}

function getAddressSemiModular(address owner, uint256 salt) public view returns (address) {
Expand Down

0 comments on commit 90b5482

Please sign in to comment.