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 }