Make MCQs for learning every single logical line in the following. (Please do it all without requiring me to inquire about it again.):
Uniswap V3 Liquidity Examples
Examples of minting new position, collect fees, increase and decrease liquidity.


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external returns (bytes4);
}

contract UniswapV3Liquidity is IERC721Receiver {
IERC20 private constant dai = IERC20(DAI);
IWETH private constant weth = IWETH(WETH);

int24 private constant MIN_TICK = -887272;
int24 private constant MAX_TICK = -MIN_TICK;
int24 private constant TICK_SPACING = 60;

INonfungiblePositionManager public nonfungiblePositionManager =
INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);

function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata
) external returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}

function mintNewPosition(
uint amount0ToAdd,
uint amount1ToAdd
) external returns (uint tokenId, uint128 liquidity, uint amount0, uint amount1) {
dai.transferFrom(msg.sender, address(this), amount0ToAdd);
weth.transferFrom(msg.sender, address(this), amount1ToAdd);

dai.approve(address(nonfungiblePositionManager), amount0ToAdd);
weth.approve(address(nonfungiblePositionManager), amount1ToAdd);

INonfungiblePositionManager.MintParams
memory params = INonfungiblePositionManager.MintParams({
token0: DAI,
token1: WETH,
fee: 3000,
tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING,
tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING,
amount0Desired: amount0ToAdd,
amount1Desired: amount1ToAdd,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp
});

(tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
params
);

if (amount0 < amount0ToAdd) {
dai.approve(address(nonfungiblePositionManager), 0);
uint refund0 = amount0ToAdd - amount0;
dai.transfer(msg.sender, refund0);
}
if (amount1 < amount1ToAdd) {
weth.approve(address(nonfungiblePositionManager), 0);
uint refund1 = amount1ToAdd - amount1;
weth.transfer(msg.sender, refund1);
}
}

function collectAllFees(
uint tokenId
) external returns (uint amount0, uint amount1) {
INonfungiblePositionManager.CollectParams
memory params = INonfungiblePositionManager.CollectParams({
tokenId: tokenId,
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
});

(amount0, amount1) = nonfungiblePositionManager.collect(params);
}

function increaseLiquidityCurrentRange(
uint tokenId,
uint amount0ToAdd,
uint amount1ToAdd
) external returns (uint128 liquidity, uint amount0, uint amount1) {
dai.transferFrom(msg.sender, address(this), amount0ToAdd);
weth.transferFrom(msg.sender, address(this), amount1ToAdd);

dai.approve(address(nonfungiblePositionManager), amount0ToAdd);
weth.approve(address(nonfungiblePositionManager), amount1ToAdd);

INonfungiblePositionManager.IncreaseLiquidityParams
memory params = INonfungiblePositionManager.IncreaseLiquidityParams({
tokenId: tokenId,
amount0Desired: amount0ToAdd,
amount1Desired: amount1ToAdd,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
});

(liquidity, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity(
params
);
}

function decreaseLiquidityCurrentRange(
uint tokenId,
uint128 liquidity
) external returns (uint amount0, uint amount1) {
INonfungiblePositionManager.DecreaseLiquidityParams
memory params = INonfungiblePositionManager.DecreaseLiquidityParams({
tokenId: tokenId,
liquidity: liquidity,
amount0Min: 0,
amount1Min: 0,
deadline: block.timestamp
});

(amount0, amount1) = nonfungiblePositionManager.decreaseLiquidity(params);
}
}

interface INonfungiblePositionManager {
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint amount0Desired;
uint amount1Desired;
uint amount0Min;
uint amount1Min;
address recipient;
uint deadline;
}

function mint(
MintParams calldata params
)
external
payable
returns (uint tokenId, uint128 liquidity, uint amount0, uint amount1);

struct IncreaseLiquidityParams {
uint tokenId;
uint amount0Desired;
uint amount1Desired;
uint amount0Min;
uint amount1Min;
uint deadline;
}

function increaseLiquidity(
IncreaseLiquidityParams calldata params
) external payable returns (uint128 liquidity, uint amount0, uint amount1);

struct DecreaseLiquidityParams {
uint tokenId;
uint128 liquidity;
uint amount0Min;
uint amount1Min;
uint deadline;
}

function decreaseLiquidity(
DecreaseLiquidityParams calldata params
) external payable returns (uint amount0, uint amount1);

struct CollectParams {
uint tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}

function collect(
CollectParams calldata params
) external payable returns (uint amount0, uint amount1);
}

interface IERC20 {
function totalSupply() external view returns (uint);

function balanceOf(address account) external view returns (uint);

function transfer(address recipient, uint amount) external returns (bool);

function allowance(address owner, address spender) external view returns (uint);

function approve(address spender, uint amount) external returns (bool);

function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);

event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}

interface IWETH is IERC20 {
function deposit() external payable;

function withdraw(uint amount) external;
}
Test with Foundry

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import “forge-std/Test.sol”;
import “forge-std/console.sol”;

import “…/src/UniswapV3Liquidity.sol”;

contract UniswapV3LiquidityTest is Test {
IWETH private constant weth = IWETH(WETH);
IERC20 private constant dai = IERC20(DAI);

address private constant DAI_WHALE = 0xe81D6f03028107A20DBc83176DA82aE8099E9C42;

UniswapV3Liquidity private uni = new UniswapV3Liquidity();

function setUp() public {
vm.prank(DAI_WHALE);
dai.transfer(address(this), 20 * 1e18);

weth.deposit{value: 2 * 1e18}();

dai.approve(address(uni), 20 * 1e18);
weth.approve(address(uni), 2 * 1e18);
}

function testLiquidity() public {
// Track total liquidity
uint128 liquidity;

// Mint new position
uint daiAmount = 10 * 1e18;
uint wethAmount = 1e18;

(uint tokenId, uint128 liquidityDelta, uint amount0, uint amount1) = uni
.mintNewPosition(daiAmount, wethAmount);
liquidity += liquidityDelta;

console.log(“— Mint new position —”);
console.log(“token id”, tokenId);
console.log(“liquidity”, liquidity);
console.log(“amount 0”, amount0);
console.log(“amount 1”, amount1);

// Collect fees
(uint fee0, uint fee1) = uni.collectAllFees(tokenId);

console.log(“— Collect fees —”);
console.log(“fee 0”, fee0);
console.log(“fee 1”, fee1);

// Increase liquidity
uint daiAmountToAdd = 5 * 1e18;
uint wethAmountToAdd = 0.5 * 1e18;

(liquidityDelta, amount0, amount1) = uni.increaseLiquidityCurrentRange(
tokenId,
daiAmountToAdd,
wethAmountToAdd
);
liquidity += liquidityDelta;

console.log(“— Increase liquidity —”);
console.log(“liquidity”, liquidity);
console.log(“amount 0”, amount0);
console.log(“amount 1”, amount1);

// Decrease liquidity
(amount0, amount1) = uni.decreaseLiquidityCurrentRange(tokenId, liquidity);
console.log(“— Decrease liquidity —”);
console.log(“amount 0”, amount0);
console.log(“amount 1”, amount1);
}
}

FORK_URL=https://eth-mainnet.g.alchemy.com/v2/613t3mfjTevdrCwDl28CVvuk6wSIxRPi
forge test -vv --gas-report --fork-url $FORK_URL --match-path test/UniswapV3Liquidity.test.sol