@@ -7,8 +7,10 @@ contract PermittableToken is ERC677BridgeToken {
7
7
8
8
// EIP712 niceties
9
9
bytes32 public DOMAIN_SEPARATOR;
10
- // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
11
- bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb ;
10
+ // bytes32 public constant PERMIT_TYPEHASH_LEGACY = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
11
+ bytes32 public constant PERMIT_TYPEHASH_LEGACY = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb ;
12
+ // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
13
+ bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9 ;
12
14
13
15
mapping (address => uint256 ) public nonces;
14
16
mapping (address => mapping (address => uint256 )) public expirations;
@@ -56,7 +58,7 @@ contract PermittableToken is ERC677BridgeToken {
56
58
} else {
57
59
// If allowance is unlimited by `permit`, `approve`, or `increaseAllowance`
58
60
// function, don't adjust it. But the expiration date must be empty or in the future
59
- require (expirations[_sender][msg .sender ] == 0 || expirations[_sender][msg .sender ] >= _now () );
61
+ require (expirations[_sender][msg .sender ] == 0 || expirations[_sender][msg .sender ] >= now );
60
62
}
61
63
} else {
62
64
// If `_sender` is `msg.sender`,
@@ -71,20 +73,16 @@ contract PermittableToken is ERC677BridgeToken {
71
73
/// @param _to The address which will spend the funds.
72
74
/// @param _value The amount of tokens to be spent.
73
75
function approve (address _to , uint256 _value ) public returns (bool result ) {
74
- result = super .approve (_to, _value);
75
- if (_value == uint256 (- 1 )) {
76
- delete expirations[msg .sender ][_to];
77
- }
76
+ _approveAndResetExpirations (msg .sender , _to, _value);
77
+ return true ;
78
78
}
79
79
80
80
/// @dev Atomically increases the allowance granted to spender by the caller.
81
81
/// @param _to The address which will spend the funds.
82
82
/// @param _addedValue The amount of tokens to increase the allowance by.
83
83
function increaseAllowance (address _to , uint256 _addedValue ) public returns (bool result ) {
84
- result = super .increaseAllowance (_to, _addedValue);
85
- if (allowed[msg .sender ][_to] == uint256 (- 1 )) {
86
- delete expirations[msg .sender ][_to];
87
- }
84
+ _approveAndResetExpirations (msg .sender , _to, allowed[msg .sender ][_to].add (_addedValue));
85
+ return true ;
88
86
}
89
87
90
88
/// @dev An alias for `transfer` function.
@@ -134,33 +132,107 @@ contract PermittableToken is ERC677BridgeToken {
134
132
bytes32 _r ,
135
133
bytes32 _s
136
134
) external {
137
- require (_holder != address (0 ));
138
- require (_spender != address (0 ));
139
- require (_expiry == 0 || _now () <= _expiry);
140
-
141
- require (_v == 27 || _v == 28 );
142
- require (uint256 (_s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 );
143
-
144
- bytes32 digest = keccak256 (
145
- abi.encodePacked (
146
- "\x19\x01 " ,
147
- DOMAIN_SEPARATOR,
148
- keccak256 (abi.encode (PERMIT_TYPEHASH, _holder, _spender, _nonce, _expiry, _allowed))
149
- )
150
- );
135
+ require (_expiry == 0 || now <= _expiry);
151
136
152
- require (_holder == ecrecover (digest, _v, _r, _s));
137
+ bytes32 digest = _digest (abi.encode (PERMIT_TYPEHASH_LEGACY, _holder, _spender, _nonce, _expiry, _allowed));
138
+
139
+ require (_holder == _recover (digest, _v, _r, _s));
153
140
require (_nonce == nonces[_holder]++ );
154
141
155
142
uint256 amount = _allowed ? uint256 (- 1 ) : 0 ;
156
143
157
- allowed[_holder][_spender] = amount;
158
144
expirations[_holder][_spender] = _allowed ? _expiry : 0 ;
159
145
160
- emit Approval (_holder, _spender, amount);
146
+ _approve (_holder, _spender, amount);
161
147
}
162
148
163
- function _now () internal view returns (uint256 ) {
164
- return now ;
149
+ /** @dev Allows to spend holder's unlimited amount by the specified spender according to EIP2612.
150
+ * The function can be called by anyone, but requires having allowance parameters
151
+ * signed by the holder according to EIP712.
152
+ * @param _holder The holder's address.
153
+ * @param _spender The spender's address.
154
+ * @param _value Allowance value to set as a result of the call.
155
+ * @param _deadline The deadline timestamp to call the permit function. Must be a timestamp in the future.
156
+ * Note that timestamps are not precise, malicious miner/validator can manipulate them to some extend.
157
+ * Assume that there can be a 900 seconds time delta between the desired timestamp and the actual expiration.
158
+ * @param _v A final byte of signature (ECDSA component).
159
+ * @param _r The first 32 bytes of signature (ECDSA component).
160
+ * @param _s The second 32 bytes of signature (ECDSA component).
161
+ */
162
+ function permit (
163
+ address _holder ,
164
+ address _spender ,
165
+ uint256 _value ,
166
+ uint256 _deadline ,
167
+ uint8 _v ,
168
+ bytes32 _r ,
169
+ bytes32 _s
170
+ ) external {
171
+ require (now <= _deadline);
172
+
173
+ uint256 nonce = nonces[_holder]++ ;
174
+ bytes32 digest = _digest (abi.encode (PERMIT_TYPEHASH, _holder, _spender, _value, nonce, _deadline));
175
+
176
+ require (_holder == _recover (digest, _v, _r, _s));
177
+
178
+ _approveAndResetExpirations (_holder, _spender, _value);
179
+ }
180
+
181
+ /**
182
+ * @dev Sets a new allowance value for the given owner and spender addresses.
183
+ * Resets expiration timestamp in case of unlimited approval.
184
+ * @param _owner address tokens holder.
185
+ * @param _spender address of tokens spender.
186
+ * @param _amount amount of approved tokens.
187
+ */
188
+ function _approveAndResetExpirations (address _owner , address _spender , uint256 _amount ) internal {
189
+ _approve (_owner, _spender, _amount);
190
+
191
+ // it is not necessary to reset _expirations in other cases, since it is only used together with infinite allowance
192
+ if (_amount == uint256 (- 1 )) {
193
+ delete expirations[_owner][_spender];
194
+ }
195
+ }
196
+
197
+ /**
198
+ * @dev Internal function for issuing an allowance.
199
+ * @param _owner address of the tokens owner.
200
+ * @param _spender address of the approved tokens spender.
201
+ * @param _amount amount of the approved tokens.
202
+ */
203
+ function _approve (address _owner , address _spender , uint256 _amount ) internal {
204
+ require (_owner != address (0 ), "ERC20: approve from the zero address " );
205
+ require (_spender != address (0 ), "ERC20: approve to the zero address " );
206
+
207
+ allowed[_owner][_spender] = _amount;
208
+ emit Approval (_owner, _spender, _amount);
209
+ }
210
+
211
+ /**
212
+ * @dev Calculates the message digest for encoded EIP712 typed struct.
213
+ * @param _typedStruct encoded payload.
214
+ */
215
+ function _digest (bytes memory _typedStruct ) internal view returns (bytes32 ) {
216
+ return keccak256 (abi.encodePacked ("\x19\x01 " , DOMAIN_SEPARATOR, keccak256 (_typedStruct)));
217
+ }
218
+
219
+ /**
220
+ * @dev Derives the signer address for the given message digest and ECDSA signature params.
221
+ * @param _digest signed message digest.
222
+ * @param _v a final byte of signature (ECDSA component).
223
+ * @param _r the first 32 bytes of the signature (ECDSA component).
224
+ * @param _s the second 32 bytes of the signature (ECDSA component).
225
+ */
226
+ function _recover (bytes32 _digest , uint8 _v , bytes32 _r , bytes32 _s ) internal pure returns (address ) {
227
+ require (_v == 27 || _v == 28 , "ECDSA: invalid signature 'v' value " );
228
+ require (
229
+ uint256 (_s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 ,
230
+ "ECDSA: invalid signature 's' value "
231
+ );
232
+
233
+ address signer = ecrecover (_digest, _v, _r, _s);
234
+ require (signer != address (0 ), "ECDSA: invalid signature " );
235
+
236
+ return signer;
165
237
}
166
238
}
0 commit comments