diff --git a/contracts/protocols/mainnet/GelatoUniV3pool.sol b/contracts/protocols/mainnet/GelatoUniV3pool.sol new file mode 100644 index 0000000..0b30823 --- /dev/null +++ b/contracts/protocols/mainnet/GelatoUniV3pool.sol @@ -0,0 +1,244 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + + +interface IERC20 { + function balanceOf(address) external view returns (uint256); + function totalSupply() external view returns (uint256); + function decimals() external view returns (uint256); +} + +interface IUniswapV3Pool { + + function slot0() + external + view + returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ); + +} + +interface IGUniPool { + function token0() external view returns (IERC20); + + function token1() external view returns (IERC20); + + function pool() external view returns (IUniswapV3Pool); + + function totalSupply() external view returns (uint256); + + function balanceOf(address account) external view returns (uint256); + + function getMintAmounts(uint256 amount0Max, uint256 amount1Max) + external + view + returns ( + uint256 amount0, + uint256 amount1, + uint256 mintAmount + ); + + function getPositionID() external view returns (bytes32 positionID); + + function lowerTick() external view returns (int24); + + function upperTick() external view returns (int24); + + +} + +interface GUniResolver { + + function getPoolUnderlyingBalances(IGUniPool pool) + external + view + returns (uint256 amount0, uint256 amount1); + + function getUnderlyingBalances( + IGUniPool pool, + uint256 balance + ) external view returns (uint256 amount0, uint256 amount1); + + function getRebalanceParams( + IGUniPool pool, + uint256 amount0In, + uint256 amount1In, + uint16 slippageBPS + ) external view returns ( + bool zeroForOne, + uint256 swapAmount, + uint160 swapThreshold + ); + +} + +struct StakingRewardsInfo { + address stakingRewards; + uint rewardAmount; +} +interface StakingFactoryInterface { + + function stakingRewardsInfoByStakingToken(address) external view returns(StakingRewardsInfo memory); + +} + +interface StakingInterface { + function totalSupply() external view returns (uint256); + function rewardRate() external view returns (uint256); + function balanceOf(address account) external view returns (uint256); + function earned(address account) external view returns (uint256); + function rewardPerToken() external view returns (uint256); +} + + +contract DSMath { + + function add(uint x, uint y) internal pure returns (uint z) { + require((z = x + y) >= x, "math-not-safe"); + } + + function mul(uint x, uint y) internal pure returns (uint z) { + require(y == 0 || (z = x * y) / y == x, "math-not-safe"); + } + + uint constant WAD = 10 ** 18; + + function wmul(uint x, uint y) internal pure returns (uint z) { + z = add(mul(x, y), WAD / 2) / WAD; + } + + function wdiv(uint x, uint y) internal pure returns (uint z) { + z = add(mul(x, WAD), y / 2) / y; + } + + function sub(uint x, uint y) internal pure returns (uint z) { + require((z = x - y) <= x, "ds-math-sub-underflow"); + } + +} + +contract Helpers is DSMath { + + StakingFactoryInterface public constant getStakingFactory = StakingFactoryInterface(0xf39eC5a471edF20Ecc7db1c2c34B4C73ab4B2C19); + GUniResolver public constant gelatoRouter = GUniResolver(0xC8b92036cf2bfc5aD2116c9b9Fb3cee2d3b3dc89); + + struct UserData { + address pool; // address of pool contract + address staking; // address of staking contract + address token0Addr; // address of token 0 + address token1Addr; // address of token 1 + uint poolTokenSupply; // Total supply of Pool token + uint poolToken0Bal; // balance of total pool for token0 + uint poolToken1Bal; // balance of total pool for token1 + uint poolTokenSupplyStaked; // total pool token locked in staking contract + uint stakingToken0Bal; // total balance of token0 in Staking + uint stakingToken1Bal; // total balance of token1 in Staking + uint rewardRate; // INST distributing per second + uint token0Bal; // balance of token 0 of user + uint token1Bal; // balance of token 1 of user + uint earned; // INST earned from staking + uint stakedBal; // user's pool token bal in staking contract + uint poolBal; // ideal pool token in user's DSA + uint totalBal; // stakedBal + poolTknBal + uint token0Decimals; // token0 decimals + uint token1Decimals; // token1 decimals + int24 currentTick; // Current price of 1 token w.r.t to other + int24 lowerTick; // Price of 1 token w.r.t to other at lower tick + int24 upperTick; // Price of 1 token w.r.t to other at upper tick + } + +} + +contract Resolver is Helpers { + + function getSinglePosition(address user, address pool) public view returns(UserData memory _data) { + _data.pool = pool; + StakingInterface stakingContract = StakingInterface(getStakingFactory.stakingRewardsInfoByStakingToken(pool).stakingRewards); + _data.staking = address(stakingContract); + IGUniPool poolContract = IGUniPool(pool); + _data.token0Addr = address(poolContract.token0()); + _data.token1Addr = address(poolContract.token1()); + if (_data.staking == address(0)) { + _data.earned = 0; + _data.stakedBal = 0; + } else { + _data.earned = stakingContract.earned(user); + _data.stakedBal = stakingContract.balanceOf(user); + } + _data.poolBal = poolContract.balanceOf(user); + _data.totalBal = add(_data.stakedBal, _data.poolBal); + (_data.token0Bal, _data.token1Bal) = gelatoRouter.getUnderlyingBalances(poolContract, _data.totalBal); + _data.poolTokenSupply = poolContract.balanceOf(user); + (_data.poolToken0Bal, _data.poolToken1Bal) = gelatoRouter.getPoolUnderlyingBalances(poolContract); + _data.poolTokenSupplyStaked = stakingContract.totalSupply(); + (_data.stakingToken0Bal, _data.stakingToken1Bal) = gelatoRouter.getUnderlyingBalances(poolContract, _data.poolTokenSupplyStaked); + _data.rewardRate = stakingContract.rewardRate(); + + _data.token0Decimals = poolContract.token0().decimals(); + _data.token1Decimals = poolContract.token1().decimals(); + + IUniswapV3Pool uniNft = poolContract.pool(); + (, _data.currentTick, , , , , ) = uniNft.slot0(); + _data.lowerTick = poolContract.lowerTick(); + _data.upperTick = poolContract.upperTick(); + } + + function getPosition(address user, address[] memory pools) public view returns(UserData[] memory _data) { + _data = new UserData[](pools.length); + for (uint i = 0; i < pools.length; i++) { + _data[i] = getSinglePosition(user, pools[i]); + } + } + + /** + * @param pool - gelato pool address. + * @param amount0In - amount of token0 user wants to deposit. + * @param amount1In - amount of token1 user wants to deposit. + * @param slippage in 18 decimal where 100% = 1e18. + * @return zeroForOne - if true swap token0 for token1 else vice versa + * @return swapAmount - Amount of tokens to swap. + * @return swapThreshold - Max slippage that the swap can take. + */ + function getSwapAndDepositParams( + address pool, + uint amount0In, + uint amount1In, + uint slippage + ) public view returns ( + bool zeroForOne, + uint256 swapAmount, + uint160 swapThreshold + ) { + uint slippageBPS = slippage / 1e16; + (zeroForOne, swapAmount, swapThreshold) = gelatoRouter.getRebalanceParams(IGUniPool(pool), amount0In, amount1In, uint16(slippageBPS)); + } + + /** + * @param user - address of user. + * @param pool - address of Gelato Pool. + * @param burnPercent - in 18 decimal where 100% = 1e18. + * @param slippage in 18 decimal where 100% = 1e18. + * @return burnAmt - Amount of pool tokens to burn. + * @return amount0 - Amount of token0 user will get. + * @return amount1 - Amount of token1 user will get. + * @return amount0Min - Min amount of token0 user should get. + * @return amount1Min - Min amount of token1 user should get. + */ + function getWithdrawParams(address user, address pool, uint burnPercent, uint slippage) public view returns (uint burnAmt, uint amount0, uint amount1, uint amount0Min, uint amount1Min) { + UserData memory _data = getSinglePosition(user, pool); + burnPercent = burnPercent > 1e18 ? 1e18 : burnPercent; + burnAmt = wmul(_data.totalBal, burnPercent); + amount0 = wmul(_data.token0Bal, burnPercent); + amount1 = wmul(_data.token1Bal, burnPercent); + amount0Min = wmul(amount0, sub(1e18, slippage)); + amount1Min = wmul(amount1, sub(1e18, slippage)); + } + +} \ No newline at end of file