github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/safe-tools/SafeTestTools.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity >=0.7.0 <0.9.0;
     3  
     4  import "forge-std/Test.sol";
     5  import { LibSort } from "solady/utils/LibSort.sol";
     6  import { Safe as GnosisSafe, OwnerManager, ModuleManager, GuardManager } from "safe-contracts/Safe.sol";
     7  import { SafeProxyFactory as GnosisSafeProxyFactory } from "safe-contracts/proxies/SafeProxyFactory.sol";
     8  import { Enum } from "safe-contracts/common/Enum.sol";
     9  import { SignMessageLib } from "safe-contracts/libraries/SignMessageLib.sol";
    10  import "./CompatibilityFallbackHandler_1_3_0.sol";
    11  
    12  // Tools to simplify testing Safe contracts
    13  // Author: Colin Nielsen (https://github.com/colinnielsen/safe-tools)
    14  // With expanded and improved functionality by OP Labs
    15  
    16  /// @dev A minimal wrapper around the OwnerManager contract. This contract is meant to be initialized with
    17  ///      the same owners as a Safe instance, and then used to simulate the resulting owners list
    18  ///      after an owner is removed.
    19  contract OwnerSimulator is OwnerManager {
    20      constructor(address[] memory _owners, uint256 _threshold) {
    21          setupOwners(_owners, _threshold);
    22      }
    23  
    24      /// @dev Exposes the OwnerManager's removeOwner function so that anyone may call without needing auth
    25      function removeOwnerWrapped(address prevOwner, address owner, uint256 _threshold) public {
    26          OwnerManager(address(this)).removeOwner(prevOwner, owner, _threshold);
    27      }
    28  }
    29  
    30  /// @dev collapsed interface that includes comapatibilityfallback handler calls
    31  abstract contract DeployedSafe is GnosisSafe, CompatibilityFallbackHandler { }
    32  
    33  struct AdvancedSafeInitParams {
    34      bool includeFallbackHandler;
    35      uint256 saltNonce;
    36      address setupModulesCall_to;
    37      bytes setupModulesCall_data;
    38      uint256 refundAmount;
    39      address refundToken;
    40      address payable refundReceiver;
    41      bytes initData;
    42  }
    43  
    44  struct SafeInstance {
    45      uint256 instanceId;
    46      uint256[] ownerPKs;
    47      address[] owners;
    48      uint256 threshold;
    49      DeployedSafe safe;
    50  }
    51  
    52  library Sort {
    53      /// @dev Sorts an array of addresses in place
    54      function sort(address[] memory arr) public pure returns (address[] memory) {
    55          LibSort.sort(arr);
    56          return arr;
    57      }
    58  }
    59  
    60  library SafeTestLib {
    61      /// @dev The address of foundry's VM contract
    62      address constant VM_ADDR = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D;
    63      /// @dev The address of the first owner in the linked list of owners
    64      address constant SENTINEL_OWNERS = address(0x1);
    65  
    66      /// @dev Get the address from a private key
    67      function getAddr(uint256 pk) internal pure returns (address) {
    68          return Vm(VM_ADDR).addr(pk);
    69      }
    70  
    71      /// @dev Get arrays of addresses and private keys. The arrays are sorted by address, and the addresses are labelled
    72      function makeAddrsAndKeys(
    73          string memory prefix,
    74          uint256 num
    75      )
    76          internal
    77          returns (address[] memory addrs, uint256[] memory keys)
    78      {
    79          keys = new uint256[](num);
    80          addrs = new address[](num);
    81          for (uint256 i; i < num; i++) {
    82              uint256 key = uint256(keccak256(abi.encodePacked(i)));
    83              keys[i] = key;
    84          }
    85  
    86          for (uint256 i; i < num; i++) {
    87              addrs[i] = Vm(VM_ADDR).addr(keys[i]);
    88              Vm(VM_ADDR).label(getAddr(keys[i]), string.concat(prefix, Vm(VM_ADDR).toString(i)));
    89          }
    90      }
    91  
    92      bytes12 constant ADDR_MASK = 0xffffffffffffffffffffffff;
    93  
    94      /// @dev Encode a smart contract wallet as a private key
    95      function encodeSmartContractWalletAsPK(address addr) internal pure returns (uint256 encodedPK) {
    96          assembly {
    97              let addr_b32 := addr
    98              encodedPK := or(addr, ADDR_MASK)
    99          }
   100      }
   101  
   102      /// @dev Decode a smart contract wallet as an address from a private key
   103      function decodeSmartContractWalletAsAddress(uint256 pk) internal pure returns (address decodedAddr) {
   104          assembly {
   105              let addr := shl(96, pk)
   106              decodedAddr := shr(96, addr)
   107          }
   108      }
   109  
   110      /// @dev Checks if a private key is an encoded smart contract address
   111      function isSmartContractPK(uint256 pk) internal pure returns (bool isEncoded) {
   112          assembly {
   113              isEncoded := eq(shr(160, pk), shr(160, ADDR_MASK))
   114          }
   115      }
   116  
   117      /// @dev Sorts an array of private keys by the computed address
   118      ///      If the private key is a smart contract wallet, it will be decoded and sorted by the address
   119      function sortPKsByComputedAddress(uint256[] memory _pks) internal pure returns (uint256[] memory) {
   120          uint256[] memory sortedPKs = new uint256[](_pks.length);
   121  
   122          address[] memory addresses = new address[](_pks.length);
   123          bytes32[2][] memory accounts = new bytes32[2][](_pks.length);
   124  
   125          for (uint256 i; i < _pks.length; i++) {
   126              uint256 pk = _pks[i];
   127              address signer = SafeTestLib.getAddr(pk);
   128              if (isSmartContractPK(pk)) {
   129                  signer = decodeSmartContractWalletAsAddress(pk);
   130              }
   131              addresses[i] = signer;
   132              accounts[i][0] = bytes32(abi.encode(signer));
   133              accounts[i][1] = bytes32(pk);
   134          }
   135  
   136          addresses = Sort.sort(addresses);
   137  
   138          uint256 found;
   139          for (uint256 j; j < addresses.length; j++) {
   140              address signer = addresses[j];
   141              uint256 pk;
   142              for (uint256 k; k < accounts.length; k++) {
   143                  if (address(uint160(uint256(accounts[k][0]))) == signer) {
   144                      pk = uint256(accounts[k][1]);
   145                      found++;
   146                  }
   147              }
   148  
   149              sortedPKs[j] = pk;
   150          }
   151  
   152          if (found < _pks.length) {
   153              revert("SAFETESTTOOLS: issue with private key sorting, please open a ticket on github");
   154          }
   155          return sortedPKs;
   156      }
   157  
   158      /// @dev Sign a transaction as a safe owner with a private key.
   159      function signTransaction(
   160          SafeInstance memory instance,
   161          uint256 pk,
   162          address to,
   163          uint256 value,
   164          bytes memory data,
   165          Enum.Operation operation,
   166          uint256 safeTxGas,
   167          uint256 baseGas,
   168          uint256 gasPrice,
   169          address gasToken,
   170          address refundReceiver
   171      )
   172          internal
   173          view
   174          returns (uint8 v, bytes32 r, bytes32 s)
   175      {
   176          bytes32 txDataHash;
   177          {
   178              uint256 _nonce = instance.safe.nonce();
   179              txDataHash = instance.safe.getTransactionHash({
   180                  to: to,
   181                  value: value,
   182                  data: data,
   183                  operation: operation,
   184                  safeTxGas: safeTxGas,
   185                  baseGas: baseGas,
   186                  gasPrice: gasPrice,
   187                  gasToken: gasToken,
   188                  refundReceiver: refundReceiver,
   189                  _nonce: _nonce
   190              });
   191          }
   192  
   193          (v, r, s) = Vm(VM_ADDR).sign(pk, txDataHash);
   194      }
   195  
   196      /// @dev Get the previous owner in the linked list of owners.
   197      ///      This version of getPrevOwner will call to the Safe contract to get the current list of owners.
   198      ///      Note that this will break vm.expectRevert() tests by making a call which does not revert..
   199      /// @param _owner The owner whose previous owner we want to find
   200      function getPrevOwner(SafeInstance memory instance, address _owner) internal view returns (address prevOwner_) {
   201          address[] memory owners = instance.safe.getOwners();
   202          prevOwner_ = getPrevOwnerFromList(_owner, owners);
   203      }
   204  
   205      /// @dev Get the previous owner in the provided list of owners.
   206      ///      This version of getPrevOwner accepts a list of owners, and will return the previous owner.
   207      ///      It is useful when testing for a revert, as it avoids the need to call to the Safe contract.
   208      /// @param _owner The owner whose previous owner we want to find
   209      /// @param _ownersList The list of owners to search in
   210      function getPrevOwnerFromList(
   211          address _owner,
   212          address[] memory _ownersList
   213      )
   214          internal
   215          pure
   216          returns (address prevOwner_)
   217      {
   218          for (uint256 i; i < _ownersList.length; i++) {
   219              if (_ownersList[i] != _owner) continue;
   220              if (i == 0) {
   221                  prevOwner_ = SENTINEL_OWNERS;
   222                  break;
   223              }
   224              prevOwner_ = _ownersList[i - 1];
   225          }
   226      }
   227  
   228      /// @dev Given an array of owners to remove, this function will return an array of the previous owners
   229      ///         in the order that they must be provided to the LivenessMoules's removeOwners() function.
   230      ///         Because owners are removed one at a time, and not necessarily in order, we need to simulate
   231      ///         the owners list after each removal, in order to identify the correct previous owner.
   232      /// @param _ownersToRemove The owners to remove
   233      /// @return prevOwners_ The previous owners in the linked list
   234      function getPrevOwners(
   235          SafeInstance memory instance,
   236          address[] memory _ownersToRemove
   237      )
   238          internal
   239          returns (address[] memory prevOwners_)
   240      {
   241          OwnerSimulator ownerSimulator = new OwnerSimulator(instance.owners, 1);
   242          prevOwners_ = new address[](_ownersToRemove.length);
   243          address[] memory currentOwners;
   244          for (uint256 i; i < _ownersToRemove.length; i++) {
   245              currentOwners = ownerSimulator.getOwners();
   246              prevOwners_[i] = SafeTestLib.getPrevOwnerFromList(_ownersToRemove[i], currentOwners);
   247  
   248              // Don't try to remove the last owner
   249              if (currentOwners.length == 1) break;
   250              ownerSimulator.removeOwnerWrapped(prevOwners_[i], _ownersToRemove[i], 1);
   251          }
   252      }
   253  
   254      /// @dev Enables a module on the Safe.
   255      function enableModule(SafeInstance memory instance, address module) internal {
   256          execTransaction(
   257              instance,
   258              address(instance.safe),
   259              0,
   260              abi.encodeWithSelector(ModuleManager.enableModule.selector, module),
   261              Enum.Operation.Call,
   262              0,
   263              0,
   264              0,
   265              address(0),
   266              address(0),
   267              ""
   268          );
   269      }
   270  
   271      /// @dev Disables a module on the Safe.
   272      function disableModule(SafeInstance memory instance, address module) internal {
   273          (address[] memory modules,) = instance.safe.getModulesPaginated(SENTINEL_MODULES, 1000);
   274          address prevModule = SENTINEL_MODULES;
   275          bool moduleFound;
   276          for (uint256 i; i < modules.length; i++) {
   277              if (modules[i] == module) {
   278                  moduleFound = true;
   279                  break;
   280              }
   281              prevModule = modules[i];
   282          }
   283          if (!moduleFound) revert("SAFETESTTOOLS: cannot disable module that is not enabled");
   284  
   285          execTransaction(
   286              instance,
   287              address(instance.safe),
   288              0,
   289              abi.encodeWithSelector(ModuleManager.disableModule.selector, prevModule, module),
   290              Enum.Operation.Call,
   291              0,
   292              0,
   293              0,
   294              address(0),
   295              address(0),
   296              ""
   297          );
   298      }
   299  
   300      /// @dev Sets the guard address on the Safe. Unlike modules there can only be one guard, so
   301      ///      this method will remove the previous guard. If the guard is set to the 0 address, the
   302      ///      guard will be disabled.
   303      function setGuard(SafeInstance memory instance, address guard) internal {
   304          execTransaction(
   305              instance,
   306              address(instance.safe),
   307              0,
   308              abi.encodeWithSelector(GuardManager.setGuard.selector, guard),
   309              Enum.Operation.Call,
   310              0,
   311              0,
   312              0,
   313              address(0),
   314              address(0),
   315              ""
   316          );
   317      }
   318  
   319      /// @dev Signs message data using EIP1271: Standard Signature Validation Method for Contracts
   320      function EIP1271Sign(SafeInstance memory instance, bytes memory data) internal {
   321          address signMessageLib = address(new SignMessageLib());
   322          execTransaction({
   323              instance: instance,
   324              to: signMessageLib,
   325              value: 0,
   326              data: abi.encodeWithSelector(SignMessageLib.signMessage.selector, data),
   327              operation: Enum.Operation.DelegateCall,
   328              safeTxGas: 0,
   329              baseGas: 0,
   330              gasPrice: 0,
   331              gasToken: address(0),
   332              refundReceiver: payable(address(0)),
   333              signatures: ""
   334          });
   335      }
   336  
   337      /// @dev Signs a data hash using EIP1271: Standard Signature Validation Method for Contracts
   338      function EIP1271Sign(SafeInstance memory instance, bytes32 digest) internal {
   339          EIP1271Sign(instance, abi.encodePacked(digest));
   340      }
   341  
   342      /// @dev Increments the nonce of the Safe by sending an empty transaction.
   343      function incrementNonce(SafeInstance memory instance) internal returns (uint256 newNonce) {
   344          execTransaction(instance, address(0), 0, "", Enum.Operation.Call, 0, 0, 0, address(0), address(0), "");
   345          return instance.safe.nonce();
   346      }
   347  
   348      /// @dev Adds a new owner to the safe
   349      function changeThreshold(SafeInstance memory instance, uint256 threshold) internal {
   350          execTransaction(
   351              instance,
   352              address(instance.safe),
   353              0,
   354              abi.encodeWithSelector(OwnerManager.changeThreshold.selector, threshold)
   355          );
   356      }
   357  
   358      /// @dev Adds a new owner to the safe
   359      function addOwnerWithThreshold(SafeInstance memory instance, address owner, uint256 threshold) internal {
   360          execTransaction(
   361              instance,
   362              address(instance.safe),
   363              0,
   364              abi.encodeWithSelector(OwnerManager.addOwnerWithThreshold.selector, owner, threshold)
   365          );
   366      }
   367  
   368      /// @dev Removes an owner from the safe. If not provided explictly, the identification of the prevOwner is handled
   369      ///     automatically.
   370      function removeOwner(SafeInstance memory instance, address prevOwner, address owner, uint256 threshold) internal {
   371          prevOwner = prevOwner > address(0) ? prevOwner : SafeTestLib.getPrevOwner(instance, owner);
   372          execTransaction(
   373              instance,
   374              address(instance.safe),
   375              0,
   376              abi.encodeWithSelector(OwnerManager.removeOwner.selector, prevOwner, owner, threshold)
   377          );
   378      }
   379  
   380      /// @dev Replaces an old owner with a new owner. If not provided explictly, the identification of the prevOwner is
   381      /// handled automatically.
   382      function swapOwner(SafeInstance memory instance, address prevOwner, address oldOwner, address newOwner) internal {
   383          prevOwner = prevOwner > address(0) ? prevOwner : SafeTestLib.getPrevOwner(instance, oldOwner);
   384          execTransaction(
   385              instance,
   386              address(instance.safe),
   387              0,
   388              abi.encodeWithSelector(OwnerManager.swapOwner.selector, prevOwner, oldOwner, newOwner)
   389          );
   390      }
   391  
   392      /// @dev A wrapper for the full execTransaction method, if no signatures are provided it will
   393      ///         generate them for all owners.
   394      function execTransaction(
   395          SafeInstance memory instance,
   396          address to,
   397          uint256 value,
   398          bytes memory data,
   399          Enum.Operation operation,
   400          uint256 safeTxGas,
   401          uint256 baseGas,
   402          uint256 gasPrice,
   403          address gasToken,
   404          address refundReceiver,
   405          bytes memory signatures
   406      )
   407          internal
   408          returns (bool)
   409      {
   410          if (instance.owners.length == 0) {
   411              revert("SAFETEST: Instance not initialized. Call _setupSafe() to initialize a test safe");
   412          }
   413  
   414          bytes32 safeTxHash;
   415          {
   416              uint256 _nonce = instance.safe.nonce();
   417              safeTxHash = instance.safe.getTransactionHash({
   418                  to: to,
   419                  value: value,
   420                  data: data,
   421                  operation: operation,
   422                  safeTxGas: safeTxGas,
   423                  baseGas: baseGas,
   424                  gasPrice: gasPrice,
   425                  gasToken: gasToken,
   426                  refundReceiver: refundReceiver,
   427                  _nonce: _nonce
   428              });
   429          }
   430  
   431          if (signatures.length == 0) {
   432              for (uint256 i; i < instance.ownerPKs.length; ++i) {
   433                  uint256 pk = instance.ownerPKs[i];
   434                  (uint8 v, bytes32 r, bytes32 s) = Vm(VM_ADDR).sign(pk, safeTxHash);
   435                  if (isSmartContractPK(pk)) {
   436                      v = 0;
   437                      address addr = decodeSmartContractWalletAsAddress(pk);
   438                      assembly {
   439                          r := addr
   440                      }
   441                      console.logBytes32(r);
   442                  }
   443                  signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
   444              }
   445          }
   446  
   447          return instance.safe.execTransaction({
   448              to: to,
   449              value: value,
   450              data: data,
   451              operation: operation,
   452              safeTxGas: safeTxGas,
   453              baseGas: baseGas,
   454              gasPrice: gasPrice,
   455              gasToken: gasToken,
   456              refundReceiver: payable(refundReceiver),
   457              signatures: signatures
   458          });
   459      }
   460  
   461      /// @dev Executes either a CALL or DELEGATECALL transaction.
   462      function execTransaction(
   463          SafeInstance memory instance,
   464          address to,
   465          uint256 value,
   466          bytes memory data,
   467          Enum.Operation operation
   468      )
   469          internal
   470          returns (bool)
   471      {
   472          return execTransaction(instance, to, value, data, operation, 0, 0, 0, address(0), address(0), "");
   473      }
   474  
   475      /// @dev Executes a CALL transaction.
   476      function execTransaction(
   477          SafeInstance memory instance,
   478          address to,
   479          uint256 value,
   480          bytes memory data
   481      )
   482          internal
   483          returns (bool)
   484      {
   485          return execTransaction(instance, to, value, data, Enum.Operation.Call, 0, 0, 0, address(0), address(0), "");
   486      }
   487  }
   488  
   489  /// @dev SafeTestTools implements a set of helper functions for testing Safe contracts.
   490  contract SafeTestTools {
   491      using SafeTestLib for SafeInstance;
   492  
   493      GnosisSafe internal singleton = new GnosisSafe();
   494      GnosisSafeProxyFactory internal proxyFactory = new GnosisSafeProxyFactory();
   495      CompatibilityFallbackHandler internal handler = new CompatibilityFallbackHandler();
   496  
   497      SafeInstance[] internal instances;
   498  
   499      /// @dev can be called to reinitialize the singleton, proxyFactory and handler. Useful for forking.
   500      function _initializeSafeTools() internal {
   501          singleton = new GnosisSafe();
   502          proxyFactory = new GnosisSafeProxyFactory();
   503          handler = new CompatibilityFallbackHandler();
   504      }
   505  
   506      function _setupSafe(
   507          uint256[] memory ownerPKs,
   508          uint256 threshold,
   509          uint256 initialBalance,
   510          AdvancedSafeInitParams memory advancedParams
   511      )
   512          public
   513          returns (SafeInstance memory)
   514      {
   515          uint256[] memory sortedPKs = SafeTestLib.sortPKsByComputedAddress(ownerPKs);
   516          address[] memory owners = new address[](sortedPKs.length);
   517  
   518          for (uint256 i; i < sortedPKs.length; i++) {
   519              if (SafeTestLib.isSmartContractPK(sortedPKs[i])) {
   520                  owners[i] = SafeTestLib.decodeSmartContractWalletAsAddress(sortedPKs[i]);
   521              } else {
   522                  owners[i] = SafeTestLib.getAddr(sortedPKs[i]);
   523              }
   524          }
   525          // store the initialization parameters
   526  
   527          bytes memory initData = advancedParams.initData.length > 0
   528              ? advancedParams.initData
   529              : abi.encodeWithSelector(
   530                  GnosisSafe.setup.selector,
   531                  owners,
   532                  threshold,
   533                  advancedParams.setupModulesCall_to,
   534                  advancedParams.setupModulesCall_data,
   535                  advancedParams.includeFallbackHandler ? address(handler) : address(0),
   536                  advancedParams.refundToken,
   537                  advancedParams.refundAmount,
   538                  advancedParams.refundReceiver
   539              );
   540  
   541          DeployedSafe safe0 = DeployedSafe(
   542              payable(proxyFactory.createProxyWithNonce(address(singleton), initData, advancedParams.saltNonce))
   543          );
   544  
   545          SafeInstance memory instance0 = SafeInstance({
   546              instanceId: instances.length,
   547              ownerPKs: sortedPKs,
   548              owners: owners,
   549              threshold: threshold,
   550              // setup safe ecosystem, singleton, proxy factory, fallback handler, and create a new safe
   551              safe: safe0
   552          });
   553          instances.push(instance0);
   554  
   555          Vm(SafeTestLib.VM_ADDR).deal(address(safe0), initialBalance);
   556  
   557          return instance0;
   558      }
   559  
   560      function _setupSafe(
   561          uint256[] memory ownerPKs,
   562          uint256 threshold,
   563          uint256 initialBalance
   564      )
   565          public
   566          returns (SafeInstance memory)
   567      {
   568          return _setupSafe(
   569              ownerPKs,
   570              threshold,
   571              initialBalance,
   572              AdvancedSafeInitParams({
   573                  includeFallbackHandler: true,
   574                  initData: "",
   575                  saltNonce: 0,
   576                  setupModulesCall_to: address(0),
   577                  setupModulesCall_data: "",
   578                  refundAmount: 0,
   579                  refundToken: address(0),
   580                  refundReceiver: payable(address(0))
   581              })
   582          );
   583      }
   584  
   585      function _setupSafe(uint256[] memory ownerPKs, uint256 threshold) public returns (SafeInstance memory) {
   586          return _setupSafe(
   587              ownerPKs,
   588              threshold,
   589              10000 ether,
   590              AdvancedSafeInitParams({
   591                  includeFallbackHandler: true,
   592                  initData: "",
   593                  saltNonce: 0,
   594                  setupModulesCall_to: address(0),
   595                  setupModulesCall_data: "",
   596                  refundAmount: 0,
   597                  refundToken: address(0),
   598                  refundReceiver: payable(address(0))
   599              })
   600          );
   601      }
   602  
   603      function _setupSafe() public returns (SafeInstance memory) {
   604          (, uint256[] memory defaultPKs) = SafeTestLib.makeAddrsAndKeys("default", 3);
   605  
   606          return _setupSafe(
   607              defaultPKs,
   608              2,
   609              10000 ether,
   610              AdvancedSafeInitParams({
   611                  includeFallbackHandler: true,
   612                  initData: "",
   613                  saltNonce: uint256(keccak256(bytes("SAFE TEST"))),
   614                  setupModulesCall_to: address(0),
   615                  setupModulesCall_data: "",
   616                  refundAmount: 0,
   617                  refundToken: address(0),
   618                  refundReceiver: payable(address(0))
   619              })
   620          );
   621      }
   622  
   623      function getSafe() public view returns (SafeInstance memory) {
   624          if (instances.length == 0) {
   625              revert("SAFETESTTOOLS: Test Safe has not been deployed, use _setupSafe() calling safe()");
   626          }
   627          return instances[0];
   628      }
   629  
   630      function getSafe(address _safe) public view returns (SafeInstance memory) {
   631          for (uint256 i; i < instances.length; ++i) {
   632              if (address(instances[i].safe) == _safe) return instances[i];
   633          }
   634          revert("SAFETESTTOOLS: Safe instance not found");
   635      }
   636  }