编译器
0.8.23+commit.f704f362
文件 1 的 24:AccessControl.sol
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
文件 2 的 24:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 3 的 24:Common.sol
pragma solidity ^0.8.0;
import "v3-periphery/interfaces/external/IWETH9.sol";
import "v3-periphery/interfaces/INonfungiblePositionManager.sol" as univ3;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "v3-core/libraries/FullMath.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
interface INonfungiblePositionManager is univ3.INonfungiblePositionManager {
struct AlgebraV1MintParams {
address token0;
address token1;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
function mint(AlgebraV1MintParams calldata params)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
function WNativeToken() external view returns (address);
}
abstract contract Common is AccessControl, Pausable {
using Address for address;
bytes32 public constant WITHDRAWER_ROLE = keccak256("WITHDRAWER_ROLE");
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
uint256 internal constant Q64 = 2 ** 64;
uint256 internal constant Q96 = 2 ** 96;
error SelfSend();
error NotSupportedAction();
error NotSupportedProtocol();
error SameToken();
error AmountError();
error SlippageError();
error CollectError();
error TransferError();
error EtherSendFailed();
error TooMuchEtherSent();
error NoEtherToken();
error NotWETH();
error TooMuchFee();
error AlreadyInitialised();
error GetPositionFailed();
error NoFees();
struct DeductFeesEventData {
address token0;
address token1;
address token2;
uint256 amount0;
uint256 amount1;
uint256 amount2;
uint256 feeAmount0;
uint256 feeAmount1;
uint256 feeAmount2;
uint64 feeX64;
FeeType feeType;
}
event CompoundFees(address indexed nfpm, uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
event DeductFees(address indexed nfpm, uint256 indexed tokenId, address indexed userAddress, DeductFeesEventData data);
event ChangeRange(address indexed nfpm, uint256 indexed tokenId, uint256 newTokenId, uint256 newLiquidity, uint256 token0Added, uint256 token1Added);
event WithdrawAndCollectAndSwap(address indexed nfpm, uint256 indexed tokenId, address token, uint256 amount);
event Swap(address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut);
event SwapAndMint(address indexed nfpm, uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
event SwapAndIncreaseLiquidity(address indexed nfpm, uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
address public swapRouter;
mapping (FeeType=>uint64) _maxFeeX64;
constructor() {
_maxFeeX64[FeeType.GAS_FEE] = 1844674407370955264;
_maxFeeX64[FeeType.PROTOCOL_FEE] = 1844674407370955264;
}
bool private _initialized = false;
function initialize(address router, address admin, address withdrawer) public virtual {
if (_initialized) {
revert AlreadyInitialised();
}
if (withdrawer == address(0)) {
revert();
}
_grantRole(ADMIN_ROLE, admin);
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(WITHDRAWER_ROLE, withdrawer);
_grantRole(DEFAULT_ADMIN_ROLE, withdrawer);
swapRouter = router;
_initialized = true;
}
enum Protocol {
UNI_V3,
ALGEBRA_V1
}
enum FeeType {
GAS_FEE,
PROTOCOL_FEE
}
struct SwapAndMintParams {
Protocol protocol;
INonfungiblePositionManager nfpm;
IERC20 token0;
IERC20 token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint64 protocolFeeX64;
uint256 amount0;
uint256 amount1;
uint256 amount2;
address recipient;
uint256 deadline;
IERC20 swapSourceToken;
uint256 amountIn0;
uint256 amountOut0Min;
bytes swapData0;
uint256 amountIn1;
uint256 amountOut1Min;
bytes swapData1;
uint256 amountAddMin0;
uint256 amountAddMin1;
}
struct SwapAndIncreaseLiquidityParams {
Protocol protocol;
INonfungiblePositionManager nfpm;
uint256 tokenId;
uint256 amount0;
uint256 amount1;
uint256 amount2;
address recipient;
uint256 deadline;
IERC20 swapSourceToken;
uint256 amountIn0;
uint256 amountOut0Min;
bytes swapData0;
uint256 amountIn1;
uint256 amountOut1Min;
bytes swapData1;
uint256 amountAddMin0;
uint256 amountAddMin1;
uint64 protocolFeeX64;
}
struct ReturnLeftoverTokensParams{
IWETH9 weth;
address to;
IERC20 token0;
IERC20 token1;
uint256 total0;
uint256 total1;
uint256 added0;
uint256 added1;
bool unwrap;
}
struct DecreaseAndCollectFeesParams {
INonfungiblePositionManager nfpm;
address userAddress;
IERC20 token0;
IERC20 token1;
uint256 tokenId;
uint128 liquidity;
uint256 deadline;
uint256 token0Min;
uint256 token1Min;
bool compoundFees;
}
struct DeductFeesParams {
uint256 amount0;
uint256 amount1;
uint256 amount2;
uint64 feeX64;
FeeType feeType;
address nfpm;
uint256 tokenId;
address userAddress;
address token0;
address token1;
address token2;
}
function withdrawERC20(IERC20[] calldata tokens, address to) external onlyRole(WITHDRAWER_ROLE) {
uint count = tokens.length;
for(uint i = 0; i < count; ++i) {
uint256 balance = tokens[i].balanceOf(address(this));
if (balance > 0) {
SafeERC20.safeTransfer(tokens[i], to, balance);
}
}
}
function withdrawNative(address to) external onlyRole(WITHDRAWER_ROLE) {
uint256 nativeBalance = address(this).balance;
if (nativeBalance > 0) {
payable(to).transfer(nativeBalance);
}
}
function withdrawERC721(INonfungiblePositionManager nfpm, uint256 tokenId, address to) external onlyRole(WITHDRAWER_ROLE) {
nfpm.transferFrom(address(this), to, tokenId);
}
function _prepareSwap(IWETH9 weth, IERC20 token0, IERC20 token1, IERC20 otherToken, uint256 amount0, uint256 amount1, uint256 amountOther) internal {
uint256 amountAdded0;
uint256 amountAdded1;
uint256 amountAddedOther;
if (msg.value != 0) {
weth.deposit{ value: msg.value }();
if (address(weth) == address(token0)) {
amountAdded0 = msg.value;
if (amountAdded0 > amount0) {
revert TooMuchEtherSent();
}
} else if (address(weth) == address(token1)) {
amountAdded1 = msg.value;
if (amountAdded1 > amount1) {
revert TooMuchEtherSent();
}
} else if (address(weth) == address(otherToken)) {
amountAddedOther = msg.value;
if (amountAddedOther > amountOther) {
revert TooMuchEtherSent();
}
} else {
revert NoEtherToken();
}
}
if (amount0 > amountAdded0) {
uint256 balanceBefore = token0.balanceOf(address(this));
SafeERC20.safeTransferFrom(token0, msg.sender, address(this), amount0 - amountAdded0);
uint256 balanceAfter = token0.balanceOf(address(this));
if (balanceAfter - balanceBefore != amount0 - amountAdded0) {
revert TransferError();
}
}
if (amount1 > amountAdded1) {
uint256 balanceBefore = token1.balanceOf(address(this));
SafeERC20.safeTransferFrom(token1, msg.sender, address(this), amount1 - amountAdded1);
uint256 balanceAfter = token1.balanceOf(address(this));
if (balanceAfter - balanceBefore != amount1 - amountAdded1) {
revert TransferError();
}
}
if (amountOther > amountAddedOther && address(otherToken) != address(0) && token0 != otherToken && token1 != otherToken) {
uint256 balanceBefore = otherToken.balanceOf(address(this));
SafeERC20.safeTransferFrom(otherToken, msg.sender, address(this), amountOther - amountAddedOther);
uint256 balanceAfter = otherToken.balanceOf(address(this));
if (balanceAfter - balanceBefore != amountOther - amountAddedOther) {
revert TransferError();
}
}
}
struct SwapAndMintResult {
uint256 tokenId;
uint128 liquidity;
uint256 added0;
uint256 added1;
}
function _swapAndMint(SwapAndMintParams memory params, bool unwrap) internal returns (SwapAndMintResult memory result) {
(uint256 total0, uint256 total1) = _swapAndPrepareAmounts(params, unwrap);
if (params.protocol == Protocol.UNI_V3) {
(result.tokenId,result.liquidity,result.added0,result.added1) = _mintUniv3(params.nfpm, univ3.INonfungiblePositionManager.MintParams(
address(params.token0),
address(params.token1),
params.fee,
params.tickLower,
params.tickUpper,
total0,
total1,
params.amountAddMin0,
params.amountAddMin1,
address(this),
params.deadline
));
} else if (params.protocol == Protocol.ALGEBRA_V1) {
(result.tokenId,result.liquidity,result.added0,result.added1) = _mintAlgebraV1(params.nfpm, univ3.INonfungiblePositionManager.MintParams(
address(params.token0),
address(params.token1),
params.fee,
params.tickLower,
params.tickUpper,
total0,
total1,
params.amountAddMin0,
params.amountAddMin1,
address(this),
params.deadline
));
} else {
revert NotSupportedProtocol();
}
params.nfpm.transferFrom(address(this), params.recipient, result.tokenId);
emit SwapAndMint(address(params.nfpm), result.tokenId, result.liquidity, result.added0, result.added1);
_returnLeftoverTokens(ReturnLeftoverTokensParams(_getWeth9(params.nfpm, params.protocol), params.recipient, params.token0, params.token1, total0, total1, result.added0, result.added1, unwrap));
}
function _mintUniv3(INonfungiblePositionManager nfpm, INonfungiblePositionManager.MintParams memory params) internal returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
) {
return nfpm.mint(params);
}
function _mintAlgebraV1(INonfungiblePositionManager nfpm, INonfungiblePositionManager.MintParams memory params) internal returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
) {
INonfungiblePositionManager.AlgebraV1MintParams memory mintParams =
INonfungiblePositionManager.AlgebraV1MintParams(
params.token0,
params.token1,
params.tickLower,
params.tickUpper,
params.amount0Desired,
params.amount1Desired,
params.amount0Min,
params.amount1Min,
address(this),
params.deadline
);
return nfpm.mint(mintParams);
}
struct SwapAndIncreaseLiquidityResult {
uint128 liquidity;
uint256 added0;
uint256 added1;
uint256 feeAmount0;
uint256 feeAmount1;
}
function _swapAndIncrease(SwapAndIncreaseLiquidityParams memory params, IERC20 token0, IERC20 token1, bool unwrap) internal returns (SwapAndIncreaseLiquidityResult memory result) {
(uint256 total0, uint256 total1) = _swapAndPrepareAmounts(
SwapAndMintParams(params.protocol, params.nfpm, token0, token1, 0, 0, 0, 0, params.amount0, params.amount1, 0, params.recipient, params.deadline, params.swapSourceToken, params.amountIn0, params.amountOut0Min, params.swapData0, params.amountIn1, params.amountOut1Min, params.swapData1, params.amountAddMin0, params.amountAddMin1), unwrap);
INonfungiblePositionManager.IncreaseLiquidityParams memory increaseLiquidityParams =
univ3.INonfungiblePositionManager.IncreaseLiquidityParams(
params.tokenId,
total0,
total1,
params.amountAddMin0,
params.amountAddMin1,
params.deadline
);
(result.liquidity, result.added0, result.added1) = params.nfpm.increaseLiquidity(increaseLiquidityParams);
emit SwapAndIncreaseLiquidity(address(params.nfpm), params.tokenId, result.liquidity, result.added0, result.added1);
IWETH9 weth = _getWeth9(params.nfpm, params.protocol);
_returnLeftoverTokens(ReturnLeftoverTokensParams(weth, params.recipient, token0, token1, total0, total1, result.added0, result.added1, unwrap));
}
function _swapAndPrepareAmounts(SwapAndMintParams memory params, bool unwrap) internal returns (uint256 total0, uint256 total1) {
if (params.swapSourceToken == params.token0) {
if (params.amount0 < params.amountIn1) {
revert AmountError();
}
(uint256 amountInDelta, uint256 amountOutDelta) = _swap(params.token0, params.token1, params.amountIn1, params.amountOut1Min, params.swapData1);
total0 = params.amount0 - amountInDelta;
total1 = params.amount1 + amountOutDelta;
} else if (params.swapSourceToken == params.token1) {
if (params.amount1 < params.amountIn0) {
revert AmountError();
}
(uint256 amountInDelta, uint256 amountOutDelta) = _swap(params.token1, params.token0, params.amountIn0, params.amountOut0Min, params.swapData0);
total1 = params.amount1 - amountInDelta;
total0 = params.amount0 + amountOutDelta;
} else if (address(params.swapSourceToken) != address(0)) {
(uint256 amountInDelta0, uint256 amountOutDelta0) = _swap(params.swapSourceToken, params.token0, params.amountIn0, params.amountOut0Min, params.swapData0);
(uint256 amountInDelta1, uint256 amountOutDelta1) = _swap(params.swapSourceToken, params.token1, params.amountIn1, params.amountOut1Min, params.swapData1);
total0 = params.amount0 + amountOutDelta0;
total1 = params.amount1 + amountOutDelta1;
uint256 leftOver = params.amountIn0 + params.amountIn1 - amountInDelta0 - amountInDelta1;
if (leftOver != 0) {
IWETH9 weth = _getWeth9(params.nfpm, params.protocol);
_transferToken(weth, params.recipient, params.swapSourceToken, leftOver, unwrap);
}
} else {
total0 = params.amount0;
total1 = params.amount1;
}
if (total0 != 0) {
_safeResetAndApprove(params.token0, address(params.nfpm), total0);
}
if (total1 != 0) {
_safeResetAndApprove(params.token1, address(params.nfpm), total1);
}
}
function _returnLeftoverTokens(ReturnLeftoverTokensParams memory params) internal {
uint256 left0 = params.total0 - params.added0;
uint256 left1 = params.total1 - params.added1;
if (left0 != 0) {
_transferToken(params.weth, params.to, params.token0, left0, params.unwrap);
}
if (left1 != 0) {
_transferToken(params.weth, params.to, params.token1, left1, params.unwrap);
}
}
function _transferToken(IWETH9 weth, address to, IERC20 token, uint256 amount, bool unwrap) internal {
if (address(weth) == address(token) && unwrap) {
weth.withdraw(amount);
(bool sent, ) = to.call{value: amount}("");
if (!sent) {
revert EtherSendFailed();
}
} else {
SafeERC20.safeTransfer(token, to, amount);
}
}
function _swap(IERC20 tokenIn, IERC20 tokenOut, uint256 amountIn, uint256 amountOutMin, bytes memory swapData) internal returns (uint256 amountInDelta, uint256 amountOutDelta) {
if (amountIn != 0 && swapData.length != 0 && address(tokenOut) != address(0)) {
uint256 balanceInBefore = tokenIn.balanceOf(address(this));
uint256 balanceOutBefore = tokenOut.balanceOf(address(this));
_safeApprove(tokenIn, swapRouter, amountIn);
(bool success,) = swapRouter.call(swapData);
if (!success) {
revert ("swap failed!");
}
_safeApprove(tokenIn, swapRouter, 0);
uint256 balanceInAfter = tokenIn.balanceOf(address(this));
uint256 balanceOutAfter = tokenOut.balanceOf(address(this));
amountInDelta = balanceInBefore - balanceInAfter;
amountOutDelta = balanceOutAfter - balanceOutBefore;
if (amountOutDelta < amountOutMin) {
revert SlippageError();
}
emit Swap(address(tokenIn), address(tokenOut), amountInDelta, amountOutDelta);
}
}
function _decreaseLiquidity(INonfungiblePositionManager nfpm, uint256 tokenId, uint128 liquidity, uint256 deadline, uint256 token0Min, uint256 token1Min) internal returns (uint256 amount0, uint256 amount1) {
if (liquidity != 0) {
(amount0, amount1) = nfpm.decreaseLiquidity(
univ3.INonfungiblePositionManager.DecreaseLiquidityParams(
tokenId,
liquidity,
token0Min,
token1Min,
deadline
)
);
}
}
function _collectFees(INonfungiblePositionManager nfpm, uint256 tokenId, IERC20 token0, IERC20 token1, uint128 collectAmount0, uint128 collectAmount1) internal returns (uint256 amount0, uint256 amount1) {
uint256 balanceBefore0 = token0.balanceOf(address(this));
uint256 balanceBefore1 = token1.balanceOf(address(this));
(amount0, amount1) = nfpm.collect(
univ3.INonfungiblePositionManager.CollectParams(tokenId, address(this), collectAmount0, collectAmount1)
);
uint256 balanceAfter0 = token0.balanceOf(address(this));
uint256 balanceAfter1 = token1.balanceOf(address(this));
if (balanceAfter0 - balanceBefore0 != amount0) {
revert CollectError();
}
if (balanceAfter1 - balanceBefore1 != amount1) {
revert CollectError();
}
}
function _decreaseLiquidityAndCollectFees(DecreaseAndCollectFeesParams memory params) internal returns (uint256 amount0, uint256 amount1) {
(uint256 positionAmount0, uint256 positionAmount1) = _decreaseLiquidity(params.nfpm, params.tokenId, params.liquidity, params.deadline, params.token0Min, params.token1Min);
(amount0, amount1) = params.nfpm.collect(
univ3.INonfungiblePositionManager.CollectParams(
params.tokenId,
address(this),
type(uint128).max,
type(uint128).max
)
);
if (!params.compoundFees) {
{
uint256 fees0Return = amount0 - positionAmount0;
uint256 fees1Return = amount1 - positionAmount1;
if (fees0Return > 0) {
SafeERC20.safeTransfer(params.token0, params.userAddress, fees0Return);
}
if (fees1Return > 0) {
SafeERC20.safeTransfer(params.token1, params.userAddress, fees1Return);
}
}
amount0 = positionAmount0;
amount1 = positionAmount1;
}
}
function _getWeth9(INonfungiblePositionManager nfpm, Protocol protocol) view internal returns (IWETH9 weth) {
if (protocol == Protocol.UNI_V3) {
weth = IWETH9(nfpm.WETH9());
} else if (protocol == Protocol.ALGEBRA_V1) {
weth = IWETH9(nfpm.WNativeToken());
} else {
revert NotSupportedProtocol();
}
}
function _getPosition(INonfungiblePositionManager nfpm, Protocol protocol, uint256 tokenId) internal returns (address token0, address token1, uint128 liquidity, int24 tickLower, int24 tickUpper, uint24 fee) {
(bool success, bytes memory data) = address(nfpm).call(abi.encodeWithSignature("positions(uint256)", tokenId));
if (!success) {
revert GetPositionFailed();
}
if (protocol == Protocol.UNI_V3) {
(,, token0, token1, fee,tickLower, tickUpper, liquidity,,,,) = abi.decode(data, (uint96,address,address,address,uint24,int24,int24,uint128,uint256,uint256,uint128,uint128));
} else if (protocol == Protocol.ALGEBRA_V1) {
(,, token0, token1, tickLower, tickUpper, liquidity,,,,) = abi.decode(data, (uint96,address,address,address,int24,int24,uint128,uint256,uint256,uint128,uint128));
}
}
function _deductFees(DeductFeesParams memory params, bool emitEvent) internal returns(uint256 amount0Left, uint256 amount1Left, uint256 amount2Left, uint256 feeAmount0, uint256 feeAmount1, uint256 feeAmount2) {
if (params.feeX64 > _maxFeeX64[params.feeType]) {
revert TooMuchFee();
}
if (params.feeX64 == 0) {
revert NoFees();
}
if (params.amount0 > 0) {
feeAmount0 = FullMath.mulDiv(params.amount0, params.feeX64, Q64);
amount0Left = params.amount0 - feeAmount0;
}
if (params.amount1 > 0) {
feeAmount1 = FullMath.mulDiv(params.amount1, params.feeX64, Q64);
amount1Left = params.amount1 - feeAmount1;
}
if (params.amount2 > 0) {
feeAmount2 = FullMath.mulDiv(params.amount2, params.feeX64, Q64);
amount2Left = params.amount2 - feeAmount2;
}
if (emitEvent) {
emit DeductFees(address(params.nfpm), params.tokenId, params.userAddress, DeductFeesEventData(
params.token0, params.token1, params.token2,
params.amount0, params.amount1, params.amount2,
feeAmount0, feeAmount1, feeAmount2,
params.feeX64,
params.feeType
));
}
}
function pause() public onlyRole(ADMIN_ROLE) {
_pause();
}
function unpause() public onlyRole(ADMIN_ROLE) {
_unpause();
}
function setMaxFeeX64(FeeType feeType, uint64 feex64) external onlyRole(ADMIN_ROLE) {
_maxFeeX64[feeType] = feex64;
}
function getMaxFeeX64(FeeType feeType) public view returns (uint64) {
return _maxFeeX64[feeType];
}
function _safeResetAndApprove(IERC20 token, address _spender, uint256 _value) internal {
address(token).call(abi.encodeWithSelector(token.approve.selector, _spender, 0));
require(_value > 0);
_safeApprove(token, _spender, _value);
}
function _safeApprove(IERC20 token, address _spender, uint256 _value) internal {
bytes memory returnData = address(token).functionCall(abi.encodeWithSelector(token.approve.selector, _spender, _value));
if (returnData.length > 0) {
require(abi.decode(returnData, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 4 的 24:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 5 的 24:ERC165.sol
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 6 的 24:FullMath.sol
pragma solidity ^0.8.0;
library FullMath {
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
require(denominator > prod1);
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = (0 - denominator) & denominator;
assembly {
denominator := div(denominator, twos)
}
assembly {
prod0 := div(prod0, twos)
}
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inv = (3 * denominator) ^ 2;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
inv *= 2 - denominator * inv;
result = prod0 * inv;
return result;
}
}
function mulDivRoundingUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}
}
文件 7 的 24:IAccessControl.sol
pragma solidity ^0.8.20;
interface IAccessControl {
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
error AccessControlBadConfirmation();
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address callerConfirmation) external;
}
文件 8 的 24:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 9 的 24:IERC20.sol
pragma solidity ^0.8.20;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
文件 10 的 24:IERC20Permit.sol
pragma solidity ^0.8.20;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 11 的 24:IERC721.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 12 的 24:IERC721Enumerable.sol
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
interface IERC721Enumerable is IERC721 {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 13 的 24:IERC721Metadata.sol
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
文件 14 的 24:IERC721Permit.sol
pragma solidity >=0.7.5;
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
interface IERC721Permit is IERC721 {
function PERMIT_TYPEHASH() external pure returns (bytes32);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function permit(
address spender,
uint256 tokenId,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}
文件 15 的 24:IERC721Receiver.sol
pragma solidity ^0.8.20;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 16 的 24:INonfungiblePositionManager.sol
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol';
import './IPoolInitializer.sol';
import './IERC721Permit.sol';
import './IPeripheryPayments.sol';
import './IPeripheryImmutableState.sol';
import '../libraries/PoolAddress.sol';
interface INonfungiblePositionManager is
IPoolInitializer,
IPeripheryPayments,
IPeripheryImmutableState,
IERC721Metadata,
IERC721Enumerable,
IERC721Permit
{
event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1);
function positions(uint256 tokenId)
external
view
returns (
uint96 nonce,
address operator,
address token0,
address token1,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
function mint(MintParams calldata params)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
function increaseLiquidity(IncreaseLiquidityParams calldata params)
external
payable
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
function decreaseLiquidity(DecreaseLiquidityParams calldata params)
external
payable
returns (uint256 amount0, uint256 amount1);
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
function burn(uint256 tokenId) external payable;
}
文件 17 的 24:IPeripheryImmutableState.sol
pragma solidity >=0.5.0;
interface IPeripheryImmutableState {
function factory() external view returns (address);
function WETH9() external view returns (address);
}
文件 18 的 24:IPeripheryPayments.sol
pragma solidity >=0.7.5;
interface IPeripheryPayments {
function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;
function refundETH() external payable;
function sweepToken(
address token,
uint256 amountMinimum,
address recipient
) external payable;
}
文件 19 的 24:IPoolInitializer.sol
pragma solidity >=0.7.5;
pragma abicoder v2;
interface IPoolInitializer {
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable returns (address pool);
}
文件 20 的 24:IWETH9.sol
pragma solidity >=0.8.15;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
interface IWETH9 is IERC20 {
function deposit() external payable;
function withdraw(uint256) external;
}
文件 21 的 24:Pausable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Pausable is Context {
bool private _paused;
event Paused(address account);
event Unpaused(address account);
error EnforcedPause();
error ExpectedPause();
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 22 的 24:PoolAddress.sol
pragma solidity >=0.5.0;
library PoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH = 0xa598dd2fba360510c5a8f02f44423a4468e902df5857dbce3ca162a43a3a31ff;
struct PoolKey {
address token0;
address token1;
uint24 fee;
}
function getPoolKey(
address tokenA,
address tokenB,
uint24 fee
) internal pure returns (PoolKey memory) {
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
}
function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) {
require(key.token0 < key.token1);
pool = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encode(key.token0, key.token1, key.fee)),
POOL_INIT_CODE_HASH
)
)
)
)
);
}
}
文件 23 的 24:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 24 的 24:V3Utils.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "./Common.sol";
contract V3Utils is IERC721Receiver, Common {
enum WhatToDo {
CHANGE_RANGE,
WITHDRAW_AND_COLLECT_AND_SWAP,
COMPOUND_FEES
}
struct Instructions {
WhatToDo whatToDo;
Protocol protocol;
address targetToken;
uint256 amountRemoveMin0;
uint256 amountRemoveMin1;
uint256 amountIn0;
uint256 amountOut0Min;
bytes swapData0;
uint256 amountIn1;
uint256 amountOut1Min;
bytes swapData1;
int24 tickLower;
int24 tickUpper;
bool compoundFees;
uint128 liquidity;
uint256 amountAddMin0;
uint256 amountAddMin1;
uint256 deadline;
address recipient;
bool unwrap;
uint64 protocolFeeX64;
}
function execute(INonfungiblePositionManager _nfpm, uint256 tokenId, Instructions calldata instructions) whenNotPaused() external
{
_nfpm.safeTransferFrom(
msg.sender,
address(this),
tokenId,
abi.encode(instructions)
);
}
function onERC721Received(address, address from, uint256 tokenId, bytes calldata data) whenNotPaused() external override returns (bytes4) {
INonfungiblePositionManager nfpm = INonfungiblePositionManager(msg.sender);
if (from == address(this)) {
revert SelfSend();
}
Instructions memory instructions = abi.decode(data, (Instructions));
(address token0,address token1,,,,uint24 fee) = _getPosition(nfpm, instructions.protocol, tokenId);
(uint256 amount0, uint256 amount1) = _decreaseLiquidityAndCollectFees(DecreaseAndCollectFeesParams(nfpm, instructions.recipient, IERC20(token0), IERC20(token1), tokenId, instructions.liquidity, instructions.deadline, instructions.amountRemoveMin0, instructions.amountRemoveMin1, instructions.compoundFees));
if (instructions.protocolFeeX64 > 0) {
(amount0, amount1,,,,) = _deductFees(DeductFeesParams(amount0, amount1, 0, instructions.protocolFeeX64, FeeType.PROTOCOL_FEE, address(nfpm), tokenId, instructions.recipient, token0, token1, address(0)), true);
}
if (amount0 < instructions.amountIn0 || amount1 < instructions.amountIn1) {
revert AmountError();
}
if (instructions.whatToDo == WhatToDo.COMPOUND_FEES) {
SwapAndIncreaseLiquidityResult memory result;
if (instructions.targetToken == token0) {
result = _swapAndIncrease(SwapAndIncreaseLiquidityParams(instructions.protocol, nfpm, tokenId, amount0, amount1, 0, instructions.recipient, instructions.deadline, IERC20(token1), instructions.amountIn1, instructions.amountOut1Min, instructions.swapData1, 0, 0, "", instructions.amountAddMin0, instructions.amountAddMin1, 0), IERC20(token0), IERC20(token1), instructions.unwrap);
} else if (instructions.targetToken == token1) {
result = _swapAndIncrease(SwapAndIncreaseLiquidityParams(instructions.protocol, nfpm, tokenId, amount0, amount1, 0, instructions.recipient, instructions.deadline, IERC20(token0), 0, 0, "", instructions.amountIn0, instructions.amountOut0Min, instructions.swapData0, instructions.amountAddMin0, instructions.amountAddMin1, 0), IERC20(token0), IERC20(token1), instructions.unwrap);
} else {
result = _swapAndIncrease(SwapAndIncreaseLiquidityParams(instructions.protocol, nfpm, tokenId, amount0, amount1, 0, instructions.recipient, instructions.deadline, IERC20(address(0)), 0, 0, "", 0, 0, "", instructions.amountAddMin0, instructions.amountAddMin1, 0), IERC20(token0), IERC20(token1), instructions.unwrap);
}
emit CompoundFees(address(nfpm), tokenId, result.liquidity, result.added0, result.added1);
} else if (instructions.whatToDo == WhatToDo.CHANGE_RANGE) {
SwapAndMintResult memory result;
if (instructions.targetToken == token0) {
result = _swapAndMint(SwapAndMintParams(instructions.protocol, nfpm, IERC20(token0), IERC20(token1), fee, instructions.tickLower, instructions.tickUpper, 0, amount0, amount1, 0, instructions.recipient, instructions.deadline, IERC20(token1), instructions.amountIn1, instructions.amountOut1Min, instructions.swapData1, 0, 0, "", instructions.amountAddMin0, instructions.amountAddMin1), instructions.unwrap);
} else if (instructions.targetToken == token1) {
result = _swapAndMint(SwapAndMintParams(instructions.protocol, nfpm, IERC20(token0), IERC20(token1), fee, instructions.tickLower, instructions.tickUpper, 0, amount0, amount1, 0, instructions.recipient, instructions.deadline, IERC20(token0), 0, 0, "", instructions.amountIn0, instructions.amountOut0Min, instructions.swapData0, instructions.amountAddMin0, instructions.amountAddMin1), instructions.unwrap);
} else {
result = _swapAndMint(SwapAndMintParams(instructions.protocol, nfpm, IERC20(token0), IERC20(token1), fee, instructions.tickLower, instructions.tickUpper, 0, amount0, amount1, 0, instructions.recipient, instructions.deadline, IERC20(address(0)), 0, 0, "", 0, 0, "", instructions.amountAddMin0, instructions.amountAddMin1), instructions.unwrap);
}
emit ChangeRange(msg.sender, tokenId, result.tokenId, result.liquidity, result.added0, result.added1);
} else if (instructions.whatToDo == WhatToDo.WITHDRAW_AND_COLLECT_AND_SWAP) {
IWETH9 weth = _getWeth9(nfpm, instructions.protocol);
uint256 targetAmount;
if (token0 != instructions.targetToken) {
(uint256 amountInDelta, uint256 amountOutDelta) = _swap(IERC20(token0), IERC20(instructions.targetToken), amount0, instructions.amountOut0Min, instructions.swapData0);
if (amountInDelta < amount0) {
_transferToken(weth, instructions.recipient, IERC20(token0), amount0 - amountInDelta, instructions.unwrap);
}
targetAmount += amountOutDelta;
} else {
targetAmount += amount0;
}
if (token1 != instructions.targetToken) {
(uint256 amountInDelta, uint256 amountOutDelta) = _swap(IERC20(token1), IERC20(instructions.targetToken), amount1, instructions.amountOut1Min, instructions.swapData1);
if (amountInDelta < amount1) {
_transferToken(weth, instructions.recipient, IERC20(token1), amount1 - amountInDelta, instructions.unwrap);
}
targetAmount += amountOutDelta;
} else {
targetAmount += amount1;
}
if (targetAmount != 0 && instructions.targetToken != address(0)) {
_transferToken(weth, instructions.recipient, IERC20(instructions.targetToken), targetAmount, instructions.unwrap);
}
emit WithdrawAndCollectAndSwap(address(nfpm), tokenId, instructions.targetToken, targetAmount);
} else {
revert NotSupportedAction();
}
nfpm.transferFrom(address(this), from, tokenId);
return IERC721Receiver.onERC721Received.selector;
}
struct SwapParams {
IWETH9 weth;
IERC20 tokenIn;
IERC20 tokenOut;
uint256 amountIn;
uint256 minAmountOut;
address recipient;
bytes swapData;
bool unwrap;
}
function swap(SwapParams calldata params) whenNotPaused() external payable returns (uint256 amountOut) {
if (params.tokenIn == params.tokenOut) {
revert SameToken();
}
_prepareSwap(params.weth, params.tokenIn, IERC20(address(0)), IERC20(address(0)), params.amountIn, 0, 0);
uint256 amountInDelta;
(amountInDelta, amountOut) = _swap(params.tokenIn, params.tokenOut, params.amountIn, params.minAmountOut, params.swapData);
if (amountOut != 0) {
_transferToken(params.weth, params.recipient, params.tokenOut, amountOut, params.unwrap);
}
uint256 leftOver = params.amountIn - amountInDelta;
if (leftOver != 0) {
_transferToken(params.weth, params.recipient, params.tokenIn, leftOver, params.unwrap);
}
}
function swapAndMint(SwapAndMintParams calldata params) whenNotPaused() external payable returns (SwapAndMintResult memory result) {
if (params.token0 == params.token1) {
revert SameToken();
}
IWETH9 weth = _getWeth9(params.nfpm, params.protocol);
_prepareSwap(weth, params.token0, params.token1, params.swapSourceToken, params.amount0, params.amount1, params.amount2);
SwapAndMintParams memory _params = params;
DeductFeesEventData memory eventData;
if (params.protocolFeeX64 > 0) {
uint256 feeAmount0;
uint256 feeAmount1;
uint256 feeAmount2;
(_params.amount0, _params.amount1, _params.amount2, feeAmount0, feeAmount1, feeAmount2) = _deductFees(DeductFeesParams(params.amount0, params.amount1, params.amount2, params.protocolFeeX64, FeeType.PROTOCOL_FEE, address(params.nfpm), 0, params.recipient, address(params.token0), address(params.token1), address(params.swapSourceToken)), false);
if (_params.swapSourceToken != _params.token0 && _params.swapSourceToken != _params.token1) {
if (_params.amountIn0 + _params.amountIn1 > _params.amount2) {
revert AmountError();
}
if (_params.amountIn0 + _params.amountIn1 < _params.amount2) {
uint256 leftOverAmount = _params.amount2 - (_params.amountIn0 + _params.amountIn1);
_transferToken(weth, msg.sender, _params.swapSourceToken, leftOverAmount, msg.value != 0);
}
}
eventData = DeductFeesEventData({
token0: address(params.token0),
token1: address(params.token1),
token2: address(params.swapSourceToken),
amount0: params.amount0,
amount1: params.amount1,
amount2: params.amount2,
feeAmount0: feeAmount0,
feeAmount1: feeAmount1,
feeAmount2: feeAmount2,
feeX64: params.protocolFeeX64,
feeType: FeeType.PROTOCOL_FEE
});
}
result = _swapAndMint(_params, msg.value != 0);
emit DeductFees(address(params.nfpm), result.tokenId, params.recipient, eventData);
}
function swapAndIncreaseLiquidity(SwapAndIncreaseLiquidityParams calldata params) whenNotPaused() external payable returns (SwapAndIncreaseLiquidityResult memory result) {
address owner = params.nfpm.ownerOf(params.tokenId);
require(owner == msg.sender, "sender is not owner of position");
(address token0,address token1,,,,) = _getPosition(params.nfpm, params.protocol, params.tokenId);
IWETH9 weth = _getWeth9(params.nfpm, params.protocol);
_prepareSwap(weth, IERC20(token0), IERC20(token1), params.swapSourceToken, params.amount0, params.amount1, params.amount2);
SwapAndIncreaseLiquidityParams memory _params = params;
if (params.protocolFeeX64 > 0) {
(_params.amount0, _params.amount1, _params.amount2,,,) = _deductFees(DeductFeesParams(params.amount0, params.amount1, params.amount2, params.protocolFeeX64, FeeType.PROTOCOL_FEE, address(params.nfpm), params.tokenId, params.recipient, token0, token1, address(params.swapSourceToken)), true);
if (address(_params.swapSourceToken) != token0 && address(_params.swapSourceToken) != token1) {
if (_params.amountIn0 + _params.amountIn1 > _params.amount2) {
revert AmountError();
}
if (_params.amountIn0 + _params.amountIn1 < _params.amount2) {
uint256 leftOverAmount = _params.amount2 - (_params.amountIn0 + _params.amountIn1);
_transferToken(weth, msg.sender, _params.swapSourceToken, leftOverAmount, msg.value != 0);
}
}
}
result = _swapAndIncrease(_params, IERC20(token0), IERC20(token1), msg.value != 0);
}
receive() external payable{}
}
{
"compilationTarget": {
"src/V3Utils.sol": "V3Utils"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@cryptoalgebra/AlgebraV1/=lib/AlgebraV1/",
":@openzeppelin/=lib/openzeppelin-contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@uniswap/v3-core/=lib/v3-core/",
":@uniswap/v3-periphery/=lib/v3-periphery/",
":AlgebraV1/=lib/AlgebraV1/src/",
":algebra-core/=lib/AlgebraV1/src/core/",
":algebra-periphery/=lib/AlgebraV1/src/periphery/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":v3-core/=lib/v3-core/contracts/",
":v3-periphery/=lib/v3-periphery/contracts/"
]
}
[{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AlreadyInitialised","type":"error"},{"inputs":[],"name":"AmountError","type":"error"},{"inputs":[],"name":"CollectError","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"EtherSendFailed","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"GetPositionFailed","type":"error"},{"inputs":[],"name":"NoEtherToken","type":"error"},{"inputs":[],"name":"NoFees","type":"error"},{"inputs":[],"name":"NotSupportedAction","type":"error"},{"inputs":[],"name":"NotSupportedProtocol","type":"error"},{"inputs":[],"name":"NotWETH","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SameToken","type":"error"},{"inputs":[],"name":"SelfSend","type":"error"},{"inputs":[],"name":"SlippageError","type":"error"},{"inputs":[],"name":"TooMuchEtherSent","type":"error"},{"inputs":[],"name":"TooMuchFee","type":"error"},{"inputs":[],"name":"TransferError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nfpm","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLiquidity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Added","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Added","type":"uint256"}],"name":"ChangeRange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nfpm","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"liquidity","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"CompoundFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nfpm","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"address","name":"token2","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"amount2","type":"uint256"},{"internalType":"uint256","name":"feeAmount0","type":"uint256"},{"internalType":"uint256","name":"feeAmount1","type":"uint256"},{"internalType":"uint256","name":"feeAmount2","type":"uint256"},{"internalType":"uint64","name":"feeX64","type":"uint64"},{"internalType":"enum Common.FeeType","name":"feeType","type":"uint8"}],"indexed":false,"internalType":"struct Common.DeductFeesEventData","name":"data","type":"tuple"}],"name":"DeductFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":true,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nfpm","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"liquidity","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"SwapAndIncreaseLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nfpm","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"liquidity","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"SwapAndMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nfpm","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawAndCollectAndSwap","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAWER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract INonfungiblePositionManager","name":"_nfpm","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"enum V3Utils.WhatToDo","name":"whatToDo","type":"uint8"},{"internalType":"enum Common.Protocol","name":"protocol","type":"uint8"},{"internalType":"address","name":"targetToken","type":"address"},{"internalType":"uint256","name":"amountRemoveMin0","type":"uint256"},{"internalType":"uint256","name":"amountRemoveMin1","type":"uint256"},{"internalType":"uint256","name":"amountIn0","type":"uint256"},{"internalType":"uint256","name":"amountOut0Min","type":"uint256"},{"internalType":"bytes","name":"swapData0","type":"bytes"},{"internalType":"uint256","name":"amountIn1","type":"uint256"},{"internalType":"uint256","name":"amountOut1Min","type":"uint256"},{"internalType":"bytes","name":"swapData1","type":"bytes"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"bool","name":"compoundFees","type":"bool"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"amountAddMin0","type":"uint256"},{"internalType":"uint256","name":"amountAddMin1","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"uint64","name":"protocolFeeX64","type":"uint64"}],"internalType":"struct V3Utils.Instructions","name":"instructions","type":"tuple"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum Common.FeeType","name":"feeType","type":"uint8"}],"name":"getMaxFeeX64","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"withdrawer","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum Common.FeeType","name":"feeType","type":"uint8"},{"internalType":"uint64","name":"feex64","type":"uint64"}],"name":"setMaxFeeX64","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IWETH9","name":"weth","type":"address"},{"internalType":"contract IERC20","name":"tokenIn","type":"address"},{"internalType":"contract IERC20","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"bool","name":"unwrap","type":"bool"}],"internalType":"struct V3Utils.SwapParams","name":"params","type":"tuple"}],"name":"swap","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"enum Common.Protocol","name":"protocol","type":"uint8"},{"internalType":"contract INonfungiblePositionManager","name":"nfpm","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"amount2","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"contract IERC20","name":"swapSourceToken","type":"address"},{"internalType":"uint256","name":"amountIn0","type":"uint256"},{"internalType":"uint256","name":"amountOut0Min","type":"uint256"},{"internalType":"bytes","name":"swapData0","type":"bytes"},{"internalType":"uint256","name":"amountIn1","type":"uint256"},{"internalType":"uint256","name":"amountOut1Min","type":"uint256"},{"internalType":"bytes","name":"swapData1","type":"bytes"},{"internalType":"uint256","name":"amountAddMin0","type":"uint256"},{"internalType":"uint256","name":"amountAddMin1","type":"uint256"},{"internalType":"uint64","name":"protocolFeeX64","type":"uint64"}],"internalType":"struct Common.SwapAndIncreaseLiquidityParams","name":"params","type":"tuple"}],"name":"swapAndIncreaseLiquidity","outputs":[{"components":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"added0","type":"uint256"},{"internalType":"uint256","name":"added1","type":"uint256"},{"internalType":"uint256","name":"feeAmount0","type":"uint256"},{"internalType":"uint256","name":"feeAmount1","type":"uint256"}],"internalType":"struct Common.SwapAndIncreaseLiquidityResult","name":"result","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"enum Common.Protocol","name":"protocol","type":"uint8"},{"internalType":"contract INonfungiblePositionManager","name":"nfpm","type":"address"},{"internalType":"contract IERC20","name":"token0","type":"address"},{"internalType":"contract IERC20","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint64","name":"protocolFeeX64","type":"uint64"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"uint256","name":"amount2","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"contract IERC20","name":"swapSourceToken","type":"address"},{"internalType":"uint256","name":"amountIn0","type":"uint256"},{"internalType":"uint256","name":"amountOut0Min","type":"uint256"},{"internalType":"bytes","name":"swapData0","type":"bytes"},{"internalType":"uint256","name":"amountIn1","type":"uint256"},{"internalType":"uint256","name":"amountOut1Min","type":"uint256"},{"internalType":"bytes","name":"swapData1","type":"bytes"},{"internalType":"uint256","name":"amountAddMin0","type":"uint256"},{"internalType":"uint256","name":"amountAddMin1","type":"uint256"}],"internalType":"struct Common.SwapAndMintParams","name":"params","type":"tuple"}],"name":"swapAndMint","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"added0","type":"uint256"},{"internalType":"uint256","name":"added1","type":"uint256"}],"internalType":"struct Common.SwapAndMintResult","name":"result","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"swapRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract INonfungiblePositionManager","name":"nfpm","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"withdrawNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]