Skip to content

Commit 30adb10

Browse files
Suggested Smart Account Changes (#378)
* make createAccount virtual * add bytes to createAccount so that extra args can be added when overriding * _accountImplementation internal so inheriting contracts can access * add revert for best practices * fix failing test * prettier * disable initialize on base impl * create BaseAccountFactory for inheriting contracts * prettier * revert to using address type instead of BaseAccount * replace internal var + getter with a public var * cleanup * refactor createAccount --------- Co-authored-by: Krishang <[email protected]>
1 parent 7b850ee commit 30adb10

13 files changed

+196
-263
lines changed

contracts/smart-wallet/dynamic/DynamicAccount.sol

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ contract DynamicAccount is AccountCore, BaseRouter {
3434
receive() external payable override(Router, AccountCore) {}
3535

3636
constructor(IEntryPoint _entrypoint, address _defaultExtension) AccountCore(_entrypoint) {
37+
_disableInitializers();
3738
defaultExtension = _defaultExtension;
3839
}
3940

40-
function initialize(address _defaultAdmin) public override initializer {
41+
function initialize(address _defaultAdmin, bytes calldata) public virtual override initializer {
4142
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
4243
_setupRole(EXTENSION_ADMIN_ROLE, _defaultAdmin);
4344
}

contracts/smart-wallet/dynamic/DynamicAccountFactory.sol

+20-84
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
pragma solidity ^0.8.12;
33

44
// Utils
5-
import "../../extension/Multicall.sol";
5+
import "../utils/BaseAccountFactory.sol";
6+
import "../utils/BaseAccount.sol";
67
import "../../openzeppelin-presets/proxy/Clones.sol";
7-
import "../../openzeppelin-presets/utils/structs/EnumerableSet.sol";
8-
9-
// Interface
10-
import "./../interfaces/IAccountFactory.sol";
118

129
// Smart wallet implementation
1310
import "../utils/AccountExtension.sol";
@@ -22,92 +19,31 @@ import "./DynamicAccount.sol";
2219
// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ |
2320
// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/
2421

25-
contract DynamicAccountFactory is IAccountFactory, Multicall {
26-
using EnumerableSet for EnumerableSet.AddressSet;
27-
28-
/*///////////////////////////////////////////////////////////////
29-
State
30-
//////////////////////////////////////////////////////////////*/
31-
32-
DynamicAccount private immutable _accountImplementation;
33-
34-
mapping(address => EnumerableSet.AddressSet) private accountsOfSigner;
35-
mapping(address => EnumerableSet.AddressSet) private signersOfAccount;
36-
22+
contract DynamicAccountFactory is BaseAccountFactory {
3723
/*///////////////////////////////////////////////////////////////
3824
Constructor
3925
//////////////////////////////////////////////////////////////*/
4026

41-
constructor(IEntryPoint _entrypoint) {
42-
address defaultExtension = address(new AccountExtension(address(_entrypoint), address(this)));
43-
_accountImplementation = new DynamicAccount(_entrypoint, defaultExtension);
44-
}
45-
46-
/*///////////////////////////////////////////////////////////////
47-
External functions
48-
//////////////////////////////////////////////////////////////*/
49-
50-
/// @notice Deploys a new Account for admin.
51-
function createAccount(address _admin) external returns (address) {
52-
address impl = address(_accountImplementation);
53-
bytes32 salt = keccak256(abi.encode(_admin));
54-
address account = Clones.predictDeterministicAddress(impl, salt);
55-
56-
if (account.code.length > 0) {
57-
return account;
58-
}
59-
60-
account = Clones.cloneDeterministic(impl, salt);
61-
62-
Account(payable(account)).initialize(_admin);
63-
64-
emit AccountCreated(account, _admin);
65-
66-
return account;
67-
}
68-
69-
/// @notice Callback function for an Account to register its signers.
70-
function addSigner(address _signer) external {
71-
address account = msg.sender;
72-
73-
accountsOfSigner[_signer].add(account);
74-
signersOfAccount[account].add(_signer);
75-
76-
emit SignerAdded(account, _signer);
77-
}
78-
79-
/// @notice Callback function for an Account to un-register its signers.
80-
function removeSigner(address _signer) external {
81-
address account = msg.sender;
82-
83-
accountsOfSigner[_signer].remove(account);
84-
signersOfAccount[account].remove(_signer);
85-
86-
emit SignerRemoved(account, _signer);
87-
}
27+
constructor(IEntryPoint _entrypoint)
28+
BaseAccountFactory(
29+
payable(
30+
address(
31+
new DynamicAccount(_entrypoint, address(new AccountExtension(address(_entrypoint), address(this))))
32+
)
33+
)
34+
)
35+
{}
8836

8937
/*///////////////////////////////////////////////////////////////
90-
View functions
38+
Internal functions
9139
//////////////////////////////////////////////////////////////*/
9240

93-
/// @notice Returns the implementation of the Account.
94-
function accountImplementation() external view override returns (address) {
95-
return address(_accountImplementation);
96-
}
97-
98-
/// @notice Returns the address of an Account that would be deployed with the given accountId as salt.
99-
function getAddress(address _adminSigner) public view returns (address) {
100-
bytes32 salt = keccak256(abi.encode(_adminSigner));
101-
return Clones.predictDeterministicAddress(address(_accountImplementation), salt);
102-
}
103-
104-
/// @notice Returns all signers of an account.
105-
function getSignersOfAccount(address account) external view returns (address[] memory signers) {
106-
return signersOfAccount[account].values();
107-
}
108-
109-
/// @notice Returns all accounts that the given address is a signer of.
110-
function getAccountsOfSigner(address signer) external view returns (address[] memory accounts) {
111-
return accountsOfSigner[signer].values();
41+
/// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`.
42+
function _initializeAccount(
43+
address _account,
44+
address _admin,
45+
bytes calldata _data
46+
) internal override {
47+
DynamicAccount(payable(_account)).initialize(_admin, _data);
11248
}
11349
}

contracts/smart-wallet/interfaces/IAccountFactory.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ interface IAccountFactory {
2020
//////////////////////////////////////////////////////////////*/
2121

2222
/// @notice Deploys a new Account for admin.
23-
function createAccount(address admin) external returns (address account);
23+
function createAccount(address admin, bytes calldata _data) external returns (address account);
2424

2525
/// @notice Callback function for an Account to register its signers.
2626
function addSigner(address signer) external;

contracts/smart-wallet/managed/ManagedAccount.sol

+4-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import "lib/dynamic-contracts/src/core/Router.sol";
2020
contract ManagedAccount is AccountCore, Router {
2121
address public factory;
2222

23-
constructor(IEntryPoint _entrypoint) AccountCore(_entrypoint) {}
23+
constructor(IEntryPoint _entrypoint) AccountCore(_entrypoint) {
24+
_disableInitializers();
25+
}
2426

2527
/// @notice Initializes the smart contract wallet.
26-
function initialize(address _defaultAdmin) public virtual override initializer {
28+
function initialize(address _defaultAdmin, bytes calldata) public virtual override initializer {
2729
factory = msg.sender;
2830
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
2931
}

contracts/smart-wallet/managed/ManagedAccountFactory.sol

+15-80
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ pragma solidity ^0.8.12;
44
// Utils
55

66
import "../utils/BaseRouter.sol";
7-
import "../../extension/Multicall.sol";
8-
import "../../openzeppelin-presets/proxy/Clones.sol";
97
import "../../dynamic-contracts/extension/PermissionsEnumerable.sol";
10-
import "../../openzeppelin-presets/utils/structs/EnumerableSet.sol";
11-
12-
// Interface
13-
import "../interfaces/IAccountFactory.sol";
8+
import "../utils/BaseAccountFactory.sol";
9+
import "../utils/BaseAccount.sol";
10+
import "../../openzeppelin-presets/proxy/Clones.sol";
1411

1512
// Smart wallet implementation
1613
import "../utils/AccountExtension.sol";
@@ -25,98 +22,26 @@ import { ManagedAccount, IEntryPoint } from "./ManagedAccount.sol";
2522
// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ |
2623
// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/
2724

28-
contract ManagedAccountFactory is IAccountFactory, Multicall, PermissionsEnumerable, BaseRouter {
29-
using EnumerableSet for EnumerableSet.AddressSet;
30-
25+
contract ManagedAccountFactory is BaseAccountFactory, PermissionsEnumerable, BaseRouter {
3126
/*///////////////////////////////////////////////////////////////
3227
State
3328
//////////////////////////////////////////////////////////////*/
3429

35-
ManagedAccount private immutable _accountImplementation;
36-
37-
mapping(address => EnumerableSet.AddressSet) private accountsOfSigner;
38-
mapping(address => EnumerableSet.AddressSet) private signersOfAccount;
39-
4030
address public immutable defaultExtension;
4131

4232
/*///////////////////////////////////////////////////////////////
4333
Constructor
4434
//////////////////////////////////////////////////////////////*/
4535

46-
constructor(IEntryPoint _entrypoint) {
36+
constructor(IEntryPoint _entrypoint) BaseAccountFactory(payable(address(new ManagedAccount(_entrypoint)))) {
4737
defaultExtension = address(new AccountExtension(address(_entrypoint), address(this)));
4838
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
49-
_accountImplementation = new ManagedAccount(_entrypoint);
50-
}
51-
52-
/*///////////////////////////////////////////////////////////////
53-
External functions
54-
//////////////////////////////////////////////////////////////*/
55-
56-
/// @notice Deploys a new Account for admin.
57-
function createAccount(address _admin) external returns (address) {
58-
address impl = address(_accountImplementation);
59-
bytes32 salt = keccak256(abi.encode(_admin));
60-
address account = Clones.predictDeterministicAddress(impl, salt);
61-
62-
if (account.code.length > 0) {
63-
return account;
64-
}
65-
66-
account = Clones.cloneDeterministic(impl, salt);
67-
68-
ManagedAccount(payable(account)).initialize(_admin);
69-
70-
emit AccountCreated(account, _admin);
71-
72-
return account;
73-
}
74-
75-
/// @notice Callback function for an Account to register its signers.
76-
function addSigner(address _signer) external {
77-
address account = msg.sender;
78-
79-
accountsOfSigner[_signer].add(account);
80-
signersOfAccount[account].add(_signer);
81-
82-
emit SignerAdded(account, _signer);
83-
}
84-
85-
/// @notice Callback function for an Account to un-register its signers.
86-
function removeSigner(address _signer) external {
87-
address account = msg.sender;
88-
89-
accountsOfSigner[_signer].remove(account);
90-
signersOfAccount[account].remove(_signer);
91-
92-
emit SignerRemoved(account, _signer);
9339
}
9440

9541
/*///////////////////////////////////////////////////////////////
9642
View functions
9743
//////////////////////////////////////////////////////////////*/
9844

99-
/// @notice Returns the implementation of the Account.
100-
function accountImplementation() external view override returns (address) {
101-
return address(_accountImplementation);
102-
}
103-
104-
/// @notice Returns the address of an Account that would be deployed with the given admin signer.
105-
function getAddress(address _adminSigner) public view returns (address) {
106-
bytes32 salt = keccak256(abi.encode(_adminSigner));
107-
return Clones.predictDeterministicAddress(address(_accountImplementation), salt);
108-
}
109-
110-
/// @notice Returns all signers of an account.
111-
function getSignersOfAccount(address account) external view returns (address[] memory signers) {
112-
return signersOfAccount[account].values();
113-
}
114-
115-
/// @notice Returns all accounts that the given address is a signer of.
116-
function getAccountsOfSigner(address signer) external view returns (address[] memory accounts) {
117-
return accountsOfSigner[signer].values();
118-
}
119-
12045
/// @dev Returns the extension implementation address stored in router, for the given function.
12146
function getImplementationForFunction(bytes4 _functionSelector) public view override returns (address) {
12247
address impl = getExtensionForFunction(_functionSelector).implementation;
@@ -127,6 +52,16 @@ contract ManagedAccountFactory is IAccountFactory, Multicall, PermissionsEnumera
12752
Internal functions
12853
//////////////////////////////////////////////////////////////*/
12954

55+
/// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`.
56+
function _initializeAccount(
57+
address _account,
58+
address _admin,
59+
bytes calldata _data
60+
) internal override {
61+
ManagedAccount(payable(_account)).initialize(_admin, _data);
62+
}
63+
64+
/// @dev Returns whether an extension can be set in the given execution context.
13065
function _canSetExtension() internal view virtual override returns (bool) {
13166
return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
13267
}

contracts/smart-wallet/non-upgradeable/Account.sol

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,13 @@ contract Account is
6060
receive() external payable virtual {}
6161

6262
constructor(IEntryPoint _entrypoint, address _factory) {
63+
_disableInitializers();
6364
factory = _factory;
6465
entrypointContract = _entrypoint;
6566
}
6667

6768
/// @notice Initializes the smart contract wallet.
68-
function initialize(address _defaultAdmin) public virtual initializer {
69+
function initialize(address _defaultAdmin, bytes calldata) public virtual initializer {
6970
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
7071
}
7172

0 commit comments

Comments
 (0)