Skip to content

Commit 8c3ff17

Browse files
authored
Simplify permission model for smart accounts (#422)
* Create simpler AccountPermissions * wip * Add getters to AccountPermissionsSimple * Use AccountPermissionsSimple for account contracts * store signers of account in account, not account factory * update account tests * Rename AccountPermissionsSimple -> AccountPermissions
1 parent 7c016c6 commit 8c3ff17

File tree

11 files changed

+403
-529
lines changed

11 files changed

+403
-529
lines changed

contracts/dynamic-contracts/extension/AccountPermissions.sol

+131-85
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ library AccountPermissionsStorage {
1111
bytes32 public constant ACCOUNT_PERMISSIONS_STORAGE_POSITION = keccak256("account.permissions.storage");
1212

1313
struct Data {
14+
/// @dev The set of all admins of the wallet.
15+
EnumerableSet.AddressSet allAdmins;
16+
/// @dev The set of all signers with permission to use the account.
17+
EnumerableSet.AddressSet allSigners;
1418
/// @dev Map from address => whether the address is an admin.
1519
mapping(address => bool) isAdmin;
16-
/// @dev Map from keccak256 hash of a role => active restrictions for that role.
17-
mapping(bytes32 => IAccountPermissions.RoleStatic) roleRestrictions;
18-
/// @dev Map from address => the role held by that address.
19-
mapping(address => bytes32) roleOfAccount;
20+
/// @dev Map from signer address => active restrictions for that signer.
21+
mapping(address => IAccountPermissions.SignerPermissionsStatic) signerPermissions;
22+
/// @dev Map from signer address => approved target the signer can call using the account contract.
23+
mapping(address => EnumerableSet.AddressSet) approvedTargets;
2024
/// @dev Mapping from a signed request UID => whether the request is processed.
2125
mapping(bytes32 => bool) executed;
22-
/// @dev Map from keccak256 hash of a role to its approved targets.
23-
mapping(bytes32 => EnumerableSet.AddressSet) approvedTargets;
24-
/// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.
25-
mapping(bytes32 => EnumerableSet.AddressSet) roleMembers;
2626
}
2727

2828
function accountPermissionsStorage() internal pure returns (Data storage accountPermissionsData) {
@@ -39,7 +39,7 @@ abstract contract AccountPermissions is IAccountPermissions, EIP712 {
3939

4040
bytes32 private constant TYPEHASH =
4141
keccak256(
42-
"RoleRequest(bytes32 role,address target,uint8 action,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)"
42+
"SignerPermissionRequest(address signer,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)"
4343
);
4444

4545
modifier onlyAdmin() virtual {
@@ -56,61 +56,45 @@ abstract contract AccountPermissions is IAccountPermissions, EIP712 {
5656
_setAdmin(_account, _isAdmin);
5757
}
5858

59-
/// @notice Sets the restrictions for a given role.
60-
function setRoleRestrictions(RoleRestrictions calldata _restrictions) external virtual onlyAdmin {
61-
bytes32 role = _restrictions.role;
59+
/// @notice Sets the permissions for a given signer.
60+
function setPermissionsForSigner(SignerPermissionRequest calldata _req, bytes calldata _signature) external {
61+
address targetSigner = _req.signer;
62+
require(!isAdmin(targetSigner), "AccountPermissions: signer is already an admin");
6263

63-
require(role != bytes32(0), "AccountPermissions: role cannot be empty");
64+
require(
65+
_req.reqValidityStartTimestamp <= block.timestamp && block.timestamp < _req.reqValidityEndTimestamp,
66+
"AccountPermissions: invalid request validity period"
67+
);
68+
69+
(bool success, address signer) = verifySignerPermissionRequest(_req, _signature);
70+
require(success, "AccountPermissions: invalid signature");
6471

6572
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
66-
data.roleRestrictions[role] = RoleStatic(
67-
role,
68-
_restrictions.maxValuePerTransaction,
69-
_restrictions.startTimestamp,
70-
_restrictions.endTimestamp
73+
74+
data.allSigners.add(targetSigner);
75+
data.executed[_req.uid] = true;
76+
77+
data.signerPermissions[targetSigner] = SignerPermissionsStatic(
78+
_req.nativeTokenLimitPerTransaction,
79+
_req.permissionStartTimestamp,
80+
_req.permissionEndTimestamp
7181
);
7282

73-
address[] memory currentTargets = data.approvedTargets[role].values();
83+
address[] memory currentTargets = data.approvedTargets[targetSigner].values();
7484
uint256 currentLen = currentTargets.length;
7585

7686
for (uint256 i = 0; i < currentLen; i += 1) {
77-
data.approvedTargets[role].remove(currentTargets[i]);
87+
data.approvedTargets[targetSigner].remove(currentTargets[i]);
7888
}
7989

80-
uint256 len = _restrictions.approvedTargets.length;
90+
uint256 len = _req.approvedTargets.length;
8191
for (uint256 i = 0; i < len; i += 1) {
82-
data.approvedTargets[role].add(_restrictions.approvedTargets[i]);
92+
data.approvedTargets[targetSigner].add(_req.approvedTargets[i]);
8393
}
8494

85-
emit RoleUpdated(_restrictions.role, _restrictions);
86-
}
87-
88-
/// @notice Grant / revoke a role from a given signer.
89-
function changeRole(RoleRequest calldata _req, bytes calldata _signature) external virtual {
90-
require(_req.role != bytes32(0), "AccountPermissions: role cannot be empty");
91-
require(
92-
_req.validityStartTimestamp < block.timestamp && block.timestamp < _req.validityEndTimestamp,
93-
"AccountPermissions: invalid validity period"
94-
);
95-
96-
(bool success, address signer) = verifyRoleRequest(_req, _signature);
95+
_afterSignerPermissionsUpdate(_req);
9796

98-
require(success, "AccountPermissions: invalid signature");
99-
100-
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
101-
data.executed[_req.uid] = true;
102-
103-
if (_req.action == RoleAction.GRANT) {
104-
data.roleOfAccount[_req.target] = _req.role;
105-
data.roleMembers[_req.role].add(_req.target);
106-
} else {
107-
delete data.roleOfAccount[_req.target];
108-
data.roleMembers[_req.role].remove(_req.target);
109-
}
110-
111-
emit RoleAssignment(_req.role, _req.target, signer, _req);
112-
113-
_afterChangeRole(_req);
97+
emit SignerPermissionsUpdated(signer, targetSigner, _req);
11498
}
11599

116100
/*///////////////////////////////////////////////////////////////
@@ -123,45 +107,34 @@ abstract contract AccountPermissions is IAccountPermissions, EIP712 {
123107
return data.isAdmin[_account];
124108
}
125109

126-
/// @notice Returns the role held by a given account along with its restrictions.
127-
function getRoleRestrictionsForAccount(address _account) external view virtual returns (RoleRestrictions memory) {
110+
/// @notice Returns whether the given account is an active signer on the account.
111+
function isActiveSigner(address signer) public view returns (bool) {
128112
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
129-
bytes32 role = data.roleOfAccount[_account];
130-
RoleStatic memory roleRestrictions = data.roleRestrictions[role];
113+
SignerPermissionsStatic memory permissions = data.signerPermissions[signer];
131114

132115
return
133-
RoleRestrictions(
134-
role,
135-
data.approvedTargets[role].values(),
136-
roleRestrictions.maxValuePerTransaction,
137-
roleRestrictions.startTimestamp,
138-
roleRestrictions.endTimestamp
139-
);
116+
permissions.startTimestamp <= block.timestamp &&
117+
block.timestamp < permissions.endTimestamp &&
118+
data.approvedTargets[signer].length() > 0;
140119
}
141120

142-
/// @notice Returns the role restrictions for a given role.
143-
function getRoleRestrictions(bytes32 _role) external view virtual returns (RoleRestrictions memory) {
121+
/// @notice Returns the restrictions under which a signer can use the smart wallet.
122+
function getPermissionsForSigner(address signer) external view returns (SignerPermissions memory) {
144123
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
145-
RoleStatic memory roleRestrictions = data.roleRestrictions[_role];
124+
SignerPermissionsStatic memory permissions = data.signerPermissions[signer];
146125

147126
return
148-
RoleRestrictions(
149-
_role,
150-
data.approvedTargets[_role].values(),
151-
roleRestrictions.maxValuePerTransaction,
152-
roleRestrictions.startTimestamp,
153-
roleRestrictions.endTimestamp
127+
SignerPermissions(
128+
signer,
129+
data.approvedTargets[signer].values(),
130+
permissions.nativeTokenLimitPerTransaction,
131+
permissions.startTimestamp,
132+
permissions.endTimestamp
154133
);
155134
}
156135

157-
/// @notice Returns all accounts that have a role.
158-
function getAllRoleMembers(bytes32 _role) external view virtual returns (address[] memory) {
159-
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
160-
return data.roleMembers[_role].values();
161-
}
162-
163136
/// @dev Verifies that a request is signed by an authorized account.
164-
function verifyRoleRequest(RoleRequest calldata req, bytes calldata signature)
137+
function verifySignerPermissionRequest(SignerPermissionRequest calldata req, bytes calldata signature)
165138
public
166139
view
167140
virtual
@@ -172,23 +145,94 @@ abstract contract AccountPermissions is IAccountPermissions, EIP712 {
172145
success = !data.executed[req.uid] && isAdmin(signer);
173146
}
174147

148+
/// @notice Returns all active and inactive signers of the account.
149+
function getAllSigners() external view returns (SignerPermissions[] memory signers) {
150+
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
151+
address[] memory allSigners = data.allSigners.values();
152+
153+
uint256 len = allSigners.length;
154+
signers = new SignerPermissions[](len);
155+
for (uint256 i = 0; i < len; i += 1) {
156+
address signer = allSigners[i];
157+
SignerPermissionsStatic memory permissions = data.signerPermissions[signer];
158+
159+
signers[i] = SignerPermissions(
160+
signer,
161+
data.approvedTargets[signer].values(),
162+
permissions.nativeTokenLimitPerTransaction,
163+
permissions.startTimestamp,
164+
permissions.endTimestamp
165+
);
166+
}
167+
}
168+
169+
/// @notice Returns all signers with active permissions to use the account.
170+
function getAllActiveSigners() external view returns (SignerPermissions[] memory signers) {
171+
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
172+
address[] memory allSigners = data.allSigners.values();
173+
174+
uint256 len = allSigners.length;
175+
uint256 numOfActiveSigners = 0;
176+
bool[] memory isSignerActive = new bool[](len);
177+
178+
for (uint256 i = 0; i < len; i += 1) {
179+
address signer = allSigners[i];
180+
181+
bool isActive = isActiveSigner(signer);
182+
isSignerActive[i] = isActive;
183+
if (isActive) {
184+
numOfActiveSigners++;
185+
}
186+
}
187+
188+
signers = new SignerPermissions[](numOfActiveSigners);
189+
uint256 index = 0;
190+
for (uint256 i = 0; i < len; i += 1) {
191+
if (!isSignerActive[i]) {
192+
continue;
193+
}
194+
address signer = allSigners[i];
195+
SignerPermissionsStatic memory permissions = data.signerPermissions[signer];
196+
197+
signers[index++] = SignerPermissions(
198+
signer,
199+
data.approvedTargets[signer].values(),
200+
permissions.nativeTokenLimitPerTransaction,
201+
permissions.startTimestamp,
202+
permissions.endTimestamp
203+
);
204+
}
205+
}
206+
207+
/// @notice Returns all admins of the account.
208+
function getAllAdmins() external view returns (address[] memory) {
209+
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
210+
return data.allAdmins.values();
211+
}
212+
175213
/*///////////////////////////////////////////////////////////////
176214
Internal functions
177215
//////////////////////////////////////////////////////////////*/
178216

179217
/// @notice Runs after every `changeRole` run.
180-
function _afterChangeRole(RoleRequest calldata _req) internal virtual;
218+
function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual;
181219

182220
/// @notice Makes the given account an admin.
183221
function _setAdmin(address _account, bool _isAdmin) internal virtual {
184222
AccountPermissionsStorage.Data storage data = AccountPermissionsStorage.accountPermissionsStorage();
185223
data.isAdmin[_account] = _isAdmin;
186224

225+
if (_isAdmin) {
226+
data.allAdmins.add(_account);
227+
} else {
228+
data.allAdmins.remove(_account);
229+
}
230+
187231
emit AdminUpdated(_account, _isAdmin);
188232
}
189233

190234
/// @dev Returns the address of the signer of the request.
191-
function _recoverAddress(RoleRequest calldata _req, bytes calldata _signature)
235+
function _recoverAddress(SignerPermissionRequest calldata _req, bytes calldata _signature)
192236
internal
193237
view
194238
virtual
@@ -198,15 +242,17 @@ abstract contract AccountPermissions is IAccountPermissions, EIP712 {
198242
}
199243

200244
/// @dev Encodes a request for recovery of the signer in `recoverAddress`.
201-
function _encodeRequest(RoleRequest calldata _req) internal pure virtual returns (bytes memory) {
245+
function _encodeRequest(SignerPermissionRequest calldata _req) internal pure virtual returns (bytes memory) {
202246
return
203247
abi.encode(
204248
TYPEHASH,
205-
_req.role,
206-
_req.target,
207-
_req.action,
208-
_req.validityStartTimestamp,
209-
_req.validityEndTimestamp,
249+
_req.signer,
250+
_req.approvedTargets,
251+
_req.nativeTokenLimitPerTransaction,
252+
_req.permissionStartTimestamp,
253+
_req.permissionEndTimestamp,
254+
_req.reqValidityStartTimestamp,
255+
_req.reqValidityEndTimestamp,
210256
_req.uid
211257
);
212258
}

0 commit comments

Comments
 (0)