-
Notifications
You must be signed in to change notification settings - Fork 25
/
AccountFactory.sol
126 lines (103 loc) · 5.35 KB
/
AccountFactory.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;
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 {ReferenceModularAccount} from "../account/ReferenceModularAccount.sol";
import {SemiModularAccount} from "../account/SemiModularAccount.sol";
import {ValidationConfigLib} from "../libraries/ValidationConfigLib.sol";
import {LibClone} from "solady/utils/LibClone.sol";
contract AccountFactory is Ownable {
ReferenceModularAccount 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;
event ModularAccountDeployed(address indexed account, address indexed owner, uint256 salt);
event SemiModularAccountDeployed(address indexed account, address indexed owner, uint256 salt);
constructor(
IEntryPoint _entryPoint,
ReferenceModularAccount _accountImpl,
SemiModularAccount _semiModularImpl,
address _singleSignerValidationModule,
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;
}
/**
* Create an account, and return its address.
* Returns the address even if the account is already deployed.
* Note that during user operation execution, this method is called only if the account is not deployed.
* This method returns an existing account address so that entryPoint.getSenderAddress() would work even after
* account creation
*/
function createAccount(address owner, uint256 salt, uint32 entityId)
external
returns (ReferenceModularAccount)
{
bytes32 combinedSalt = getSalt(owner, salt, entityId);
address addr = Create2.computeAddress(combinedSalt, _PROXY_BYTECODE_HASH);
// 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), "");
// point proxy to actual implementation and init plugins
ReferenceModularAccount(payable(addr)).initializeWithValidation(
ValidationConfigLib.pack(SINGLE_SIGNER_VALIDATION_MODULE, entityId, true, true, true),
new bytes4[](0),
pluginInstallData,
new bytes[](0)
);
emit ModularAccountDeployed(addr, owner, salt);
}
return ReferenceModularAccount(payable(addr));
}
function createSemiModularAccount(address owner, uint256 salt) external returns (SemiModularAccount) {
// both module address and entityId for fallback validations are hardcoded at the maximum value.
bytes32 fullSalt = getSalt(owner, salt, type(uint32).max);
bytes memory immutables = _getImmutableArgs(owner);
// LibClone short-circuits if it's already deployed.
(bool alreadyDeployed, address instance) =
LibClone.createDeterministicERC1967(address(SEMI_MODULAR_ACCOUNT_IMPL), immutables, fullSalt);
if (!alreadyDeployed) {
emit SemiModularAccountDeployed(instance, owner, salt);
}
return SemiModularAccount(payable(instance));
}
function addStake(uint32 unstakeDelay) external payable onlyOwner {
ENTRY_POINT.addStake{value: msg.value}(unstakeDelay);
}
function unlockStake() external onlyOwner {
ENTRY_POINT.unlockStake();
}
function withdrawStake(address payable withdrawAddress) external onlyOwner {
ENTRY_POINT.withdrawStake(withdrawAddress);
}
/**
* 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);
}
function getAddressSemiModular(address owner, uint256 salt) public view returns (address) {
bytes32 fullSalt = getSalt(owner, salt, type(uint32).max);
bytes memory immutables = _getImmutableArgs(owner);
return _getAddressSemiModular(immutables, fullSalt);
}
function getSalt(address owner, uint256 salt, uint32 entityId) public pure returns (bytes32) {
return keccak256(abi.encodePacked(owner, salt, entityId));
}
function _getAddressSemiModular(bytes memory immutables, bytes32 salt) internal view returns (address) {
return LibClone.predictDeterministicAddressERC1967(address(ACCOUNT_IMPL), immutables, salt, address(this));
}
function _getImmutableArgs(address owner) private pure returns (bytes memory) {
return abi.encodePacked(owner);
}
}