@@ -4,6 +4,7 @@ pragma abicoder v2;
44
55import {Vm} from 'forge-std/Vm.sol ' ;
66import {Test} from 'forge-std/Test.sol ' ;
7+ import {console2} from 'forge-std/console2.sol ' ;
78import {AaveGovernanceV2, IAaveGovernanceV2, IExecutorWithTimelock} from 'aave-address-book/AaveGovernanceV2.sol ' ;
89import {IPoolAddressesProvider} from 'aave-address-book/AaveV3.sol ' ;
910import {AaveMisc} from 'aave-address-book/AaveMisc.sol ' ;
@@ -112,6 +113,19 @@ library GovHelpers {
112113 withDelegatecalls[i] = true ;
113114 }
114115
116+ console2.logBytes (
117+ abi.encodeWithSelector (
118+ AaveGovernanceV2.GOV.create.selector ,
119+ IExecutorWithTimelock (executor),
120+ targets,
121+ values,
122+ signatures,
123+ calldatas,
124+ withDelegatecalls,
125+ ipfsHash
126+ )
127+ );
128+
115129 return
116130 AaveGovernanceV2.GOV.create (
117131 IExecutorWithTimelock (executor),
@@ -182,13 +196,285 @@ library GovHelpers {
182196
183197/**
184198 * @dev Mock contract which allows performing a delegatecall to `execute`
185- * Intended to be used as replacement for L2 admins to mock governance/gnosis execution.
199+ * Intended to be used as replacement for L2 admins/executors to mock governance/gnosis execution.
186200 */
187201contract MockExecutor {
202+ error OnlyQueuedActions ();
203+ error InvalidActionsSetId ();
204+ error InsufficientBalance ();
205+ error FailedActionExecution ();
206+ error DuplicateAction ();
207+ error InconsistentParamsLength ();
208+ error EmptyTargets ();
209+
210+ /**
211+ * @dev Emitted when an ActionsSet is queued
212+ * @param id Id of the ActionsSet
213+ * @param targets Array of targets to be called by the actions set
214+ * @param values Array of values to pass in each call by the actions set
215+ * @param signatures Array of function signatures to encode in each call by the actions set
216+ * @param calldatas Array of calldata to pass in each call by the actions set
217+ * @param withDelegatecalls Array of whether to delegatecall for each call of the actions set
218+ * @param executionTime The timestamp at which this actions set can be executed
219+ **/
220+ event ActionsSetQueued (
221+ uint256 indexed id ,
222+ address [] targets ,
223+ uint256 [] values ,
224+ string [] signatures ,
225+ bytes [] calldatas ,
226+ bool [] withDelegatecalls ,
227+ uint256 executionTime
228+ );
229+
230+ /**
231+ * @dev Emitted when an ActionsSet is successfully executed
232+ * @param id Id of the ActionsSet
233+ * @param initiatorExecution The address that triggered the ActionsSet execution
234+ * @param returnedData The returned data from the ActionsSet execution
235+ **/
236+ event ActionsSetExecuted (
237+ uint256 indexed id ,
238+ address indexed initiatorExecution ,
239+ bytes [] returnedData
240+ );
241+
242+ /**
243+ * @notice This struct contains the data needed to execute a specified set of actions
244+ * @param targets Array of targets to call
245+ * @param values Array of values to pass in each call
246+ * @param signatures Array of function signatures to encode in each call (can be empty)
247+ * @param calldatas Array of calldatas to pass in each call, appended to the signature at the same array index if not empty
248+ * @param withDelegateCalls Array of whether to delegatecall for each call
249+ * @param executionTime Timestamp starting from which the actions set can be executed
250+ * @param executed True if the actions set has been executed, false otherwise
251+ * @param canceled True if the actions set has been canceled, false otherwise
252+ */
253+ struct ActionsSet {
254+ address [] targets;
255+ uint256 [] values;
256+ string [] signatures;
257+ bytes [] calldatas;
258+ bool [] withDelegatecalls;
259+ uint256 executionTime;
260+ bool executed;
261+ bool canceled;
262+ }
263+
264+ /**
265+ * @notice This enum contains all possible actions set states
266+ */
267+ enum ActionsSetState {
268+ Queued,
269+ Executed,
270+ Canceled,
271+ Expired
272+ }
273+
274+ // Time between queuing and execution
275+ uint256 private _delay;
276+ // Time after the execution time during which the actions set can be executed
277+ uint256 private _gracePeriod;
278+ // Minimum allowed delay
279+ uint256 private _minimumDelay;
280+ // Maximum allowed delay
281+ uint256 private _maximumDelay;
282+ // Address with the ability of canceling actions sets
283+ address private _guardian;
284+
285+ // Number of actions sets
286+ uint256 private _actionsSetCounter;
287+ // Map of registered actions sets (id => ActionsSet)
288+ mapping (uint256 => ActionsSet) private _actionsSets;
289+ // Map of queued actions (actionHash => isQueued)
290+ mapping (bytes32 => bool ) private _queuedActions;
291+
292+ function execute (uint256 actionsSetId ) external payable {
293+ if (getCurrentState (actionsSetId) != ActionsSetState.Queued) revert OnlyQueuedActions ();
294+
295+ ActionsSet storage actionsSet = _actionsSets[actionsSetId];
296+ actionsSet.executed = true ;
297+ uint256 actionCount = actionsSet.targets.length ;
298+
299+ bytes [] memory returnedData = new bytes [](actionCount);
300+ for (uint256 i = 0 ; i < actionCount; ) {
301+ returnedData[i] = _executeTransaction (
302+ actionsSet.targets[i],
303+ actionsSet.values[i],
304+ actionsSet.signatures[i],
305+ actionsSet.calldatas[i],
306+ actionsSet.executionTime,
307+ actionsSet.withDelegatecalls[i]
308+ );
309+ unchecked {
310+ ++ i;
311+ }
312+ }
313+
314+ emit ActionsSetExecuted (actionsSetId, msg .sender , returnedData);
315+ }
316+
317+ /**
318+ * @notice Non-standard functionality used to skip governance and just execute a payload.
319+ */
188320 function execute (address payload ) public {
189321 (bool success , ) = address (payload).delegatecall (abi.encodeWithSignature ('execute() ' ));
190322 require (success, 'PROPOSAL_EXECUTION_FAILED ' );
191323 }
324+
325+ /**
326+ * @notice Queue an ActionsSet
327+ * @dev If a signature is empty, calldata is used for the execution, calldata is appended to signature otherwise
328+ * @param targets Array of targets to be called by the actions set
329+ * @param values Array of values to pass in each call by the actions set
330+ * @param signatures Array of function signatures to encode in each call (can be empty)
331+ * @param calldatas Array of calldata to pass in each call (can be empty)
332+ * @param withDelegatecalls Array of whether to delegatecall for each call
333+ **/
334+ function queue (
335+ address [] memory targets ,
336+ uint256 [] memory values ,
337+ string [] memory signatures ,
338+ bytes [] memory calldatas ,
339+ bool [] memory withDelegatecalls
340+ ) public {
341+ if (targets.length == 0 ) revert EmptyTargets ();
342+ uint256 targetsLength = targets.length ;
343+ if (
344+ targetsLength != values.length ||
345+ targetsLength != signatures.length ||
346+ targetsLength != calldatas.length ||
347+ targetsLength != withDelegatecalls.length
348+ ) revert InconsistentParamsLength ();
349+
350+ uint256 actionsSetId = _actionsSetCounter;
351+ uint256 executionTime = block .timestamp + _delay;
352+ unchecked {
353+ ++ _actionsSetCounter;
354+ }
355+
356+ for (uint256 i = 0 ; i < targetsLength; ) {
357+ bytes32 actionHash = keccak256 (
358+ abi.encode (
359+ targets[i],
360+ values[i],
361+ signatures[i],
362+ calldatas[i],
363+ executionTime,
364+ withDelegatecalls[i]
365+ )
366+ );
367+ if (isActionQueued (actionHash)) revert DuplicateAction ();
368+ _queuedActions[actionHash] = true ;
369+ unchecked {
370+ ++ i;
371+ }
372+ }
373+
374+ ActionsSet storage actionsSet = _actionsSets[actionsSetId];
375+ actionsSet.targets = targets;
376+ actionsSet.values = values;
377+ actionsSet.signatures = signatures;
378+ actionsSet.calldatas = calldatas;
379+ actionsSet.withDelegatecalls = withDelegatecalls;
380+ actionsSet.executionTime = executionTime;
381+
382+ emit ActionsSetQueued (
383+ actionsSetId,
384+ targets,
385+ values,
386+ signatures,
387+ calldatas,
388+ withDelegatecalls,
389+ executionTime
390+ );
391+ }
392+
393+ function getCurrentState (uint256 actionsSetId ) public view returns (ActionsSetState) {
394+ if (_actionsSetCounter <= actionsSetId) revert InvalidActionsSetId ();
395+ ActionsSet storage actionsSet = _actionsSets[actionsSetId];
396+ if (actionsSet.canceled) {
397+ return ActionsSetState.Canceled;
398+ } else if (actionsSet.executed) {
399+ return ActionsSetState.Executed;
400+ } else if (block .timestamp > actionsSet.executionTime + _gracePeriod) {
401+ return ActionsSetState.Expired;
402+ } else {
403+ return ActionsSetState.Queued;
404+ }
405+ }
406+
407+ function isActionQueued (bytes32 actionHash ) public view returns (bool ) {
408+ return _queuedActions[actionHash];
409+ }
410+
411+ function _executeTransaction (
412+ address target ,
413+ uint256 value ,
414+ string memory signature ,
415+ bytes memory data ,
416+ uint256 executionTime ,
417+ bool withDelegatecall
418+ ) internal returns (bytes memory ) {
419+ if (address (this ).balance < value) revert InsufficientBalance ();
420+
421+ bytes32 actionHash = keccak256 (
422+ abi.encode (target, value, signature, data, executionTime, withDelegatecall)
423+ );
424+ _queuedActions[actionHash] = false ;
425+
426+ bytes memory callData;
427+ if (bytes (signature).length == 0 ) {
428+ callData = data;
429+ } else {
430+ callData = abi.encodePacked (bytes4 (keccak256 (bytes (signature))), data);
431+ }
432+
433+ bool success;
434+ bytes memory resultData;
435+ if (withDelegatecall) {
436+ (success, resultData) = this .executeDelegateCall {value: value}(target, callData);
437+ } else {
438+ // solium-disable-next-line security/no-call-value
439+ (success, resultData) = target.call {value: value}(callData);
440+ }
441+ return _verifyCallResult (success, resultData);
442+ }
443+
444+ function executeDelegateCall (address target , bytes calldata data )
445+ external
446+ payable
447+ returns (bool , bytes memory )
448+ {
449+ bool success;
450+ bytes memory resultData;
451+ // solium-disable-next-line security/no-call-value
452+ (success, resultData) = target.delegatecall (data);
453+ return (success, resultData);
454+ }
455+
456+ function _verifyCallResult (bool success , bytes memory returnData )
457+ private
458+ pure
459+ returns (bytes memory )
460+ {
461+ if (success) {
462+ return returnData;
463+ } else {
464+ // Look for revert reason and bubble it up if present
465+ if (returnData.length > 0 ) {
466+ // The easiest way to bubble the revert reason is using memory via assembly
467+
468+ // solhint-disable-next-line no-inline-assembly
469+ assembly {
470+ let returndata_size := mload (returnData)
471+ revert (add (32 , returnData), returndata_size)
472+ }
473+ } else {
474+ revert FailedActionExecution ();
475+ }
476+ }
477+ }
192478}
193479
194480/**
0 commit comments