github.com/cwntr/go-defi@v0.0.0-20210629134751-07f9ec2f7e66/contracts/Proxy.sol (about)

     1  pragma solidity ^0.5.0;
     2  pragma experimental ABIEncoderV2;
     3  
     4  import "./Config.sol";
     5  import "./lib/libCache.sol";
     6  import "./Cache.sol";
     7  import "./interface/IRegistry.sol";
     8  import "@openzeppelin/contracts/utils/Address.sol";
     9  import "@openzeppelin/contracts/math/SafeMath.sol";
    10  import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
    11  import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
    12  
    13  /**
    14   * @title The entrance of Furucombo
    15   * @author Ben Huang
    16   */
    17  contract Proxy is Cache, Config {
    18      using Address for address;
    19      using SafeERC20 for IERC20;
    20  
    21      // keccak256 hash of "furucombo.handler.registry"
    22      bytes32 private constant HANDLER_REGISTRY = 0x6874162fd62902201ea0f4bf541086067b3b88bd802fac9e150fd2d1db584e19;
    23  
    24      constructor(address registry) public {
    25          bytes32 dummy_slot = HANDLER_REGISTRY;
    26          bytes32 slot = HANDLER_REGISTRY;
    27          assembly {
    28              sstore(slot, registry)
    29          }
    30      }
    31  
    32      function getSig(bytes memory data) private pure returns (bytes4) {
    33          bytes4 sig = 0x0;
    34          for (uint i = 0; i < 4; i++) {
    35              sig |= (bytes4(data[i]) >> (8 * i));
    36          }
    37          return (sig);
    38      }
    39  
    40  
    41  
    42      /**
    43       * @notice Direct transfer from EOA should be reverted.
    44       * @dev Callback function will be handled here.
    45       */
    46      function() external payable {
    47          require(Address.isContract(msg.sender), "Not allowed from EOA");
    48  
    49          // If triggered by a function call, caller should be registered in registry.
    50          // The function call will then be forwarded to the location registered in
    51          // registry.
    52          if (msg.data.length != 0) {
    53              // bytes4 sig = abi.decode(getSlice(0, 4, msg.data), (bytes4));
    54              bytes4 sig = getSig(msg.data);
    55              if (_isValid(msg.sender)) {
    56                  address target = address(
    57                      bytes20(IRegistry(_getRegistry()).getInfo(msg.sender))
    58                  );
    59                  _exec(target, msg.data);
    60              } else if (sig == bytes4(keccak256("uniswapV2Call(address,uint256,uint256,bytes)"))) {
    61                  // require(false, "executed!!!!");
    62                  address target = address(
    63                      bytes20(IRegistry(_getRegistry()).getInfo(address(0x1111111111111111111111111111111111111111)))
    64                  );
    65                  _exec(target, msg.data);
    66              } else {
    67                  require(false, "Invalid caller");
    68              }
    69          }
    70      }
    71  
    72      /**
    73       * @notice Combo execution function. Including three phases: pre-process,
    74       * exection and post-process.
    75       * @param tos The handlers of combo.
    76       * @param datas The combo datas.
    77       */
    78      function batchExec(address[] memory tos, bytes[] memory datas)
    79          public
    80          payable
    81      {
    82          _preProcess();
    83          _execs(tos, datas);
    84          _postProcess();
    85      }
    86  
    87      /**
    88       * @notice The execution interface for callback function to be executed.
    89       * @dev This function can only be called through the handler, which makes
    90       * the caller become proxy itself.
    91       */
    92      function execs(address[] memory tos, bytes[] memory datas) public payable {
    93          require(msg.sender == address(this), "Does not allow external calls");
    94          _execs(tos, datas);
    95      }
    96  
    97      /**
    98       * @notice The execution phase.
    99       * @param tos The handlers of combo.
   100       * @param datas The combo datas.
   101       */
   102      function _execs(address[] memory tos, bytes[] memory datas) internal {
   103          require(
   104              tos.length == datas.length,
   105              "Tos and datas length inconsistent"
   106          );
   107          for (uint256 i = 0; i < tos.length; i++) {
   108              _exec(tos[i], datas[i]);
   109              // Setup the process to be triggered in the post-process phase
   110              _setPostProcess(tos[i]);
   111          }
   112      }
   113  
   114      /**
   115       * @notice The execution of a single cube.
   116       * @param _to The handler of cube.
   117       * @param _data The cube execution data.
   118       */
   119      function _exec(address _to, bytes memory _data)
   120          internal
   121          returns (bytes memory result)
   122      {
   123          require(_isValid(_to), "Invalid handler");
   124          assembly {
   125              let succeeded := delegatecall(
   126                  sub(gas, 5000),
   127                  _to,
   128                  add(_data, 0x20),
   129                  mload(_data),
   130                  0,
   131                  0
   132              )
   133              let size := returndatasize
   134  
   135              result := mload(0x40)
   136              mstore(
   137                  0x40,
   138                  add(result, and(add(add(size, 0x20), 0x1f), not(0x1f)))
   139              )
   140              mstore(result, size)
   141              returndatacopy(add(result, 0x20), 0, size)
   142  
   143              switch iszero(succeeded)
   144                  case 1 {
   145                      revert(add(result, 0x20), size)
   146                  }
   147          }
   148      }
   149  
   150      /**
   151       * @notice Setup the post-process.
   152       * @param _to The handler of post-process.
   153       */
   154      function _setPostProcess(address _to) internal {
   155          // If the cache is empty, just skip
   156          // If the top is a custom post-process, replace it with the handler
   157          // address.
   158          require(cache.length > 0, "cache empty");
   159          if (cache.length == 1) return;
   160          else if (cache.peek() == bytes32(bytes12(uint96(HandlerType.Custom)))) {
   161              cache.pop();
   162              // Check if the handler is already set.
   163              if (bytes4(cache.peek()) != 0x00000000) cache.setAddress(_to);
   164              cache.setHandlerType(uint256(HandlerType.Custom));
   165          }
   166      }
   167  
   168      /// @notice The pre-process phase.
   169      function _preProcess() internal isCacheEmpty {
   170          // Set the sender on the top of cache.
   171          cache.setSender(msg.sender);
   172      }
   173  
   174      /// @notice The post-process phase.
   175      function _postProcess() internal {
   176          // If the top of cache is HandlerType.Custom (which makes it being zero
   177          // address when `cache.getAddress()`), get the handler address and execute
   178          // the handler with it and the post-process function selector.
   179          // If not, use it as token address and send the token back to user.
   180          while (cache.length > 1) {
   181              address addr = cache.getAddress();
   182              if (addr == address(0)) {
   183                  addr = cache.getAddress();
   184                  _exec(addr, abi.encodeWithSelector(POSTPROCESS_SIG));
   185              } else {
   186                  uint256 amount = IERC20(addr).balanceOf(address(this));
   187                  if (amount > 0) IERC20(addr).safeTransfer(msg.sender, amount);
   188              }
   189          }
   190  
   191          // Balance should also be returned to user
   192          uint256 amount = address(this).balance;
   193          if (amount > 0) msg.sender.transfer(amount);
   194  
   195          // Pop the msg.sender
   196          cache.pop();
   197      }
   198  
   199      /// @notice Get the registry contract address.
   200      function _getRegistry() internal view returns (address registry) {
   201          bytes32 slot = HANDLER_REGISTRY;
   202          assembly {
   203              registry := sload(slot)
   204          }
   205      }
   206      
   207      /// @notice Check if the handler is valid in registry.
   208      function _isValid(address handler) internal view returns (bool result) {
   209          return IRegistry(_getRegistry()).isValid(handler);
   210      }
   211  }