-
-
Save andrecronje/7d1aa1ab5e7b76328ffde6accaefb646 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.7; | |
interface erc20 { | |
function transfer(address recipient, uint amount) external returns (bool); | |
function balanceOf(address) external view returns (uint); | |
function transferFrom(address sender, address recipient, uint amount) external returns (bool); | |
function approve(address spender, uint256 amount) external returns (bool); | |
} | |
interface sushi { | |
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); | |
function mint(address to) external returns (uint liquidity); | |
function burn(address to) external returns (uint amount0, uint amount1); | |
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; | |
function sync() external; | |
} | |
interface comptroller { | |
function enterMarkets(address[] memory cTokens) external; | |
function getAllMarkets() external view returns (address[] memory); | |
function exitMarket(address cToken) external; | |
} | |
interface cy { | |
function borrow(uint) external; | |
function mint(uint) external; | |
function redeem(uint) external; | |
function redeemUnderlying(uint) external; | |
function repayBorrow(uint) external; | |
function underlying() external view returns (address); | |
} | |
contract xVault { | |
address owner; | |
comptroller constant COMPTROLLER = comptroller(0x3d5BC3c8d13dcB8bF317092d84783c2697AE9258); | |
address constant FACTORY = address(0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac); | |
address constant WETH = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); | |
// calculates the CREATE2 address for a pair without making any external calls | |
function pairFor(address tokenA, address tokenB) internal pure returns (address pair) { | |
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); | |
pair = address(uint160(uint(keccak256(abi.encodePacked( | |
hex'ff', | |
FACTORY, | |
keccak256(abi.encodePacked(token0, token1)), | |
hex'e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303' // init code hash | |
))))); | |
} | |
function enterMarkets(address[] memory markets) public { | |
COMPTROLLER.enterMarkets(markets); | |
} | |
constructor() { | |
owner = msg.sender; | |
} | |
function withdraw(address token, uint amount) external { | |
require(owner == msg.sender); | |
_safeTransfer(token, owner, amount); | |
} | |
function open(address cylong, address long, uint lamt, address cyshort, address short, uint samt, address cymargin, uint mamt) external { | |
require(owner == msg.sender); | |
address[] memory _markets = new address[](2); | |
_markets[0] = cylong; | |
_markets[1] = cymargin; | |
enterMarkets(_markets); | |
_safeTransferFrom(cymargin, owner, address(this), mamt); | |
_borrow(cylong, long, lamt, cyshort, short, samt); | |
} | |
function close(address cyrepay, address repay, uint ramt, address cywithdraw, address uwithdraw, uint wamt) external { | |
require(owner == msg.sender); | |
address tokenB = repay == WETH ? uwithdraw : WETH; | |
sushi _pairFrom = sushi(pairFor(repay, tokenB)); | |
(uint amount0, uint amount1) = repay < tokenB ? (ramt, uint(0)) : (uint(0), ramt); | |
_pairFrom.swap(amount0, amount1, address(this), abi.encode(cyrepay, repay, ramt, address(_pairFrom), cywithdraw, uwithdraw, wamt, false)); | |
} | |
function _borrow(address cylong, address long, uint lamt, address cyshort, address short, uint samt) internal { | |
(uint amount0, uint amount1) = long < WETH ? (lamt, uint(0)) : (uint(0), lamt); | |
address tokenB = long == WETH ? short : WETH; | |
sushi _pairFrom = sushi(pairFor(long, tokenB)); | |
_pairFrom.swap(amount0, amount1, address(this), abi.encode(cylong, long, lamt, address(_pairFrom), cyshort, short, samt, true)); | |
} | |
function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external { | |
require(sender == address(this)); | |
(address _cylong, address _long, uint _lamt, address _pairFrom, address _cyshort, address _short, uint _samt, bool _pos) = abi.decode(data, (address, address, uint, address, address, address, uint, bool)); | |
{ | |
address tokenB = _long == WETH ? _short : WETH; | |
require(msg.sender == pairFor(_long, tokenB)); | |
} | |
if (_pos) { | |
_open(_cylong, _lamt, _pairFrom, amount0, _short, _long, _samt, _cyshort); | |
} else { | |
_close(_cylong, _lamt, _pairFrom, amount0, _short, _long, _samt, _cyshort); | |
} | |
} | |
function _close(address _cyrepay, uint _ramt, address _pairFrom, uint _amount0, address _withdraw, address _repay, uint _wamt, address _cywithdraw) internal { | |
_safeApprove(_repay, _cyrepay, _ramt); | |
cy(_cyrepay).repayBorrow(_ramt); | |
(uint reserve0, uint reserve1,) = sushi(_pairFrom).getReserves(); | |
(uint reserveIn, uint reserveOut) = _amount0 > 0 ? (reserve1, reserve0) : (reserve0, reserve1); | |
uint _minRepay = _getAmountIn(_ramt, reserveIn, reserveOut); | |
if (_withdraw == WETH || _repay == WETH) { | |
require(_minRepay <= _wamt); | |
cy(_cywithdraw).redeemUnderlying(_minRepay); | |
_safeTransfer(_withdraw, address(_pairFrom), _minRepay); | |
} else { | |
_crossClose(_withdraw, _minRepay, _wamt, _cywithdraw, address(_pairFrom)); | |
} | |
} | |
function _open(address _cylong, uint _lamt, address _pairFrom, uint _amount0, address _short, address _long, uint _samt, address _cyshort) internal { | |
_safeApprove(_long, _cylong, _lamt); | |
cy(_cylong).mint(_lamt); | |
(uint reserve0, uint reserve1,) = sushi(_pairFrom).getReserves(); | |
(uint reserveIn, uint reserveOut) = _amount0 > 0 ? (reserve1, reserve0) : (reserve0, reserve1); | |
uint _minRepay = _getAmountIn(_lamt, reserveIn, reserveOut); | |
if (_short == WETH || _long == WETH) { | |
require(_minRepay <= _samt); | |
cy(_cyshort).borrow(_minRepay); | |
_safeTransfer(_short, address(_pairFrom), _minRepay); | |
} else { | |
_cross(_short, _minRepay, _samt, _cyshort, address(_pairFrom)); | |
} | |
} | |
function _getShortFall(address _short, sushi _pairTo, uint _minWETHRepay) internal view returns (address, uint) { | |
(address token0,) = _short < WETH ? (_short, WETH) : (WETH, _short); | |
(uint reserve0, uint reserve1,) = _pairTo.getReserves(); | |
(uint reserveIn, uint reserveOut) = token0 == _short ? (reserve0, reserve1) : (reserve1, reserve0); | |
return (token0, _getAmountIn(_minWETHRepay, reserveIn, reserveOut)); | |
} | |
function _cross(address _short, uint _minWETHRepay, uint _samt, address _cyshort, address _pairFrom) internal { | |
sushi _pairTo = sushi(pairFor(_short, WETH)); | |
(address token0, uint _shortPay) = _getShortFall(_short, _pairTo, _minWETHRepay); | |
require(_shortPay <= _samt); | |
cy(_cyshort).borrow(_shortPay); | |
(uint amount0, uint amount1) = token0 == _short ? (uint(0), _minWETHRepay) : (_minWETHRepay, uint(0)); | |
_safeTransfer(_short, address(_pairTo), _shortPay); | |
_pairTo.swap(amount0, amount1, _pairFrom, new bytes(0)); | |
} | |
function _crossClose(address _withdraw, uint _minWETHRepay, uint _wamt, address _cywithdraw, address _pairFrom) internal { | |
sushi _pairTo = sushi(pairFor(_withdraw, WETH)); | |
(address token0, uint _shortPay) = _getShortFall(_withdraw, _pairTo, _minWETHRepay); | |
require(_shortPay <= _wamt); | |
cy(_cywithdraw).redeemUnderlying(_shortPay); | |
(uint amount0, uint amount1) = token0 == _withdraw ? (uint(0), _minWETHRepay) : (_minWETHRepay, uint(0)); | |
_safeTransfer(_withdraw, address(_pairTo), _shortPay); | |
_pairTo.swap(amount0, amount1, _pairFrom, new bytes(0)); | |
} | |
function _getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) { | |
uint numerator = reserveIn * amountOut * 1000; | |
uint denominator = (reserveOut - amountOut) * 997; | |
amountIn = (numerator / denominator) + 1; | |
} | |
function _safeTransfer(address token, address to, uint256 value) internal { | |
(bool success, bytes memory data) = | |
token.call(abi.encodeWithSelector(erc20.transfer.selector, to, value)); | |
require(success && (data.length == 0 || abi.decode(data, (bool)))); | |
} | |
function _safeTransferFrom(address token, address from, address to, uint256 value) internal { | |
(bool success, bytes memory data) = | |
token.call(abi.encodeWithSelector(erc20.transferFrom.selector, from, to, value)); | |
require(success && (data.length == 0 || abi.decode(data, (bool)))); | |
} | |
function _safeApprove(address token, address spender, uint256 value) internal { | |
(bool success, bytes memory data) = | |
token.call(abi.encodeWithSelector(erc20.approve.selector, spender, value)); | |
require(success && (data.length == 0 || abi.decode(data, (bool)))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment