@@ -31,7 +31,7 @@ abstract contract ERC7579Multisig is ERC7579Validator {
31
31
event ERC7913SignerRemoved (address indexed account , bytes signer );
32
32
33
33
/// @dev Emitted when the threshold is updated.
34
- event ERC7913ThresholdSet (address indexed account , uint256 threshold );
34
+ event ERC7913ThresholdSet (address indexed account , uint64 threshold );
35
35
36
36
/// @dev The `signer` already exists.
37
37
error ERC7579MultisigAlreadyExists (bytes signer );
@@ -42,19 +42,22 @@ abstract contract ERC7579Multisig is ERC7579Validator {
42
42
/// @dev The `signer` is less than 20 bytes long.
43
43
error ERC7579MultisigInvalidSigner (bytes signer );
44
44
45
+ /// @dev The `threshold` is zero.
46
+ error ERC7579MultisigZeroThreshold ();
47
+
45
48
/// @dev The `threshold` is unreachable given the number of `signers`.
46
- error ERC7579MultisigUnreachableThreshold (uint256 signers , uint256 threshold );
49
+ error ERC7579MultisigUnreachableThreshold (uint64 signers , uint64 threshold );
47
50
48
51
mapping (address account = > EnumerableSet.BytesSet) private _signersSetByAccount;
49
- mapping (address account = > uint256 ) private _thresholdByAccount;
52
+ mapping (address account = > uint64 ) private _thresholdByAccount;
50
53
51
54
/**
52
55
* @dev Sets up the module's initial configuration when installed by an account.
53
56
* See {ERC7579DelayedExecutor-onInstall}. Besides the delay setup, the `initdata` can
54
57
* include `signers` and `threshold`.
55
58
*
56
59
* The initData should be encoded as:
57
- * `abi.encode(bytes[] signers, uint256 threshold)`
60
+ * `abi.encode(bytes[] signers, uint64 threshold)`
58
61
*
59
62
* If no signers or threshold are provided, the multisignature functionality will be
60
63
* disabled until they are added later.
@@ -63,9 +66,9 @@ abstract contract ERC7579Multisig is ERC7579Validator {
63
66
* the signer will be set to the provided data. Future installations will behave as a no-op.
64
67
*/
65
68
function onInstall (bytes calldata initData ) public virtual {
66
- if (initData.length > 32 && _signers (msg .sender ). length ( ) == 0 ) {
69
+ if (initData.length > 32 && getSignerCount (msg .sender ) == 0 ) {
67
70
// More than just delay parameter
68
- (bytes [] memory signers_ , uint256 threshold_ ) = abi.decode (initData, (bytes [], uint256 ));
71
+ (bytes [] memory signers_ , uint64 threshold_ ) = abi.decode (initData, (bytes [], uint64 ));
69
72
_addSigners (msg .sender , signers_);
70
73
_setThreshold (msg .sender , threshold_);
71
74
}
@@ -86,30 +89,33 @@ abstract contract ERC7579Multisig is ERC7579Validator {
86
89
}
87
90
88
91
/**
89
- * @dev Returns the set of authorized signers for the specified account.
92
+ * @dev Returns a slice of the set of authorized signers for the specified account.
93
+ *
94
+ * Using `start = 0` and `end = type(uint64).max` will return the entire set of signers.
90
95
*
91
- * WARNING: This operation copies the entire signers set to memory, which
92
- * can be expensive or may result in unbounded computation.
96
+ * WARNING: Depending on the `start` and `end`, this operation can copy a large amount of data to memory, which
97
+ * can be expensive. This is designed for view accessors queried without gas fees. Using it in state-changing
98
+ * functions may become uncallable if the slice grows too large.
93
99
*/
94
- function signers (address account ) public view virtual returns (bytes [] memory ) {
95
- return _signers ( account) .values ();
100
+ function getSigners (address account , uint64 start , uint64 end ) public view virtual returns (bytes [] memory ) {
101
+ return _signersSetByAccount[ account] .values (start, end );
96
102
}
97
103
98
- /// @dev Returns whether the `signer` is an authorized signer for the specified account.
99
- function isSigner (address account , bytes memory signer ) public view virtual returns (bool ) {
100
- return _signers ( account). contains (signer );
104
+ /// @dev Returns the number of authorized signers for the specified account.
105
+ function getSignerCount (address account ) public view virtual returns (uint256 ) {
106
+ return _signersSetByAccount[ account]. length ( );
101
107
}
102
108
103
- /// @dev Returns the set of authorized signers for the specified account.
104
- function _signers (address account ) internal view virtual returns (EnumerableSet.BytesSet storage ) {
105
- return _signersSetByAccount[account];
109
+ /// @dev Returns whether the `signer` is an authorized signer for the specified account.
110
+ function isSigner (address account , bytes memory signer ) public view virtual returns (bool ) {
111
+ return _signersSetByAccount[account]. contains (signer) ;
106
112
}
107
113
108
114
/**
109
115
* @dev Returns the minimum number of signers required to approve a multisignature operation
110
116
* for the specified account.
111
117
*/
112
- function threshold (address account ) public view virtual returns (uint256 ) {
118
+ function threshold (address account ) public view virtual returns (uint64 ) {
113
119
return _thresholdByAccount[account];
114
120
}
115
121
@@ -147,7 +153,7 @@ abstract contract ERC7579Multisig is ERC7579Validator {
147
153
*
148
154
* * The threshold must be reachable with the current number of signers.
149
155
*/
150
- function setThreshold (uint256 newThreshold ) public virtual {
156
+ function setThreshold (uint64 newThreshold ) public virtual {
151
157
_setThreshold (msg .sender , newThreshold);
152
158
}
153
159
@@ -181,11 +187,10 @@ abstract contract ERC7579Multisig is ERC7579Validator {
181
187
* * Each of `newSigners` must not be authorized. Reverts with {ERC7579MultisigAlreadyExists} if it already exists.
182
188
*/
183
189
function _addSigners (address account , bytes [] memory newSigners ) internal virtual {
184
- uint256 newSignersLength = newSigners.length ;
185
- for (uint256 i = 0 ; i < newSignersLength; i++ ) {
190
+ for (uint256 i = 0 ; i < newSigners.length ; ++ i) {
186
191
bytes memory signer = newSigners[i];
187
192
require (signer.length >= 20 , ERC7579MultisigInvalidSigner (signer));
188
- require (_signers ( account) .add (signer), ERC7579MultisigAlreadyExists (signer));
193
+ require (_signersSetByAccount[ account] .add (signer), ERC7579MultisigAlreadyExists (signer));
189
194
emit ERC7913SignerAdded (account, signer);
190
195
}
191
196
}
@@ -199,10 +204,9 @@ abstract contract ERC7579Multisig is ERC7579Validator {
199
204
* * The threshold must remain reachable after removal. See {_validateReachableThreshold} for details.
200
205
*/
201
206
function _removeSigners (address account , bytes [] memory oldSigners ) internal virtual {
202
- uint256 oldSignersLength = oldSigners.length ;
203
- for (uint256 i = 0 ; i < oldSignersLength; i++ ) {
207
+ for (uint256 i = 0 ; i < oldSigners.length ; ++ i) {
204
208
bytes memory signer = oldSigners[i];
205
- require (_signers ( account) .remove (signer), ERC7579MultisigNonexistentSigner (signer));
209
+ require (_signersSetByAccount[ account] .remove (signer), ERC7579MultisigNonexistentSigner (signer));
206
210
emit ERC7913SignerRemoved (account, signer);
207
211
}
208
212
_validateReachableThreshold (account);
@@ -213,9 +217,11 @@ abstract contract ERC7579Multisig is ERC7579Validator {
213
217
*
214
218
* Requirements:
215
219
*
220
+ * * The threshold must be greater than 0. Reverts with {ERC7579MultisigZeroThreshold} if not.
216
221
* * The threshold must be reachable with the current number of signers. See {_validateReachableThreshold} for details.
217
222
*/
218
- function _setThreshold (address account , uint256 newThreshold ) internal virtual {
223
+ function _setThreshold (address account , uint64 newThreshold ) internal virtual {
224
+ require (newThreshold > 0 , ERC7579MultisigZeroThreshold ());
219
225
_thresholdByAccount[account] = newThreshold;
220
226
_validateReachableThreshold (account);
221
227
emit ERC7913ThresholdSet (account, newThreshold);
@@ -229,9 +235,15 @@ abstract contract ERC7579Multisig is ERC7579Validator {
229
235
* * The number of signers must be >= the threshold. Reverts with {ERC7579MultisigUnreachableThreshold} if not.
230
236
*/
231
237
function _validateReachableThreshold (address account ) internal view virtual {
232
- uint256 totalSigners = _signers (account).length ();
233
- uint256 currentThreshold = threshold (account);
234
- require (totalSigners >= currentThreshold, ERC7579MultisigUnreachableThreshold (totalSigners, currentThreshold));
238
+ uint256 totalSigners = getSignerCount (account);
239
+ uint64 currentThreshold = threshold (account);
240
+ require (
241
+ totalSigners >= currentThreshold,
242
+ ERC7579MultisigUnreachableThreshold (
243
+ uint64 (totalSigners), // Safe cast. Economically impossible to overflow.
244
+ currentThreshold
245
+ )
246
+ );
235
247
}
236
248
237
249
/**
@@ -252,8 +264,7 @@ abstract contract ERC7579Multisig is ERC7579Validator {
252
264
bytes [] memory signingSigners ,
253
265
bytes [] memory signatures
254
266
) internal view virtual returns (bool valid ) {
255
- uint256 signersLength = signingSigners.length ;
256
- for (uint256 i = 0 ; i < signersLength; i++ ) {
267
+ for (uint256 i = 0 ; i < signingSigners.length ; ++ i) {
257
268
if (! isSigner (account, signingSigners[i])) {
258
269
return false ;
259
270
}
0 commit comments