@@ -12,6 +12,7 @@ import {AaveV3EthereumHorizonCustom} from 'src/utils/AaveV3EthereumHorizonCustom
1212import {HorizonRwaWhitelistHelper} from 'tests/utils/HorizonRwaWhitelistHelper.sol ' ;
1313import {HorizonConfigAssertionHelper} from 'tests/utils/HorizonConfigAssertionHelper.sol ' ;
1414import {Errors} from 'src/dependencies/Errors.sol ' ;
15+ import {ISafeAccount} from 'src/dependencies/ISafeAccount.sol ' ;
1516
1617/**
1718 * @dev Adapted from ProtocolV3TestBase for the Horizon market (currently at Aave v3.3).
@@ -45,19 +46,22 @@ abstract contract ProtocolV3HorizonTestBase is
4546 * - diffing the config
4647 * - checking if the changes are plausible (no conflicting config changes etc)
4748 * - running an e2e testsuite over all assets
49+ * @param reportName the name of the report
50+ * @param pool the pool that should be tested
51+ * @param executeTx the function that will execute the transaction that is being tested
4852 */
4953 function defaultTest_v3_3 (
5054 string memory reportName ,
5155 IPool pool ,
52- address payload
53- ) public returns (ReserveConfig[] memory , ReserveConfig[] memory ) {
56+ function () internal executeTx
57+ ) internal returns (ReserveConfig[] memory , ReserveConfig[] memory ) {
5458 string memory beforeString = string (abi.encodePacked (reportName, '_before ' ));
5559 ReserveConfig[] memory configBefore = createConfigurationSnapshot (beforeString, pool);
5660
5761 uint256 startGas = gasleft ();
5862
5963 vm.startStateDiffRecording ();
60- _executeHorizonPayload (payload);
64+ executeTx (); // execute the transaction that is being tested
6165 string memory rawDiff = vm.getStateDiffJson ();
6266
6367 uint256 gasUsed = startGas - gasleft ();
@@ -278,6 +282,10 @@ abstract contract ProtocolV3HorizonTestBase is
278282 }
279283 }
280284
285+ function _pool () internal pure returns (IPool) {
286+ return IPool (AaveV3EthereumHorizonCustom.POOL);
287+ }
288+
281289 /**
282290 * @dev Execute a Horizon payload through the real executor path.
283291 * HORIZON_EMERGENCY calls HORIZON_EXECUTOR.executeTransaction() which delegatecalls
@@ -490,4 +498,96 @@ abstract contract ProtocolV3HorizonTestBase is
490498 }
491499
492500 function _setExpectedConfig () internal virtual {}
501+
502+ // ---------------------------------------------------------------------------
503+ // Gnosis Safe multisig transaction helpers
504+ // ---------------------------------------------------------------------------
505+
506+ /**
507+ * @dev Execute a transaction as the HORIZON_EMERGENCY multisig.
508+ * @param to The `to` address of the Safe transaction.
509+ * @param data The calldata of the Safe transaction.
510+ * @param operation 0 = call, 1 = delegatecall.
511+ */
512+ function _executeEmergencyMultisigTx (
513+ address to ,
514+ bytes memory data ,
515+ uint8 operation ,
516+ uint256 nonce
517+ ) internal {
518+ _executeSafeTx ({
519+ safe: AaveV3EthereumHorizonCustom.HORIZON_EMERGENCY,
520+ to: to,
521+ data: data,
522+ operation: operation,
523+ nonce: nonce
524+ });
525+ }
526+
527+ /**
528+ * @dev Execute a transaction as the HORIZON_OPS multisig.
529+ * @param to The `to` address of the Safe transaction.
530+ * @param data The calldata of the Safe transaction.
531+ * @param operation 0 = call, 1 = delegatecall.
532+ * @param nonce The Safe nonce to set before execution.
533+ */
534+ function _executeOpsMultisigTx (
535+ address to ,
536+ bytes memory data ,
537+ uint8 operation ,
538+ uint256 nonce
539+ ) internal {
540+ _executeSafeTx ({
541+ safe: AaveV3EthereumHorizonCustom.HORIZON_OPS,
542+ to: to,
543+ data: data,
544+ operation: operation,
545+ nonce: nonce
546+ });
547+ }
548+
549+ /**
550+ * @dev Core Safe execution: sets threshold to 1 and nonce, picks the first owner,
551+ * and calls execTransaction with a minimal pre-approved signature.
552+ */
553+ function _executeSafeTx (
554+ address safe ,
555+ address to ,
556+ bytes memory data ,
557+ uint8 operation ,
558+ uint256 nonce
559+ ) internal {
560+ // reset signer threshold to 1 (slot 4) and set nonce (slot 5)
561+ vm.store (safe, bytes32 (uint256 (4 )), bytes32 (uint256 (1 )));
562+ vm.store (safe, bytes32 (uint256 (5 )), bytes32 (nonce));
563+ address signer = ISafeAccount (safe).getOwners ()[0 ];
564+
565+ vm.prank (signer);
566+ (bool ok , bytes memory ret ) = safe.call (
567+ abi.encodeCall (
568+ ISafeAccount.execTransaction,
569+ (
570+ to, // to
571+ 0 , // value
572+ data,
573+ operation,
574+ 0 , // safeTxGas
575+ 0 , // baseGas
576+ 0 , // gasPrice
577+ address (0 ), // gasToken
578+ payable (address (0 )), // refundReceiver
579+ abi.encodePacked (bytes32 (uint256 (uint160 (signer))), bytes32 (0 ), uint8 (1 )) // r, s, v
580+ )
581+ )
582+ );
583+
584+ if (! ok) {
585+ if (ret.length > 0 ) {
586+ assembly {
587+ revert (add (ret, 32 ), mload (ret))
588+ }
589+ }
590+ revert ('_executeSafeTx: unknown error ' );
591+ }
592+ }
493593}
0 commit comments