文件 1 的 1:Router03.sol
pragma solidity >=0.5.0;
interface IRouter03 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function mint(address poolToken, uint amount, address to, uint deadline) external returns (uint tokens);
function mintETH(address poolToken, address to, uint deadline) external payable returns (uint tokens);
function mintCollateral(address poolToken, uint amount, address to, uint deadline, bytes calldata permitData) external returns (uint tokens);
function redeem(address poolToken, uint tokens, address to, uint deadline, bytes calldata permitData) external returns (uint amount);
function redeemETH(address poolToken, uint tokens, address to, uint deadline, bytes calldata permitData) external returns (uint amountETH);
function borrow(address borrowable, uint amount, address to, uint deadline, bytes calldata permitData) external;
function borrowETH(address borrowable, uint amountETH, address to, uint deadline, bytes calldata permitData) external;
function repay(address borrowable, uint amountMax, address borrower, uint deadline) external returns (uint amount);
function repayETH(address borrowable, address borrower, uint deadline) external payable returns (uint amountETH);
function liquidate(address borrowable, uint amountMax, address borrower, address to, uint deadline) external returns (uint amount, uint seizeTokens);
function liquidateETH(address borrowable, address borrower, address to, uint deadline) external payable returns (uint amountETH, uint seizeTokens);
function leverage(
address uniswapV2Pair, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin,
address to, uint deadline, bytes calldata permitDataA, bytes calldata permitDataB
) external;
function deleverage(
address uniswapV2Pair, uint redeemTokens, uint amountAMin, uint amountBMin, uint deadline, bytes calldata permitData
) external;
function isStakedLPToken(address underlying) external view returns(bool);
function getUniswapV2Pair(address underlying) external view returns (address);
function getBorrowable(address uniswapV2Pair, uint8 index) external view returns (address borrowable);
function getCollateral(address uniswapV2Pair) external view returns (address collateral);
function getLendingPool(address uniswapV2Pair) external view returns (address collateral, address borrowableA, address borrowableB);
}
pragma solidity >=0.5.0;
interface IPoolToken {
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, address indexed minter, uint mintAmount, uint mintTokens);
event Redeem(address indexed sender, address indexed redeemer, uint redeemAmount, uint redeemTokens);
event Sync(uint totalBalance);
function underlying() external view returns (address);
function factory() external view returns (address);
function totalBalance() external view returns (uint);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function exchangeRate() external returns (uint);
function mint(address minter) external returns (uint mintTokens);
function redeem(address redeemer) external returns (uint redeemAmount);
function skim(address to) external;
function sync() external;
function _setFactory() external;
}
pragma solidity >=0.5.0;
interface IBorrowable {
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, address indexed minter, uint mintAmount, uint mintTokens);
event Redeem(address indexed sender, address indexed redeemer, uint redeemAmount, uint redeemTokens);
event Sync(uint totalBalance);
function underlying() external view returns (address);
function factory() external view returns (address);
function totalBalance() external view returns (uint);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function exchangeRate() external returns (uint);
function mint(address minter) external returns (uint mintTokens);
function redeem(address redeemer) external returns (uint redeemAmount);
function skim(address to) external;
function sync() external;
function _setFactory() external;
event BorrowApproval(address indexed owner, address indexed spender, uint value);
event Borrow(address indexed sender, address indexed borrower, address indexed receiver, uint borrowAmount, uint repayAmount, uint accountBorrowsPrior, uint accountBorrows, uint totalBorrows);
event Liquidate(address indexed sender, address indexed borrower, address indexed liquidator, uint seizeTokens, uint repayAmount, uint accountBorrowsPrior, uint accountBorrows, uint totalBorrows);
function BORROW_FEE() external pure returns (uint);
function collateral() external view returns (address);
function reserveFactor() external view returns (uint);
function exchangeRateLast() external view returns (uint);
function borrowIndex() external view returns (uint);
function totalBorrows() external view returns (uint);
function borrowAllowance(address owner, address spender) external view returns (uint);
function borrowBalance(address borrower) external view returns (uint);
function borrowTracker() external view returns (address);
function BORROW_PERMIT_TYPEHASH() external pure returns (bytes32);
function borrowApprove(address spender, uint256 value) external returns (bool);
function borrowPermit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
function borrow(address borrower, address receiver, uint borrowAmount, bytes calldata data) external;
function liquidate(address borrower, address liquidator) external returns (uint seizeTokens);
function trackBorrow(address borrower) external;
event AccrueInterest(uint interestAccumulated, uint borrowIndex, uint totalBorrows);
event CalculateKink(uint kinkRate);
event CalculateBorrowRate(uint borrowRate);
function KINK_BORROW_RATE_MAX() external pure returns (uint);
function KINK_BORROW_RATE_MIN() external pure returns (uint);
function KINK_MULTIPLIER() external pure returns (uint);
function borrowRate() external view returns (uint);
function kinkBorrowRate() external view returns (uint);
function kinkUtilizationRate() external view returns (uint);
function adjustSpeed() external view returns (uint);
function rateUpdateTimestamp() external view returns (uint32);
function accrualTimestamp() external view returns (uint32);
function accrueInterest() external;
event NewReserveFactor(uint newReserveFactor);
event NewKinkUtilizationRate(uint newKinkUtilizationRate);
event NewAdjustSpeed(uint newAdjustSpeed);
event NewBorrowTracker(address newBorrowTracker);
function RESERVE_FACTOR_MAX() external pure returns (uint);
function KINK_UR_MIN() external pure returns (uint);
function KINK_UR_MAX() external pure returns (uint);
function ADJUST_SPEED_MIN() external pure returns (uint);
function ADJUST_SPEED_MAX() external pure returns (uint);
function _initialize (
string calldata _name,
string calldata _symbol,
address _underlying,
address _collateral
) external;
function _setReserveFactor(uint newReserveFactor) external;
function _setKinkUtilizationRate(uint newKinkUtilizationRate) external;
function _setAdjustSpeed(uint newAdjustSpeed) external;
function _setBorrowTracker(address newBorrowTracker) external;
}
pragma solidity >=0.5.0;
interface IFactory {
event LendingPoolInitialized(address indexed uniswapV2Pair, address indexed token0, address indexed token1,
address collateral, address borrowable0, address borrowable1, uint lendingPoolId);
event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
event NewAdmin(address oldAdmin, address newAdmin);
event NewReservesPendingAdmin(address oldReservesPendingAdmin, address newReservesPendingAdmin);
event NewReservesAdmin(address oldReservesAdmin, address newReservesAdmin);
event NewReservesManager(address oldReservesManager, address newReservesManager);
function admin() external view returns (address);
function pendingAdmin() external view returns (address);
function reservesAdmin() external view returns (address);
function reservesPendingAdmin() external view returns (address);
function reservesManager() external view returns (address);
function getLendingPool(address uniswapV2Pair) external view returns (
bool initialized,
uint24 lendingPoolId,
address collateral,
address borrowable0,
address borrowable1
);
function allLendingPools(uint) external view returns (address uniswapV2Pair);
function allLendingPoolsLength() external view returns (uint);
function bDeployer() external view returns (address);
function cDeployer() external view returns (address);
function simpleUniswapOracle() external view returns (address);
function createCollateral(address uniswapV2Pair) external returns (address collateral);
function createBorrowable0(address uniswapV2Pair) external returns (address borrowable0);
function createBorrowable1(address uniswapV2Pair) external returns (address borrowable1);
function initializeLendingPool(address uniswapV2Pair) external;
function _setPendingAdmin(address newPendingAdmin) external;
function _acceptAdmin() external;
function _setReservesPendingAdmin(address newPendingAdmin) external;
function _acceptReservesAdmin() external;
function _setReservesManager(address newReservesManager) external;
}
pragma solidity >=0.5.0;
interface ICollateral {
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, address indexed minter, uint mintAmount, uint mintTokens);
event Redeem(address indexed sender, address indexed redeemer, uint redeemAmount, uint redeemTokens);
event Sync(uint totalBalance);
function underlying() external view returns (address);
function factory() external view returns (address);
function totalBalance() external view returns (uint);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function exchangeRate() external returns (uint);
function mint(address minter) external returns (uint mintTokens);
function redeem(address redeemer) external returns (uint redeemAmount);
function skim(address to) external;
function sync() external;
function _setFactory() external;
function borrowable0() external view returns (address);
function borrowable1() external view returns (address);
function simpleUniswapOracle() external view returns (address);
function safetyMarginSqrt() external view returns (uint);
function liquidationIncentive() external view returns (uint);
function getPrices() external returns (uint price0, uint price1);
function tokensUnlocked(address from, uint value) external returns (bool);
function accountLiquidityAmounts(address account, uint amount0, uint amount1) external returns (uint liquidity, uint shortfall);
function accountLiquidity(address account) external returns (uint liquidity, uint shortfall);
function canBorrow(address account, address borrowable, uint accountBorrows) external returns (bool);
function seize(address liquidator, address borrower, uint repayAmount) external returns (uint seizeTokens);
function flashRedeem(address redeemer, uint redeemAmount, bytes calldata data) external;
event NewSafetyMargin(uint newSafetyMarginSqrt);
event NewLiquidationIncentive(uint newLiquidationIncentive);
function SAFETY_MARGIN_SQRT_MIN() external pure returns (uint);
function SAFETY_MARGIN_SQRT_MAX() external pure returns (uint);
function LIQUIDATION_INCENTIVE_MIN() external pure returns (uint);
function LIQUIDATION_INCENTIVE_MAX() external pure returns (uint);
function _initialize (
string calldata _name,
string calldata _symbol,
address _underlying,
address _borrowable0,
address _borrowable1
) external;
function _setSafetyMarginSqrt(uint newSafetyMarginSqrt) external;
function _setLiquidationIncentive(uint newLiquidationIncentive) external;
}
pragma solidity >=0.5.0;
interface IImpermaxCallee {
function impermaxBorrow(address sender, address borrower, uint borrowAmount, bytes calldata data) external;
function impermaxRedeem(address sender, uint redeemAmount, bytes calldata data) external;
}
pragma solidity >=0.5.0;
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}
pragma solidity >=0.5.0;
interface IStakedLPToken01 {
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, address indexed minter, uint mintAmount, uint mintTokens);
event Redeem(address indexed sender, address indexed redeemer, uint redeemAmount, uint redeemTokens);
event Sync(uint totalBalance);
function underlying() external view returns (address);
function factory() external view returns (address);
function totalBalance() external view returns (uint);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function exchangeRate() external returns (uint);
function mint(address minter) external returns (uint mintTokens);
function redeem(address redeemer) external returns (uint redeemAmount);
function skim(address to) external;
function sync() external;
function _setFactory() external;
event Reinvest(address indexed caller, uint256 reward, uint256 bounty);
function isStakedLPToken() external pure returns (bool);
function stakingRewards() external view returns (address);
function rewardsToken() external view returns (address);
function router() external view returns (address);
function WETH() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function REINVEST_BOUNTY() external pure returns (uint256);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint256);
function price1CumulativeLast() external view returns (uint256);
function _initialize (
address _stakingRewards,
address _underlying,
address _rewardsToken,
address _token0,
address _token1,
address _router,
address _WETH
) external;
function reinvest() external;
}
pragma solidity >=0.5.0;
interface IWETH {
function deposit() external payable;
function transfer(address to, uint value) external returns (bool);
function withdraw(uint) external;
}
pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
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 skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
pragma solidity =0.6.6;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, errorMessage);
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction underflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, errorMessage);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
pragma solidity =0.6.6;
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeApprove: approve failed'
);
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeTransfer: transfer failed'
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::transferFrom: transferFrom failed'
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
}
pragma solidity >=0.5.0;
library UniswapV2Library {
using SafeMath for uint;
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
}
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
))));
}
function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
(address token0,) = sortTokens(tokenA, tokenB);
(uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
amountB = amountA.mul(reserveB) / reserveA;
}
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(997);
uint numerator = amountInWithFee.mul(reserveOut);
uint denominator = reserveIn.mul(1000).add(amountInWithFee);
amountOut = numerator / denominator;
}
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
uint numerator = reserveIn.mul(amountOut).mul(1000);
uint denominator = reserveOut.sub(amountOut).mul(997);
amountIn = (numerator / denominator).add(1);
}
function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
amounts = new uint[](path.length);
amounts[0] = amountIn;
for (uint i; i < path.length - 1; i++) {
(uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
}
}
function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
amounts = new uint[](path.length);
amounts[amounts.length - 1] = amountOut;
for (uint i = path.length - 1; i > 0; i--) {
(uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
}
}
}
pragma solidity =0.6.6;
pragma experimental ABIEncoderV2;
contract Router03 is IRouter03, IImpermaxCallee {
using SafeMath for uint;
address public immutable override factory;
address public immutable override WETH;
modifier ensure(uint deadline) {
require(deadline >= block.timestamp, "ImpermaxRouter: EXPIRED");
_;
}
modifier checkETH(address poolToken) {
require(WETH == IPoolToken(poolToken).underlying(), "ImpermaxRouter: NOT_WETH");
_;
}
constructor(address _factory, address _WETH) public {
factory = _factory;
WETH = _WETH;
}
receive() external payable {
assert(msg.sender == WETH);
}
function _mint(
address poolToken,
address token,
uint amount,
address from,
address to
) internal virtual returns (uint tokens) {
if (from == address(this)) TransferHelper.safeTransfer(token, poolToken, amount);
else TransferHelper.safeTransferFrom(token, from, poolToken, amount);
tokens = IPoolToken(poolToken).mint(to);
}
function mint(
address poolToken,
uint amount,
address to,
uint deadline
) external virtual override ensure(deadline) returns (uint tokens) {
return _mint(poolToken, IPoolToken(poolToken).underlying(), amount, msg.sender, to);
}
function mintETH(
address poolToken,
address to,
uint deadline
) external virtual override payable ensure(deadline) checkETH(poolToken) returns (uint tokens) {
IWETH(WETH).deposit{value: msg.value}();
return _mint(poolToken, WETH, msg.value, address(this), to);
}
function mintCollateral(
address poolToken,
uint amount,
address to,
uint deadline,
bytes calldata permitData
) external virtual override ensure(deadline) returns (uint tokens) {
address underlying = IPoolToken(poolToken).underlying();
if (isStakedLPToken(underlying)) {
address uniswapV2Pair = IStakedLPToken01(underlying).underlying();
_permit(uniswapV2Pair, amount, deadline, permitData);
TransferHelper.safeTransferFrom(uniswapV2Pair, msg.sender, underlying, amount);
IStakedLPToken01(underlying).mint(poolToken);
return IPoolToken(poolToken).mint(to);
} else {
_permit(underlying, amount, deadline, permitData);
return _mint(poolToken, underlying, amount, msg.sender, to);
}
}
function redeem(
address poolToken,
uint tokens,
address to,
uint deadline,
bytes memory permitData
) public virtual override ensure(deadline) returns (uint amount) {
_permit(poolToken, tokens, deadline, permitData);
uint tokensBalance = IERC20(poolToken).balanceOf(msg.sender);
tokens = tokens < tokensBalance ? tokens : tokensBalance;
IPoolToken(poolToken).transferFrom(msg.sender, poolToken, tokens);
address underlying = IPoolToken(poolToken).underlying();
if (isStakedLPToken(underlying)) {
IPoolToken(poolToken).redeem(underlying);
return IStakedLPToken01(underlying).redeem(to);
} else {
return IPoolToken(poolToken).redeem(to);
}
}
function redeemETH(
address poolToken,
uint tokens,
address to,
uint deadline,
bytes memory permitData
) public virtual override ensure(deadline) checkETH(poolToken) returns (uint amountETH) {
_permit(poolToken, tokens, deadline, permitData);
uint tokensBalance = IERC20(poolToken).balanceOf(msg.sender);
tokens = tokens < tokensBalance ? tokens : tokensBalance;
IPoolToken(poolToken).transferFrom(msg.sender, poolToken, tokens);
amountETH = IPoolToken(poolToken).redeem(address(this));
IWETH(WETH).withdraw(amountETH);
TransferHelper.safeTransferETH(to, amountETH);
}
function borrow(
address borrowable,
uint amount,
address to,
uint deadline,
bytes memory permitData
) public virtual override ensure(deadline) {
_borrowPermit(borrowable, amount, deadline, permitData);
IBorrowable(borrowable).borrow(msg.sender, to, amount, new bytes(0));
}
function borrowETH(
address borrowable,
uint amountETH,
address to,
uint deadline,
bytes memory permitData
) public virtual override ensure(deadline) checkETH(borrowable) {
borrow(borrowable, amountETH, address(this), deadline, permitData);
IWETH(WETH).withdraw(amountETH);
TransferHelper.safeTransferETH(to, amountETH);
}
function _repayAmount(
address borrowable,
uint amountMax,
address borrower
) internal virtual returns (uint amount) {
IBorrowable(borrowable).accrueInterest();
uint borrowedAmount = IBorrowable(borrowable).borrowBalance(borrower);
amount = amountMax < borrowedAmount ? amountMax : borrowedAmount;
}
function repay(
address borrowable,
uint amountMax,
address borrower,
uint deadline
) external virtual override ensure(deadline) returns (uint amount) {
amount = _repayAmount(borrowable, amountMax, borrower);
TransferHelper.safeTransferFrom(IBorrowable(borrowable).underlying(), msg.sender, borrowable, amount);
IBorrowable(borrowable).borrow(borrower, address(0), 0, new bytes(0));
}
function repayETH(
address borrowable,
address borrower,
uint deadline
) external virtual override payable ensure(deadline) checkETH(borrowable) returns (uint amountETH) {
amountETH = _repayAmount(borrowable, msg.value, borrower);
IWETH(WETH).deposit{value: amountETH}();
assert(IWETH(WETH).transfer(borrowable, amountETH));
IBorrowable(borrowable).borrow(borrower, address(0), 0, new bytes(0));
if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
}
function liquidate(
address borrowable,
uint amountMax,
address borrower,
address to,
uint deadline
) external virtual override ensure(deadline) returns (uint amount, uint seizeTokens) {
amount = _repayAmount(borrowable, amountMax, borrower);
TransferHelper.safeTransferFrom(IBorrowable(borrowable).underlying(), msg.sender, borrowable, amount);
seizeTokens = IBorrowable(borrowable).liquidate(borrower, to);
}
function liquidateETH(
address borrowable,
address borrower,
address to,
uint deadline
) external virtual override payable ensure(deadline) checkETH(borrowable) returns (uint amountETH, uint seizeTokens) {
amountETH = _repayAmount(borrowable, msg.value, borrower);
IWETH(WETH).deposit{value: amountETH}();
assert(IWETH(WETH).transfer(borrowable, amountETH));
seizeTokens = IBorrowable(borrowable).liquidate(borrower, to);
if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
}
function _leverage(
address underlying,
uint amountA,
uint amountB,
address to
) internal virtual {
address borrowableA = getBorrowable(underlying, 0);
bytes memory borrowBData = abi.encode(CalleeData({
callType: CallType.ADD_LIQUIDITY_AND_MINT,
underlying: underlying,
borrowableIndex: 1,
data: abi.encode(AddLiquidityAndMintCalldata({
amountA: amountA,
amountB: amountB,
to: to
}))
}));
bytes memory borrowAData = abi.encode(CalleeData({
callType: CallType.BORROWB,
underlying: underlying,
borrowableIndex: 0,
data: abi.encode(BorrowBCalldata({
borrower: msg.sender,
receiver: address(this),
borrowAmount: amountB,
data: borrowBData
}))
}));
IBorrowable(borrowableA).borrow(msg.sender, address(this), amountA, borrowAData);
}
function leverage(
address underlying,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bytes calldata permitDataA,
bytes calldata permitDataB
) external virtual override ensure(deadline) {
_borrowPermit(getBorrowable(underlying, 0), amountADesired, deadline, permitDataA);
_borrowPermit(getBorrowable(underlying, 1), amountBDesired, deadline, permitDataB);
address uniswapV2Pair = getUniswapV2Pair(underlying);
(uint amountA, uint amountB) = _optimalLiquidity(uniswapV2Pair, amountADesired, amountBDesired, amountAMin, amountBMin);
_leverage(underlying, amountA, amountB, to);
}
function _addLiquidityAndMint(
address underlying,
uint amountA,
uint amountB,
address to
) internal virtual {
(address collateral, address borrowableA, address borrowableB) = getLendingPool(underlying);
address uniswapV2Pair = getUniswapV2Pair(underlying);
TransferHelper.safeTransfer(IBorrowable(borrowableA).underlying(), uniswapV2Pair, amountA);
TransferHelper.safeTransfer(IBorrowable(borrowableB).underlying(), uniswapV2Pair, amountB);
if (isStakedLPToken(underlying)) IUniswapV2Pair(uniswapV2Pair).mint(underlying);
IUniswapV2Pair(underlying).mint(collateral);
ICollateral(collateral).mint(to);
}
function deleverage(
address underlying,
uint redeemTokens,
uint amountAMin,
uint amountBMin,
uint deadline,
bytes calldata permitData
) external virtual override ensure(deadline) {
address collateral = getCollateral(underlying);
_permit(collateral, redeemTokens, deadline, permitData);
uint tokensBalance = IERC20(collateral).balanceOf(msg.sender);
redeemTokens = redeemTokens < tokensBalance ? redeemTokens : tokensBalance;
require(redeemTokens > 0, "ImpermaxRouter: REDEEM_ZERO");
uint exchangeRate = ICollateral(collateral).exchangeRate();
uint redeemAmount = (redeemTokens - 1).mul(exchangeRate).div(1e18);
bytes memory redeemData = abi.encode(CalleeData({
callType: CallType.REMOVE_LIQ_AND_REPAY,
underlying: underlying,
borrowableIndex: 0,
data: abi.encode(RemoveLiqAndRepayCalldata({
borrower: msg.sender,
redeemTokens: redeemTokens,
redeemAmount: redeemAmount,
amountAMin: amountAMin,
amountBMin: amountBMin
}))
}));
ICollateral(collateral).flashRedeem(address(this), redeemAmount, redeemData);
}
function _removeLiqAndRepay(
address underlying,
address borrower,
uint redeemTokens,
uint redeemAmount,
uint amountAMin,
uint amountBMin
) internal virtual {
(address collateral, address borrowableA, address borrowableB) = getLendingPool(underlying);
address tokenA = IBorrowable(borrowableA).underlying();
address tokenB = IBorrowable(borrowableB).underlying();
address uniswapV2Pair = getUniswapV2Pair(underlying);
IUniswapV2Pair(underlying).transfer(underlying, redeemAmount);
if (isStakedLPToken(underlying)) IStakedLPToken01(underlying).redeem(uniswapV2Pair);
(uint amountAMax, uint amountBMax) = IUniswapV2Pair(uniswapV2Pair).burn(address(this));
require(amountAMax >= amountAMin, "ImpermaxRouter: INSUFFICIENT_A_AMOUNT");
require(amountBMax >= amountBMin, "ImpermaxRouter: INSUFFICIENT_B_AMOUNT");
_repayAndRefund(borrowableA, tokenA, borrower, amountAMax);
_repayAndRefund(borrowableB, tokenB, borrower, amountBMax);
ICollateral(collateral).transferFrom(borrower, collateral, redeemTokens);
}
function _repayAndRefund(
address borrowable,
address token,
address borrower,
uint amountMax
) internal virtual {
uint amount = _repayAmount(borrowable, amountMax, borrower);
TransferHelper.safeTransfer(token, borrowable, amount);
IBorrowable(borrowable).borrow(borrower, address(0), 0, new bytes(0));
if (amountMax > amount) {
uint refundAmount = amountMax - amount;
if (token == WETH) {
IWETH(WETH).withdraw(refundAmount);
TransferHelper.safeTransferETH(borrower, refundAmount);
}
else TransferHelper.safeTransfer(token, borrower, refundAmount);
}
}
enum CallType {ADD_LIQUIDITY_AND_MINT, BORROWB, REMOVE_LIQ_AND_REPAY}
struct CalleeData {
CallType callType;
address underlying;
uint8 borrowableIndex;
bytes data;
}
struct AddLiquidityAndMintCalldata {
uint amountA;
uint amountB;
address to;
}
struct BorrowBCalldata {
address borrower;
address receiver;
uint borrowAmount;
bytes data;
}
struct RemoveLiqAndRepayCalldata {
address borrower;
uint redeemTokens;
uint redeemAmount;
uint amountAMin;
uint amountBMin;
}
function impermaxBorrow(address sender, address borrower, uint borrowAmount, bytes calldata data) external virtual override {
borrower; borrowAmount;
CalleeData memory calleeData = abi.decode(data, (CalleeData));
address declaredCaller = getBorrowable(calleeData.underlying, calleeData.borrowableIndex);
require(sender == address(this), "ImpermaxRouter: SENDER_NOT_ROUTER");
require(msg.sender == declaredCaller, "ImpermaxRouter: UNAUTHORIZED_CALLER");
if (calleeData.callType == CallType.ADD_LIQUIDITY_AND_MINT) {
AddLiquidityAndMintCalldata memory d = abi.decode(calleeData.data, (AddLiquidityAndMintCalldata));
_addLiquidityAndMint(calleeData.underlying, d.amountA, d.amountB, d.to);
}
else if (calleeData.callType == CallType.BORROWB) {
BorrowBCalldata memory d = abi.decode(calleeData.data, (BorrowBCalldata));
address borrowableB = getBorrowable(calleeData.underlying, 1);
IBorrowable(borrowableB).borrow(d.borrower, d.receiver, d.borrowAmount, d.data);
}
else revert();
}
function impermaxRedeem(address sender, uint redeemAmount, bytes calldata data) external virtual override {
redeemAmount;
CalleeData memory calleeData = abi.decode(data, (CalleeData));
address declaredCaller = getCollateral(calleeData.underlying);
require(sender == address(this), "ImpermaxRouter: SENDER_NOT_ROUTER");
require(msg.sender == declaredCaller, "ImpermaxRouter: UNAUTHORIZED_CALLER");
if (calleeData.callType == CallType.REMOVE_LIQ_AND_REPAY) {
RemoveLiqAndRepayCalldata memory d = abi.decode(calleeData.data, (RemoveLiqAndRepayCalldata));
_removeLiqAndRepay(calleeData.underlying, d.borrower, d.redeemTokens, d.redeemAmount, d.amountAMin, d.amountBMin);
}
else revert();
}
function _permit(
address poolToken,
uint amount,
uint deadline,
bytes memory permitData
) internal virtual {
if (permitData.length == 0) return;
(bool approveMax, uint8 v, bytes32 r, bytes32 s) = abi.decode(permitData, (bool, uint8, bytes32, bytes32));
uint value = approveMax ? uint(-1) : amount;
IPoolToken(poolToken).permit(msg.sender, address(this), value, deadline, v, r, s);
}
function _borrowPermit(
address borrowable,
uint amount,
uint deadline,
bytes memory permitData
) internal virtual {
if (permitData.length == 0) return;
(bool approveMax, uint8 v, bytes32 r, bytes32 s) = abi.decode(permitData, (bool, uint8, bytes32, bytes32));
uint value = approveMax ? uint(-1) : amount;
IBorrowable(borrowable).borrowPermit(msg.sender, address(this), value, deadline, v, r, s);
}
function _optimalLiquidity(
address uniswapV2Pair,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin
) public virtual view returns (uint amountA, uint amountB) {
(uint reserveA, uint reserveB,) = IUniswapV2Pair(uniswapV2Pair).getReserves();
uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
if (amountBOptimal <= amountBDesired) {
require(amountBOptimal >= amountBMin, "ImpermaxRouter: INSUFFICIENT_B_AMOUNT");
(amountA, amountB) = (amountADesired, amountBOptimal);
} else {
uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
assert(amountAOptimal <= amountADesired);
require(amountAOptimal >= amountAMin, "ImpermaxRouter: INSUFFICIENT_A_AMOUNT");
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
function isStakedLPToken(address underlying) public virtual override view returns(bool) {
try IStakedLPToken01(underlying).isStakedLPToken() returns (bool result) {
return result;
} catch {
return false;
}
}
function getUniswapV2Pair(address underlying) public virtual override view returns (address) {
try IStakedLPToken01(underlying).underlying() returns (address u) {
if (u != address(0)) return u;
return underlying;
} catch {
return underlying;
}
}
function getBorrowable(address underlying, uint8 index) public virtual override view returns (address borrowable) {
require(index < 2, "ImpermaxRouter: INDEX_TOO_HIGH");
(,,,address borrowable0, address borrowable1) = IFactory(factory).getLendingPool(underlying);
return index == 0 ? borrowable0 : borrowable1;
}
function getCollateral(address underlying) public virtual override view returns (address collateral) {
(,,collateral,,) = IFactory(factory).getLendingPool(underlying);
}
function getLendingPool(address underlying) public virtual override view returns (address collateral, address borrowableA, address borrowableB) {
collateral = getCollateral(underlying);
borrowableA = getBorrowable(underlying, 0);
borrowableB = getBorrowable(underlying, 1);
}
}