-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathModuleIgnoreNonceCalls.sol
More file actions
199 lines (175 loc) · 6.16 KB
/
ModuleIgnoreNonceCalls.sol
File metadata and controls
199 lines (175 loc) · 6.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.27;
import "./ModuleSelfAuth.sol";
import "./ModuleStorage.sol";
import "./ModuleERC165.sol";
import "./NonceKey.sol";
import "./interfaces/IModuleCalls.sol";
import "./interfaces/IModuleAuth.sol";
/**
@notice Implements ModuleCalls but ignores the validity of the nonce
should only be used during gas estimation.
*/
abstract contract ModuleIgnoreNonceCalls is IModuleCalls, IModuleAuth, ModuleERC165, ModuleSelfAuth {
uint256 private constant NONCE_BITS = 96;
bytes32 private constant NONCE_MASK = bytes32((1 << NONCE_BITS) - 1);
/**
* @notice Returns the next nonce of the default nonce space
* @dev The default nonce space is 0x00
* @return The next nonce
*/
function nonce() external override virtual view returns (uint256) {
return readNonce(0);
}
/**
* @notice Returns the next nonce of the given nonce space
* @param _space Nonce space, each space keeps an independent nonce count
* @return The next nonce
*/
function readNonce(uint256 _space) public override virtual view returns (uint256) {
return uint256(ModuleStorage.readBytes32Map(NonceKey.NONCE_KEY, bytes32(_space)));
}
/**
* @notice Changes the next nonce of the given nonce space
* @param _space Nonce space, each space keeps an independent nonce count
* @param _nonce Nonce to write on the space
*/
function _writeNonce(uint256 _space, uint256 _nonce) private {
ModuleStorage.writeBytes32Map(NonceKey.NONCE_KEY, bytes32(_space), bytes32(_nonce));
}
/**
* @notice Allow wallet owner to execute an action
* @dev Relayers must ensure that the gasLimit specified for each transaction
* is acceptable to them. A user could specify large enough that it could
* consume all the gas available.
* @param _txs Transactions to process
* @param _nonce Signature nonce (may contain an encoded space)
* @param _signature Encoded signature
*/
function execute(
Transaction[] memory _txs,
uint256 _nonce,
bytes memory _signature
) public override virtual {
// Validate and update nonce
_validateNonce(_nonce);
// Hash transaction bundle
bytes32 txHash = _subDigest(keccak256(abi.encode(_nonce, _txs)));
// Verify that signatures are valid
require(
_signatureValidation(txHash, _signature),
"ModuleCalls#execute: INVALID_SIGNATURE"
);
// Execute the transactions
_execute(txHash, _txs);
}
/**
* @notice Allow wallet to execute an action
* without signing the message
* @param _txs Transactions to execute
*/
function selfExecute(
Transaction[] memory _txs
) public override virtual onlySelf {
// Hash transaction bundle
bytes32 txHash = _subDigest(keccak256(abi.encode('self:', _txs)));
// Execute the transactions
_execute(txHash, _txs);
}
/**
* @notice Executes a list of transactions
* @param _txHash Hash of the batch of transactions
* @param _txs Transactions to execute
*/
function _execute(
bytes32 _txHash,
Transaction[] memory _txs
) private {
// Execute transaction
for (uint256 i = 0; i < _txs.length; i++) {
Transaction memory transaction = _txs[i];
bool success;
bytes memory result;
require(gasleft() >= transaction.gasLimit, "ModuleCalls#_execute: NOT_ENOUGH_GAS");
if (transaction.delegateCall) {
(success, result) = transaction.target.delegatecall{
gas: transaction.gasLimit == 0 ? gasleft() : transaction.gasLimit
}(transaction.data);
} else {
(success, result) = transaction.target.call{
value: transaction.value,
gas: transaction.gasLimit == 0 ? gasleft() : transaction.gasLimit
}(transaction.data);
}
if (success) {
emit TxExecuted(_txHash);
} else {
_revertBytes(transaction, _txHash, result);
}
}
}
/**
* @notice Verify if a nonce is valid
* @param _rawNonce Nonce to validate (may contain an encoded space)
* @dev A valid nonce must be above the last one used
* with a maximum delta of 100
*/
function _validateNonce(uint256 _rawNonce) private {
// Retrieve current nonce for this wallet
(uint256 space, uint256 providedNonce) = _decodeNonce(_rawNonce);
uint256 currentNonce = readNonce(space);
// Verify if nonce is valid
//
// Skip nonce validation for gas estimation, but keep the statement to
// arrive at a closer gas expenditure when compared with the version in
// ModuleCalls.sol
require(
(providedNonce == currentNonce) || true,
"MainModule#_auth: INVALID_NONCE"
);
// Update signature nonce
uint256 newNonce = providedNonce + 1;
_writeNonce(space, newNonce);
emit NonceChange(space, newNonce);
}
/**
* @notice Logs a failed transaction, reverts if the transaction is not optional
* @param _tx Transaction that is reverting
* @param _txHash Hash of the transaction
* @param _reason Encoded revert message
*/
function _revertBytes(
Transaction memory _tx,
bytes32 _txHash,
bytes memory _reason
) internal {
if (_tx.revertOnError) {
assembly { revert(add(_reason, 0x20), mload(_reason)) }
} else {
emit TxFailed(_txHash, _reason);
}
}
/**
* @notice Decodes a raw nonce
* @dev A raw nonce is encoded using the first 160 bits for the space
* and the last 96 bits for the nonce
* @param _rawNonce Nonce to be decoded
* @return _space The nonce space of the raw nonce
* @return _nonce The nonce of the raw nonce
*/
function _decodeNonce(uint256 _rawNonce) private pure returns (uint256 _space, uint256 _nonce) {
_nonce = uint256(bytes32(_rawNonce) & NONCE_MASK);
_space = _rawNonce >> NONCE_BITS;
}
/**
* @notice Query if a contract implements an interface
* @param _interfaceID The interface identifier, as specified in ERC-165
* @return `true` if the contract implements `_interfaceID`
*/
function supportsInterface(bytes4 _interfaceID) public override virtual pure returns (bool) {
if (_interfaceID == type(IModuleCalls).interfaceId) {
return true;
}
return super.supportsInterface(_interfaceID);
}
}