Skip to content

Commit 0660a23

Browse files
authored
Sync ERC7579Multisig and ERC7579MultisigWeighted with vanilla's signers (#193)
1 parent f5c0e31 commit 0660a23

File tree

5 files changed

+169
-123
lines changed

5 files changed

+169
-123
lines changed

contracts/account/modules/ERC7579Multisig.sol

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ abstract contract ERC7579Multisig is ERC7579Validator {
3131
event ERC7913SignerRemoved(address indexed account, bytes signer);
3232

3333
/// @dev Emitted when the threshold is updated.
34-
event ERC7913ThresholdSet(address indexed account, uint256 threshold);
34+
event ERC7913ThresholdSet(address indexed account, uint64 threshold);
3535

3636
/// @dev The `signer` already exists.
3737
error ERC7579MultisigAlreadyExists(bytes signer);
@@ -42,19 +42,22 @@ abstract contract ERC7579Multisig is ERC7579Validator {
4242
/// @dev The `signer` is less than 20 bytes long.
4343
error ERC7579MultisigInvalidSigner(bytes signer);
4444

45+
/// @dev The `threshold` is zero.
46+
error ERC7579MultisigZeroThreshold();
47+
4548
/// @dev The `threshold` is unreachable given the number of `signers`.
46-
error ERC7579MultisigUnreachableThreshold(uint256 signers, uint256 threshold);
49+
error ERC7579MultisigUnreachableThreshold(uint64 signers, uint64 threshold);
4750

4851
mapping(address account => EnumerableSet.BytesSet) private _signersSetByAccount;
49-
mapping(address account => uint256) private _thresholdByAccount;
52+
mapping(address account => uint64) private _thresholdByAccount;
5053

5154
/**
5255
* @dev Sets up the module's initial configuration when installed by an account.
5356
* See {ERC7579DelayedExecutor-onInstall}. Besides the delay setup, the `initdata` can
5457
* include `signers` and `threshold`.
5558
*
5659
* The initData should be encoded as:
57-
* `abi.encode(bytes[] signers, uint256 threshold)`
60+
* `abi.encode(bytes[] signers, uint64 threshold)`
5861
*
5962
* If no signers or threshold are provided, the multisignature functionality will be
6063
* disabled until they are added later.
@@ -63,9 +66,9 @@ abstract contract ERC7579Multisig is ERC7579Validator {
6366
* the signer will be set to the provided data. Future installations will behave as a no-op.
6467
*/
6568
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) {
6770
// 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));
6972
_addSigners(msg.sender, signers_);
7073
_setThreshold(msg.sender, threshold_);
7174
}
@@ -86,30 +89,33 @@ abstract contract ERC7579Multisig is ERC7579Validator {
8689
}
8790

8891
/**
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.
9095
*
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.
9399
*/
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);
96102
}
97103

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();
101107
}
102108

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);
106112
}
107113

108114
/**
109115
* @dev Returns the minimum number of signers required to approve a multisignature operation
110116
* for the specified account.
111117
*/
112-
function threshold(address account) public view virtual returns (uint256) {
118+
function threshold(address account) public view virtual returns (uint64) {
113119
return _thresholdByAccount[account];
114120
}
115121

@@ -147,7 +153,7 @@ abstract contract ERC7579Multisig is ERC7579Validator {
147153
*
148154
* * The threshold must be reachable with the current number of signers.
149155
*/
150-
function setThreshold(uint256 newThreshold) public virtual {
156+
function setThreshold(uint64 newThreshold) public virtual {
151157
_setThreshold(msg.sender, newThreshold);
152158
}
153159

@@ -181,11 +187,10 @@ abstract contract ERC7579Multisig is ERC7579Validator {
181187
* * Each of `newSigners` must not be authorized. Reverts with {ERC7579MultisigAlreadyExists} if it already exists.
182188
*/
183189
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) {
186191
bytes memory signer = newSigners[i];
187192
require(signer.length >= 20, ERC7579MultisigInvalidSigner(signer));
188-
require(_signers(account).add(signer), ERC7579MultisigAlreadyExists(signer));
193+
require(_signersSetByAccount[account].add(signer), ERC7579MultisigAlreadyExists(signer));
189194
emit ERC7913SignerAdded(account, signer);
190195
}
191196
}
@@ -199,10 +204,9 @@ abstract contract ERC7579Multisig is ERC7579Validator {
199204
* * The threshold must remain reachable after removal. See {_validateReachableThreshold} for details.
200205
*/
201206
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) {
204208
bytes memory signer = oldSigners[i];
205-
require(_signers(account).remove(signer), ERC7579MultisigNonexistentSigner(signer));
209+
require(_signersSetByAccount[account].remove(signer), ERC7579MultisigNonexistentSigner(signer));
206210
emit ERC7913SignerRemoved(account, signer);
207211
}
208212
_validateReachableThreshold(account);
@@ -213,9 +217,11 @@ abstract contract ERC7579Multisig is ERC7579Validator {
213217
*
214218
* Requirements:
215219
*
220+
* * The threshold must be greater than 0. Reverts with {ERC7579MultisigZeroThreshold} if not.
216221
* * The threshold must be reachable with the current number of signers. See {_validateReachableThreshold} for details.
217222
*/
218-
function _setThreshold(address account, uint256 newThreshold) internal virtual {
223+
function _setThreshold(address account, uint64 newThreshold) internal virtual {
224+
require(newThreshold > 0, ERC7579MultisigZeroThreshold());
219225
_thresholdByAccount[account] = newThreshold;
220226
_validateReachableThreshold(account);
221227
emit ERC7913ThresholdSet(account, newThreshold);
@@ -229,9 +235,15 @@ abstract contract ERC7579Multisig is ERC7579Validator {
229235
* * The number of signers must be >= the threshold. Reverts with {ERC7579MultisigUnreachableThreshold} if not.
230236
*/
231237
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+
);
235247
}
236248

237249
/**
@@ -252,8 +264,7 @@ abstract contract ERC7579Multisig is ERC7579Validator {
252264
bytes[] memory signingSigners,
253265
bytes[] memory signatures
254266
) 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) {
257268
if (!isSigner(account, signingSigners[i])) {
258269
return false;
259270
}

0 commit comments

Comments
 (0)