1
+ // Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
2
+
3
+ // This program is free software: you can redistribute it and/or modify
4
+ // it under the terms of the GNU General Public License as published by
5
+ // the Free Software Foundation, either version 3 of the License, or
6
+ // (at your option) any later version.
7
+
8
+ // This program is distributed in the hope that it will be useful,
9
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ // GNU General Public License for more details.
12
+
13
+ // You should have received a copy of the GNU General Public License
14
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
1
16
pragma solidity ^ 0.5.4 ;
2
17
3
18
/**
4
19
* @title MultiSig
5
20
* @dev Simple MultiSig using off-chain signing.
6
- * @author Julien Niset - <julien@argent.im >
21
+ * @author Julien Niset - <julien@argent.xyz >
7
22
*/
8
23
contract MultiSigWallet {
9
24
10
25
uint constant public MAX_OWNER_COUNT = 10 ;
11
26
12
27
// Incrementing counter to prevent replay attacks
13
- uint256 public nonce;
14
- // The threshold
15
- uint256 public threshold;
28
+ uint256 public nonce;
29
+ // The threshold
30
+ uint256 public threshold;
16
31
// The number of owners
17
32
uint256 public ownersCount;
18
33
// Mapping to check if an address is an owner
19
- mapping (address => bool ) public isOwner;
34
+ mapping (address => bool ) public isOwner;
20
35
21
36
// Events
22
37
event OwnerAdded (address indexed owner );
@@ -26,7 +41,8 @@ contract MultiSigWallet {
26
41
event Received (uint256 indexed value , address indexed from );
27
42
28
43
/**
29
- * @dev Throws is the calling account is not the multisig.
44
+ * @dev Throws if the calling account is not the multisig.
45
+ * @dev Mainly used for enforcing the use of internal functions through the "execute" function
30
46
*/
31
47
modifier onlyWallet () {
32
48
require (msg .sender == address (this ), "MSW: Calling account is not wallet " );
@@ -36,23 +52,23 @@ contract MultiSigWallet {
36
52
/**
37
53
* @dev Constructor.
38
54
* @param _threshold The threshold of the multisig.
39
- * @param _owners The owners of the multisig.
55
+ * @param _owners The initial set of owners of the multisig.
40
56
*/
41
57
constructor (uint256 _threshold , address [] memory _owners ) public {
42
58
require (_owners.length > 0 && _owners.length <= MAX_OWNER_COUNT, "MSW: Not enough or too many owners " );
43
59
require (_threshold > 0 && _threshold <= _owners.length , "MSW: Invalid threshold " );
44
60
ownersCount = _owners.length ;
45
61
threshold = _threshold;
46
- for (uint256 i = 0 ; i < _owners.length ; i++ ) {
62
+ for (uint256 i = 0 ; i < _owners.length ; i++ ) {
47
63
isOwner[_owners[i]] = true ;
48
64
emit OwnerAdded (_owners[i]);
49
65
}
50
66
emit ThresholdChanged (_threshold);
51
67
}
52
68
53
69
/**
54
- * @dev Only entry point of the multisig. The method will execute any transaction provided that it
55
- * receieved enough signatures from the wallet owners.
70
+ * @dev Only entry point of the multisig. The method will execute any transaction provided that it
71
+ * receieved enough signatures from the wallet owners.
56
72
* @param _to The destination address for the transaction to execute.
57
73
* @param _value The value parameter for the transaction to execute.
58
74
* @param _data The data parameter for the transaction to execute.
@@ -68,27 +84,28 @@ contract MultiSigWallet {
68
84
nonce += 1 ;
69
85
uint256 valid = 0 ;
70
86
address lastSigner = address (0 );
71
- for (uint256 i = 0 ; i < count; i++ ) {
87
+ for (uint256 i = 0 ; i < count; i++ ) {
72
88
(v,r,s) = splitSignature (_signatures, i);
73
89
address recovered = ecrecover (keccak256 (abi.encodePacked ("\x19Ethereum Signed Message:\n32 " ,txHash)), v, r, s);
74
90
require (recovered > lastSigner, "MSW: Badly ordered signatures " ); // make sure signers are different
75
91
lastSigner = recovered;
76
- if (isOwner[recovered]) {
92
+ if (isOwner[recovered]) {
77
93
valid += 1 ;
78
- if (valid >= threshold) {
94
+ if (valid >= threshold) {
95
+ // solium-disable-next-line security/no-call-value
79
96
(bool success ,) = _to.call.value (_value)(_data);
80
97
require (success, "MSW: External call failed " );
81
98
emit Executed (_to, _value, _data);
82
99
return ;
83
100
}
84
101
}
85
102
}
86
- // If we reach that point then the transaction is not executed
103
+ // If not enough signatures for threshold, then the transaction is not executed
87
104
revert ("MSW: Not enough valid signatures " );
88
105
}
89
106
90
107
/**
91
- * @dev Adds an owner to the multisig. This method can only be called by the multisig itself
108
+ * @dev Adds an owner to the multisig. This method can only be called by the multisig itself
92
109
* (i.e. it must go through the execute method and be confirmed by the owners).
93
110
* @param _owner The address of the new owner.
94
111
*/
@@ -101,9 +118,9 @@ contract MultiSigWallet {
101
118
}
102
119
103
120
/**
104
- * @dev Removes an owner from the multisig. This method can only be called by the multisig itself
121
+ * @dev Removes an owner from the multisig. This method can only be called by the multisig itself
105
122
* (i.e. it must go through the execute method and be confirmed by the owners).
106
- * @param _owner The address of the removed owner.
123
+ * @param _owner The address of the owner to be removed .
107
124
*/
108
125
function removeOwner (address _owner ) public onlyWallet {
109
126
require (ownersCount > threshold, "MSW: Too few owners left " );
@@ -114,7 +131,7 @@ contract MultiSigWallet {
114
131
}
115
132
116
133
/**
117
- * @dev Changes the threshold of the multisig. This method can only be called by the multisig itself
134
+ * @dev Changes the threshold of the multisig. This method can only be called by the multisig itself
118
135
* (i.e. it must go through the execute method and be confirmed by the owners).
119
136
* @param _newThreshold The new threshold.
120
137
*/
@@ -125,28 +142,29 @@ contract MultiSigWallet {
125
142
}
126
143
127
144
/**
128
- * @dev Makes it possible for the multisig to receive ETH.
129
- */
130
- function () external payable {
131
- emit Received (msg .value , msg .sender );
132
- }
133
-
134
- /**
135
145
* @dev Parses the signatures and extract (r, s, v) for a signature at a given index.
136
- * A signature is {bytes32 r}{bytes32 s}{uint8 v} in compact form and signatures are concatenated.
146
+ * A signature is {bytes32 r}{bytes32 s}{uint8 v} in compact form where the signatures are concatenated.
137
147
* @param _signatures concatenated signatures
138
148
* @param _index which signature to read (0, 1, 2, ...)
139
149
*/
140
150
function splitSignature (bytes memory _signatures , uint256 _index ) internal pure returns (uint8 v , bytes32 r , bytes32 s ) {
141
151
// we jump 32 (0x20) as the first slot of bytes contains the length
142
152
// we jump 65 (0x41) per signature
143
153
// for v we load 32 bytes ending with v (the first 31 come from s) tehn apply a mask
154
+ // solium-disable-next-line security/no-inline-assembly
144
155
assembly {
145
156
r := mload (add (_signatures, add (0x20 ,mul (0x41 ,_index))))
146
157
s := mload (add (_signatures, add (0x40 ,mul (0x41 ,_index))))
147
158
v := and (mload (add (_signatures, add (0x41 ,mul (0x41 ,_index)))), 0xff )
148
159
}
149
- require (v == 27 || v == 28 , "MSW: Invalid v " );
160
+ require (v == 27 || v == 28 , "MSW: Invalid v " );
161
+ }
162
+
163
+ /**
164
+ * @dev Fallback function to allow the multisig to receive ETH, which will fail if not implemented
165
+ */
166
+ function () external payable {
167
+ emit Received (msg .value , msg .sender );
150
168
}
151
169
152
170
}
0 commit comments