forked from mico/idle_moloch
127 lines
5.0 KiB
Solidity
127 lines
5.0 KiB
Solidity
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
pragma solidity =0.7.6;
|
|
pragma abicoder v2;
|
|
|
|
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
|
|
import '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol';
|
|
|
|
import '../interfaces/IApproveAndCall.sol';
|
|
import './ImmutableState.sol';
|
|
|
|
/// @title Approve and Call
|
|
/// @notice Allows callers to approve the Uniswap V3 position manager from this contract,
|
|
/// for any token, and then make calls into the position manager
|
|
abstract contract ApproveAndCall is IApproveAndCall, ImmutableState {
|
|
function tryApprove(address token, uint256 amount) private returns (bool) {
|
|
(bool success, bytes memory data) =
|
|
token.call(abi.encodeWithSelector(IERC20.approve.selector, positionManager, amount));
|
|
return success && (data.length == 0 || abi.decode(data, (bool)));
|
|
}
|
|
|
|
/// @inheritdoc IApproveAndCall
|
|
function getApprovalType(address token, uint256 amount) external override returns (ApprovalType) {
|
|
// check existing approval
|
|
if (IERC20(token).allowance(address(this), positionManager) >= amount) return ApprovalType.NOT_REQUIRED;
|
|
|
|
// try type(uint256).max / type(uint256).max - 1
|
|
if (tryApprove(token, type(uint256).max)) return ApprovalType.MAX;
|
|
if (tryApprove(token, type(uint256).max - 1)) return ApprovalType.MAX_MINUS_ONE;
|
|
|
|
// set approval to 0 (must succeed)
|
|
require(tryApprove(token, 0));
|
|
|
|
// try type(uint256).max / type(uint256).max - 1
|
|
if (tryApprove(token, type(uint256).max)) return ApprovalType.ZERO_THEN_MAX;
|
|
if (tryApprove(token, type(uint256).max - 1)) return ApprovalType.ZERO_THEN_MAX_MINUS_ONE;
|
|
|
|
revert();
|
|
}
|
|
|
|
/// @inheritdoc IApproveAndCall
|
|
function approveMax(address token) external payable override {
|
|
require(tryApprove(token, type(uint256).max));
|
|
}
|
|
|
|
/// @inheritdoc IApproveAndCall
|
|
function approveMaxMinusOne(address token) external payable override {
|
|
require(tryApprove(token, type(uint256).max - 1));
|
|
}
|
|
|
|
/// @inheritdoc IApproveAndCall
|
|
function approveZeroThenMax(address token) external payable override {
|
|
require(tryApprove(token, 0));
|
|
require(tryApprove(token, type(uint256).max));
|
|
}
|
|
|
|
/// @inheritdoc IApproveAndCall
|
|
function approveZeroThenMaxMinusOne(address token) external payable override {
|
|
require(tryApprove(token, 0));
|
|
require(tryApprove(token, type(uint256).max - 1));
|
|
}
|
|
|
|
/// @inheritdoc IApproveAndCall
|
|
function callPositionManager(bytes memory data) public payable override returns (bytes memory result) {
|
|
bool success;
|
|
(success, result) = positionManager.call(data);
|
|
|
|
if (!success) {
|
|
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
|
|
if (result.length < 68) revert();
|
|
assembly {
|
|
result := add(result, 0x04)
|
|
}
|
|
revert(abi.decode(result, (string)));
|
|
}
|
|
}
|
|
|
|
function balanceOf(address token) private view returns (uint256) {
|
|
return IERC20(token).balanceOf(address(this));
|
|
}
|
|
|
|
/// @inheritdoc IApproveAndCall
|
|
function mint(MintParams calldata params) external payable override returns (bytes memory result) {
|
|
return
|
|
callPositionManager(
|
|
abi.encodeWithSelector(
|
|
INonfungiblePositionManager.mint.selector,
|
|
INonfungiblePositionManager.MintParams({
|
|
token0: params.token0,
|
|
token1: params.token1,
|
|
fee: params.fee,
|
|
tickLower: params.tickLower,
|
|
tickUpper: params.tickUpper,
|
|
amount0Desired: balanceOf(params.token0),
|
|
amount1Desired: balanceOf(params.token1),
|
|
amount0Min: params.amount0Min,
|
|
amount1Min: params.amount1Min,
|
|
recipient: params.recipient,
|
|
deadline: type(uint256).max // deadline should be checked via multicall
|
|
})
|
|
)
|
|
);
|
|
}
|
|
|
|
/// @inheritdoc IApproveAndCall
|
|
function increaseLiquidity(IncreaseLiquidityParams calldata params)
|
|
external
|
|
payable
|
|
override
|
|
returns (bytes memory result)
|
|
{
|
|
return
|
|
callPositionManager(
|
|
abi.encodeWithSelector(
|
|
INonfungiblePositionManager.increaseLiquidity.selector,
|
|
INonfungiblePositionManager.IncreaseLiquidityParams({
|
|
tokenId: params.tokenId,
|
|
amount0Desired: balanceOf(params.token0),
|
|
amount1Desired: balanceOf(params.token1),
|
|
amount0Min: params.amount0Min,
|
|
amount1Min: params.amount1Min,
|
|
deadline: type(uint256).max // deadline should be checked via multicall
|
|
})
|
|
)
|
|
);
|
|
}
|
|
}
|