文件 1 的 1:OptimizerZap.sol
pragma solidity 0.7.6;
pragma abicoder v2;
interface IPopsicleV3Optimizer {
function token0() external view returns (address);
function token1() external view returns (address);
function deposit(
uint256 amount0Desired,
uint256 amount1Desired,
address to
)
external
returns (
uint256 shares,
uint256 amount0,
uint256 amount1
);
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external ;
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external;
function transferFrom(address sender, address recipient, uint256 amount) external;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
interface IChi is IERC20 {
function mint(uint256 value) external;
function free(uint256 value) external returns (uint256 freed);
function freeFromUpTo(address from, uint256 value) external returns (uint256 freed);
}
interface IGasDiscountExtension {
function calculateGas(uint256 gasUsed, uint256 flags, uint256 calldataLength) external view returns (IChi, uint256);
}
interface IAggregationExecutor is IGasDiscountExtension {
function callBytes(bytes calldata data) external payable;
}
library TransferHelper {
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'STE');
}
}
interface IWETH9 is IERC20 {
function deposit() external payable;
}
interface IRouter {
struct SwapDescription {
IERC20 srcToken;
IERC20 dstToken;
address srcReceiver;
address dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
bytes permit;
}
function swap(IAggregationExecutor caller, SwapDescription calldata desc, bytes calldata data) external payable returns (uint256 returnAmount, uint256 gasLeft);
function unoswap(IERC20 srcToken, uint256 amount, uint256 minReturn, bytes32[] calldata ) external payable returns(uint256 returnAmount);
}
contract OptimizerZap {
IRouter constant router = IRouter(0x11111112542D85B3EF69AE05771c2dCCff4fAa26);
address constant eth = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address public immutable DAO;
struct Cache {
uint256 amount0;
uint256 amount1;
uint256 return0Amount;
uint256 return1Amount;
}
struct TokenData {
bool IsUno;
IAggregationExecutor caller;
IRouter.SwapDescription desc;
bytes data;
bytes32[] pools;
}
constructor(address _DAO) {
DAO = _DAO;
}
function DepositInEth(address optimizer, address to, TokenData calldata tokenData) external payable {
uint value = msg.value;
address token0 = IPopsicleV3Optimizer(optimizer).token0();
address token1 = IPopsicleV3Optimizer(optimizer).token1();
IWETH9(weth).deposit{value: value}();
_approveToken(weth, optimizer, value);
require(token0 == weth || token1 == weth, "BO");
if (token0 == weth) {
require(token1 == address(tokenData.desc.srcToken), "TNA");
TransferHelper.safeTransferFrom(token1, msg.sender, address(this), tokenData.desc.amount);
_approveToken(token1, optimizer, tokenData.desc.amount);
(, uint256 amount0,uint256 amount1) = IPopsicleV3Optimizer(optimizer).deposit(value, tokenData.desc.amount, to);
uint256 res0 = value-amount0;
uint256 res1 = tokenData.desc.amount-amount1;
if (res0 > 0 ) TransferHelper.safeTransfer(token0, to, res0);
if (res1 > 0 ) TransferHelper.safeTransfer(token1, to, res1);
} else {
require(token0 == address(tokenData.desc.srcToken), "TNA");
TransferHelper.safeTransferFrom(token0, msg.sender, address(this), tokenData.desc.amount);
_approveToken(token0, optimizer, tokenData.desc.amount);
(, uint256 amount0,uint256 amount1) =IPopsicleV3Optimizer(optimizer).deposit(tokenData.desc.amount, value, to);
uint256 res0 = tokenData.desc.amount-amount0;
uint256 res1 = value-amount1;
if (res0 > 0 ) TransferHelper.safeTransfer(token0, to, res0);
if (res1 > 0 ) TransferHelper.safeTransfer(token1, to, res1);
}
}
function ZapIn(address tokenIn, uint amount, address optimizer, address to, TokenData calldata token0Data, TokenData calldata token1Data) external payable {
require(optimizer != address(0));
require(to != address(0));
address token0 = IPopsicleV3Optimizer(optimizer).token0();
address token1 = IPopsicleV3Optimizer(optimizer).token1();
require(tokenIn == address(token0Data.desc.srcToken), "NAT0");
require(tokenIn == address(token1Data.desc.srcToken), "NAT1");
require(token0 == address(token0Data.desc.dstToken), "IT0");
require(token1 == address(token1Data.desc.dstToken), "IT1");
require(token0Data.desc.amount + token1Data.desc.amount <= amount, "IA");
Cache memory cache;
if (tokenIn == eth || tokenIn == address(0)) {
require(amount <= msg.value, "BA");
if (token0 == weth) {
IWETH9(weth).deposit{value: token0Data.desc.amount}();
_approveToken(weth, optimizer, token0Data.desc.amount);
if (token1Data.IsUno)
{
cache.return1Amount = router.unoswap{value: token1Data.desc.amount}(IERC20(tokenIn), token1Data.desc.amount, token1Data.desc.minReturnAmount, token1Data.pools);
} else {
(cache.return1Amount, ) = router.swap{value: token1Data.desc.amount}(token1Data.caller, token1Data.desc, token1Data.data);
}
_approveToken(token1, optimizer, cache.return1Amount);
(, cache.amount0, cache.amount1) = IPopsicleV3Optimizer(optimizer).deposit(token0Data.desc.amount, cache.return1Amount, to);
cache.return0Amount = token0Data.desc.amount-cache.amount0;
cache.return1Amount = cache.return1Amount-cache.amount1;
if (cache.return0Amount > 0 ) TransferHelper.safeTransfer(token0, to, cache.return0Amount);
if (cache.return1Amount > 0 ) TransferHelper.safeTransfer(token1, to, cache.return1Amount);
} else if (token1 == weth) {
IWETH9(weth).deposit{value: token1Data.desc.amount}();
_approveToken(weth, optimizer, token1Data.desc.amount);
if (token0Data.IsUno)
{
cache.return0Amount = router.unoswap{value: token0Data.desc.amount}(IERC20(tokenIn), token0Data.desc.amount, token0Data.desc.minReturnAmount, token0Data.pools);
} else {
(cache.return0Amount, ) = router.swap{value: token0Data.desc.amount}(token0Data.caller, token0Data.desc, token0Data.data);
}
_approveToken(token0, optimizer, cache.return0Amount);
(, cache.amount0, cache.amount1) = IPopsicleV3Optimizer(optimizer).deposit(cache.return0Amount, token1Data.desc.amount, to);
cache.return0Amount = cache.return0Amount-cache.amount0;
cache.return1Amount = token1Data.desc.amount-cache.amount1;
if (cache.return0Amount > 0 ) TransferHelper.safeTransfer(token0, to, cache.return0Amount);
if (cache.return1Amount > 0 ) TransferHelper.safeTransfer(token1, to, cache.return1Amount);
} else {
if (token0Data.IsUno)
{
cache.return0Amount = router.unoswap{value: token0Data.desc.amount}(IERC20(tokenIn), token0Data.desc.amount, token0Data.desc.minReturnAmount, token0Data.pools);
} else {
(cache.return0Amount, ) = router.swap{value: token0Data.desc.amount}(token0Data.caller, token0Data.desc, token0Data.data);
}
if (token1Data.IsUno)
{
cache.return1Amount = router.unoswap{value: token1Data.desc.amount}(IERC20(tokenIn), token1Data.desc.amount, token1Data.desc.minReturnAmount, token1Data.pools);
} else {
(cache.return1Amount, ) = router.swap{value: token1Data.desc.amount}(token1Data.caller, token1Data.desc, token1Data.data);
}
_approveToken(token0, optimizer, cache.return0Amount);
_approveToken(token1, optimizer, cache.return1Amount);
(, cache.amount0, cache.amount1) = IPopsicleV3Optimizer(optimizer).deposit(cache.return0Amount, cache.return1Amount, to);
cache.return0Amount = cache.return0Amount-cache.amount0;
cache.return1Amount = cache.return1Amount-cache.amount1;
if (cache.return0Amount > 0 ) TransferHelper.safeTransfer(token0, to, cache.return0Amount);
if (cache.return1Amount > 0 ) TransferHelper.safeTransfer(token1, to, cache.return1Amount);
}
return;
} else {
TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amount);
_approveToken(tokenIn, address(router), amount);
if (tokenIn == token0) {
cache.return0Amount = token0Data.desc.amount;
} else {
if (token0Data.IsUno)
{
cache.return0Amount = router.unoswap(IERC20(tokenIn), token0Data.desc.amount, token0Data.desc.minReturnAmount, token0Data.pools);
} else {
(cache.return0Amount, ) = router.swap(token0Data.caller, token0Data.desc, token0Data.data);
}
}
if (tokenIn == token1) {
cache.return1Amount = token1Data.desc.amount;
} else {
if (token1Data.IsUno)
{
cache.return1Amount = router.unoswap(IERC20(tokenIn), token1Data.desc.amount, token1Data.desc.minReturnAmount, token1Data.pools);
} else {
(cache.return1Amount, ) = router.swap(token1Data.caller, token1Data.desc, token1Data.data);
}
}
_approveToken(token0, optimizer, cache.return0Amount);
_approveToken(token1, optimizer, cache.return1Amount);
(, cache.amount0, cache.amount1) = IPopsicleV3Optimizer(optimizer).deposit(cache.return0Amount, cache.return1Amount, to);
cache.return0Amount = cache.return0Amount-cache.amount0;
cache.return1Amount = cache.return1Amount-cache.amount1;
if (cache.return0Amount > 0 ) TransferHelper.safeTransfer(token0, to, cache.return0Amount);
if (cache.return1Amount > 0 ) TransferHelper.safeTransfer(token1, to, cache.return1Amount);
return;
}
}
function _approveToken(
address token,
address spender,
uint256 amount
) internal {
if (IERC20(token).allowance(address(this), spender) > 0) IERC20(token).approve(spender, 0);
IERC20(token).approve(spender, amount);
}
function recoverLostToken( address _token ) external returns ( bool ) {
TransferHelper.safeTransfer(_token, DAO, IERC20( _token ).balanceOf( address(this)));
return true;
}
function refundETH() external returns ( bool ) {
if (address(this).balance > 0) TransferHelper.safeTransferETH(DAO, address(this).balance);
return true;
}
}