Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
contracts/contracts/defi/routers/StakeRouter.sol
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
432 lines (381 sloc)
13.1 KB
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
| pragma solidity >=0.8.0 <0.9.0; | |
| //Copyright of DataX Protocol contributors | |
| //SPDX-License-Identifier: BSU-1.1 | |
| import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; | |
| import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
| import "@openzeppelin/contracts/utils/math/SafeMath.sol"; | |
| import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
| import "../utils/Admin.sol"; | |
| import "../utils/Const.sol"; | |
| import "../../interfaces/defi/IAdapter.sol"; | |
| import "../../interfaces/defi/IFeeCalc.sol"; | |
| import "../../interfaces/ocean/IPool.sol"; | |
| contract StakeRouter is ReentrancyGuard, Const, Admin { | |
| IFeeCalc fees; | |
| using SafeMath for uint256; | |
| using SafeERC20 for IERC20; | |
| mapping(address => mapping(address => uint256)) public referralFees; | |
| struct StakeInfo { | |
| address[4] meta; //[pool, to, refAddress, adapterAddress] | |
| uint256[3] uints; //[amountIn/maxAmountIn, refFees, amountOut/minAmountOut] | |
| address[] path; | |
| } | |
| event StakedETHInPool( | |
| address indexed pool, | |
| address indexed beneficiary, | |
| address referrer, | |
| uint256 amountInETH, | |
| uint256 amountInBasetoken | |
| ); | |
| event StakedTokenInPool( | |
| address indexed pool, | |
| address indexed token, | |
| address indexed beneficiary, | |
| address referrer, | |
| uint256 amountInToken, | |
| uint256 amountInBasetoken | |
| ); | |
| event UnstakedETHFromPool( | |
| address indexed pool, | |
| address indexed beneficiary, | |
| address referrer, | |
| uint256 amountInETH, | |
| uint256 amountInBasetoken | |
| ); | |
| event UnstakedTokenFromPool( | |
| address indexed pool, | |
| address indexed token, | |
| address indexed beneficiary, | |
| address referrer, | |
| uint256 amountInToken, | |
| uint256 amountInBasetoken | |
| ); | |
| event ReferralFeesClaimed( | |
| address indexed referrer, | |
| address indexed token, | |
| uint256 claimedAmout | |
| ); | |
| constructor(address _feeCalc) { | |
| fees = IFeeCalc(_feeCalc); | |
| } | |
| function stakeETHInDTPool(StakeInfo calldata info) | |
| external | |
| payable | |
| nonReentrant | |
| returns (uint256 poolTokensOut) | |
| { | |
| require( | |
| info.meta[2] != address(0), | |
| "StakeRouter: Destination address not provided" | |
| ); | |
| require(info.path.length > 1, "StakeRouter: Path too short"); | |
| //TODO: deduct trade fee + ref fee | |
| IAdapter adapter = IAdapter(info.meta[3]); | |
| IERC20 baseToken = IERC20(info.path[info.path.length - 1]); | |
| //handle Pool swap | |
| IPool pool = IPool(info.meta[0]); | |
| //swap ETH to base token | |
| uint256[] memory amounts = adapter.getAmountsOut(msg.value, info.path); | |
| uint256 baseAmountOutSansFee = adapter.swapExactETHForTokens{ | |
| value: msg.value | |
| }(amounts[info.path.length - 1], info.path, address(this)); | |
| //deduct fees | |
| (uint256 dataxFee, uint256 refFee) = fees.calcFees( | |
| baseAmountOutSansFee, | |
| STAKE_FEE_TYPE, | |
| info.uints[1] | |
| ); | |
| //collect ref Fees | |
| if (info.meta[2] != address(0)) { | |
| referralFees[info.meta[2]][address(baseToken)] = referralFees[ | |
| info.meta[2] | |
| ][address(baseToken)].add(refFee); | |
| //datax fee | |
| referralFees[admin][address(baseToken)] = referralFees[admin][ | |
| address(baseToken) | |
| ].add(dataxFee); | |
| } else { | |
| referralFees[admin][address(baseToken)] = referralFees[admin][ | |
| address(baseToken) | |
| ].add(dataxFee.add(refFee)); | |
| } | |
| // actual base amount minus fees | |
| uint256 baseAmountOut = baseAmountOutSansFee.sub(dataxFee.add(refFee)); | |
| //approve Pool to spend base token | |
| baseToken.safeIncreaseAllowance(address(pool), baseAmountOut); | |
| //stake tokens in Pool | |
| poolTokensOut = pool.joinswapExternAmountIn( | |
| baseAmountOut, | |
| info.uints[2] | |
| ); | |
| //transfer pool tokens to destination address | |
| IERC20(info.meta[0]).safeTransfer(info.meta[1], poolTokensOut); | |
| emit StakedETHInPool( | |
| info.meta[0], | |
| info.meta[1], | |
| info.meta[2], | |
| msg.value, | |
| baseAmountOut | |
| ); | |
| } | |
| function unstakeETHFromDTPool(StakeInfo calldata info) | |
| external | |
| nonReentrant | |
| returns (uint256 ethAmountOut) | |
| { | |
| require( | |
| info.meta[2] != address(0), | |
| "StakeRouter: Destination address not provided" | |
| ); | |
| require( | |
| info.uints[0] <= | |
| IERC20(info.meta[0]).allowance(msg.sender, address(this)), | |
| "StakeRouter: Not enough token allowance" | |
| ); | |
| require(info.path.length > 1, "StakeRouter: Path too short"); | |
| IERC20 baseToken = IERC20(info.path[0]); | |
| //unstake into baseToken | |
| IPool pool = IPool(info.meta[0]); | |
| IERC20(info.meta[0]).safeTransferFrom( | |
| msg.sender, | |
| address(this), | |
| info.uints[0] | |
| ); | |
| IERC20(info.meta[0]).safeIncreaseAllowance( | |
| address(pool), | |
| info.uints[0] | |
| ); | |
| //unstake baseToken from Pool | |
| uint256 baseAmountOutSansFee = pool.exitswapPoolAmountIn( | |
| info.uints[0], | |
| info.uints[2] | |
| ); | |
| //deduct fees | |
| (uint256 dataxFee, uint256 refFee) = fees.calcFees( | |
| baseAmountOutSansFee, | |
| UNSTAKE_FEE_TYPE, | |
| info.uints[1] | |
| ); | |
| //collect ref Fees | |
| if (info.meta[2] != address(0)) { | |
| referralFees[info.meta[2]][address(baseToken)] = referralFees[ | |
| info.meta[2] | |
| ][address(baseToken)].add(refFee); | |
| //datax fee | |
| referralFees[admin][address(baseToken)] = referralFees[admin][ | |
| address(baseToken) | |
| ].add(dataxFee); | |
| } else { | |
| referralFees[admin][address(baseToken)] = referralFees[admin][ | |
| address(baseToken) | |
| ].add(dataxFee.add(refFee)); | |
| } | |
| // actual base amount minus fees | |
| uint256 baseAmountOut = baseAmountOutSansFee.sub(dataxFee.add(refFee)); | |
| baseToken.safeIncreaseAllowance(info.meta[3], baseAmountOut); | |
| //swap to output token | |
| IAdapter adapter = IAdapter(info.meta[3]); | |
| //swap basetoken to ETH | |
| uint256[] memory amounts = adapter.getAmountsOut( | |
| baseAmountOut, | |
| info.path | |
| ); | |
| ethAmountOut = adapter.swapExactTokensForETH( | |
| baseAmountOut, | |
| amounts[info.path.length - 1], | |
| info.path, | |
| info.meta[1] | |
| ); | |
| emit UnstakedETHFromPool( | |
| info.meta[0], | |
| info.meta[1], | |
| info.meta[2], | |
| ethAmountOut, | |
| baseAmountOut | |
| ); | |
| } | |
| function stakeTokenInDTPool(StakeInfo calldata info) | |
| external | |
| nonReentrant | |
| returns (uint256 poolTokensOut) | |
| { | |
| require( | |
| info.meta[2] != address(0), | |
| "StakeRouter: Destination address not provided" | |
| ); | |
| require( | |
| info.uints[0] <= | |
| IERC20(info.path[0]).allowance(msg.sender, address(this)), | |
| "StakeRouter: Not enough token allowance" | |
| ); | |
| IERC20 baseToken = IERC20(info.path[info.path.length - 1]); | |
| uint256 baseAmountOutSansFee = info.uints[0]; | |
| require( | |
| IERC20(info.path[0]).balanceOf(msg.sender) >= info.uints[0], | |
| "StakeRouter: Not enough tokenIn balance" | |
| ); | |
| IERC20(info.path[0]).safeTransferFrom( | |
| msg.sender, | |
| address(this), | |
| info.uints[0] | |
| ); | |
| //skip if tokenIn is baseToken | |
| if (info.path.length > 1) { | |
| IAdapter adapter = IAdapter(info.meta[3]); | |
| uint256[] memory amounts = adapter.getAmountsOut( | |
| info.uints[0], | |
| info.path | |
| ); | |
| IERC20(info.path[0]).safeIncreaseAllowance( | |
| info.meta[3], | |
| info.uints[0] | |
| ); | |
| baseAmountOutSansFee = adapter.swapExactTokensForTokens( | |
| info.uints[0], | |
| amounts[info.path.length - 1], | |
| info.path, | |
| address(this) | |
| ); | |
| } | |
| //deduct fees | |
| (uint256 dataxFee, uint256 refFee) = fees.calcFees( | |
| baseAmountOutSansFee, | |
| STAKE_FEE_TYPE, | |
| info.uints[1] | |
| ); | |
| //collect ref Fees | |
| if (info.meta[2] != address(0)) { | |
| referralFees[info.meta[2]][address(baseToken)] = referralFees[ | |
| info.meta[2] | |
| ][address(baseToken)].add(refFee); | |
| //datax fee | |
| referralFees[admin][address(baseToken)] = referralFees[admin][ | |
| address(baseToken) | |
| ].add(dataxFee); | |
| } else { | |
| referralFees[admin][address(baseToken)] = referralFees[admin][ | |
| address(baseToken) | |
| ].add(dataxFee.add(refFee)); | |
| } | |
| // actual base amount minus fees | |
| uint256 baseAmountOut = baseAmountOutSansFee.sub(dataxFee.add(refFee)); | |
| //handle Pool swap | |
| IPool pool = IPool(info.meta[0]); | |
| //approve Pool to spend base token | |
| baseToken.safeIncreaseAllowance(info.meta[0], baseAmountOut); | |
| //stake tokens in Pool | |
| poolTokensOut = pool.joinswapExternAmountIn( | |
| baseAmountOut, | |
| info.uints[2] | |
| ); | |
| //transfer pool tokens to destination address | |
| IERC20(info.meta[0]).safeTransfer(info.meta[1], poolTokensOut); | |
| emit StakedTokenInPool( | |
| info.meta[0], | |
| info.path[0], | |
| info.meta[1], | |
| info.meta[2], | |
| info.uints[0], | |
| baseAmountOut | |
| ); | |
| } | |
| function unstakeTokenFromDTPool(StakeInfo calldata info) | |
| external | |
| nonReentrant | |
| returns (uint256 tokenAmountOut) | |
| { | |
| require( | |
| info.meta[2] != address(0), | |
| "StakeRouter: Destination address not provided" | |
| ); | |
| require( | |
| info.uints[0] <= | |
| IERC20(info.meta[0]).allowance(msg.sender, address(this)), | |
| "StakeRouter: Not enough token allowance" | |
| ); | |
| //unstake into baseToken | |
| IPool pool = IPool(info.meta[0]); | |
| IERC20(info.meta[0]).safeTransferFrom( | |
| msg.sender, | |
| address(this), | |
| info.uints[0] | |
| ); | |
| IERC20(info.meta[0]).safeIncreaseAllowance( | |
| address(pool), | |
| info.uints[0] | |
| ); | |
| //unstake baseToken from Pool | |
| uint256 baseAmountOutSansFee = pool.exitswapPoolAmountIn( | |
| info.uints[0], | |
| info.uints[2] | |
| ); | |
| IERC20 baseToken = IERC20(info.path[0]); | |
| //deduct fees | |
| (uint256 dataxFee, uint256 refFee) = fees.calcFees( | |
| baseAmountOutSansFee, | |
| UNSTAKE_FEE_TYPE, | |
| info.uints[1] | |
| ); | |
| //collect ref Fees | |
| if (info.meta[2] != address(0)) { | |
| referralFees[info.meta[2]][address(baseToken)] = referralFees[ | |
| info.meta[2] | |
| ][address(baseToken)].add(refFee); | |
| //datax fee | |
| referralFees[admin][address(baseToken)] = referralFees[admin][ | |
| address(baseToken) | |
| ].add(dataxFee); | |
| } else { | |
| referralFees[admin][address(baseToken)] = referralFees[admin][ | |
| address(baseToken) | |
| ].add(dataxFee.add(refFee)); | |
| } | |
| // actual base amount minus fees | |
| uint256 baseAmountOut = baseAmountOutSansFee.sub(dataxFee.add(refFee)); | |
| //skip if tokenOut is the baseToken | |
| if (info.path.length > 1) { | |
| baseToken.safeIncreaseAllowance(info.meta[3], baseAmountOut); | |
| //swap to output token | |
| IAdapter adapter = IAdapter(info.meta[3]); | |
| //swap basetoken to Destination token | |
| uint256[] memory amounts = adapter.getAmountsOut( | |
| baseAmountOut, | |
| info.path | |
| ); | |
| tokenAmountOut = adapter.swapExactTokensForTokens( | |
| baseAmountOut, | |
| amounts[info.path.length - 1], | |
| info.path, | |
| info.meta[1] | |
| ); | |
| } else { | |
| //send tokenOut to destination address | |
| baseToken.safeTransfer(info.meta[1], baseAmountOut); | |
| } | |
| emit UnstakedTokenFromPool( | |
| info.meta[0], | |
| info.path[0], | |
| info.meta[1], | |
| info.meta[2], | |
| info.uints[0], | |
| baseAmountOut | |
| ); | |
| } | |
| //claim collected Referral fees | |
| function claimRefFees(address token, address referrer) | |
| external | |
| nonReentrant | |
| returns (uint256 claimAmount) | |
| { | |
| IERC20 baseToken = IERC20(token); | |
| claimAmount = referralFees[referrer][token]; | |
| require(claimAmount > 0, "StakeRouter: No tokens to claim"); | |
| //reset claimable amount | |
| referralFees[referrer][token] = 0; | |
| //transfer tokens to referrer | |
| baseToken.safeTransfer(referrer, claimAmount); | |
| emit ReferralFeesClaimed(referrer, token, claimAmount); | |
| } | |
| //receive ETH | |
| receive() external payable {} | |
| } |