I am trying to build a Uniswap Liquidity pool contract, I have written the Below code for it.
CODE:
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
import '@uniswap/v3-core/contracts/libraries/TickMath.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
import '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol';
import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
import '@uniswap/v3-periphery/contracts/base/LiquidityManagement.sol';
contract Liquidity is IERC721Receiver
{
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F ;
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
uint24 public constant poolfee = 3000;
//Address of the INonfungiblePositionManager interface.
INonfungiblePositionManager public immutable nonfungiblePositionManager =
INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
// Every time a user adds liquidty to the pool, he receives an NFT. And all the data regarding the Deposit is stored in the NFT.
// NFT is created to represent a specific share or ownership stake in the liquidity pool. For example 10% share of liquidity pool.
// Struct to hold the deposit information of a user.
struct Deposit
{
address owner;
uint128 liquidity;
address token0;
address token1;
}
//Mapping which points to the Deposit Struct. To store deposit information of each TokenID user.
mapping(uint256 => Deposit) public deposit; // TokenID --> Deposit Structure.
// To allow NFT's to be received by this contct we define IERC721 Receiver, and inside that we call CreateDeposit function.
function onERC721Received (address operator, address, uint256 tokenId, bytes calldata) external override returns (bytes4)
{
// get position information
_createDeposit(operator, tokenId);
return this.onERC721Received.selector;
}
// Function used to set the mapping deposit whenever someone adds liquidity using the mint function.
function _createDeposit(address owner, uint256 tokenID) internal
{
// accessing values from the position function of interface INonfungilePositionManager.
(, , address token0, address token1, , , , uint128 liquidity, , , , ) = nonfungiblePositionManager.positions(tokenID);
deposit[tokenID] = Deposit({owner: owner,liquidity: liquidity,token0: token0, token1: token1});
}
// Function to mint new tokens into the liquidity pool.
// Function to add liquidity to the pool.
// Every time a user adds liquidty to the pool, he receives an NFT. And all the data regarding the Deposit is stored in the NFT.
// NFT is created to represent a specific share or ownership stake in the liquidity pool. For example 10% share of liquidity pool.
function mintNewPosition() external returns(uint256 tokenID, uint128 liquidity, uint256 amount0, uint256 amount1)
{
//User has to transfer the tokens to this contract before calling the mint function.
uint256 amount0ToMint = 100 * 1e18; // amount of token0 we want to add in the Liquidity Pool.
uint256 amount1ToMint = 100 * 1e18; // amount of token1 we want to add in the Liquidity Pool.
//Transfer Tokens from User to contract Balances
TransferHelper.safeTransferFrom(DAI, msg.sender, address(this), amount0ToMint);
TransferHelper.safeTransferFrom(USDC, msg.sender, address(this), amount1ToMint);
//Approve the nonfungiblePositionManger contract to use the tokens.
TransferHelper.safeApprove(DAI,address(nonfungiblePositionManager), amount0ToMint);
TransferHelper.safeApprove(USDC,address(nonfungiblePositionManager), amount1ToMint);
// Populating the Mint parameters to call the mint function.
INonfungiblePositionManager.MintParams memory params =
INonfungiblePositionManager.MintParams({
token0: DAI, // Token0 to Mint
token1: USDC, // Token1 to Mint
fee: poolfee, // pool fee of the two tokens.
tickLower: TickMath.MIN_TICK, // Defines the lower price range in which we provided liquidity. Here its been set to MIN_TICK and MAX_Tick as liquidity is distributed to entire range of pool.
tickUpper: TickMath.MAX_TICK, // Defines the upper price range in which we provided liquidity.
amount0Desired: amount0ToMint, // Token0 to Mint
amount1Desired: amount1ToMint, // Token1 to Mint
amount0Min: 0, // Min Token0 required to Mint.
amount1Min: 0, // Min Token1 required to Mint.
recipient: address(this), // Address to receive the Mint tokens.
deadline: block.timestamp // Till when the mint should complete.
});
// Keep in mind that the pool defined by DAI/USDC and fee tier 0.3% must already be created and initialized in order to mint.
// Mint function created an new NFT. Every time liquidity is added a new NFT is minted and given to the Owner.
// The Mint function returns the newly created NFT's token ID.
(tokenID, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(params);
//Now we add the information about the new added liquidity in the deposit mapping we created.
_createDeposit(msg.sender, tokenID);
//Once mint is completed we remove the allowace of NonfungiblePositionManger Contract.
//We return the leftover tokens to the user.
if(amount0 < amount0ToMint)
{
TransferHelper.safeApprove(DAI, address(nonfungiblePositionManager), 0);
uint256 Refund0 = amount0ToMint - amount0;
// Before calling the mint function the user has to transfer the amount of token to add.
// But if some of those tokens are left and not added to the pool they will br returned to the user. More clarification on this.
TransferHelper.safeTransfer(DAI, msg.sender, Refund0);
}
if (amount1 < amount1ToMint)
{
TransferHelper.safeApprove(USDC, address(nonfungiblePositionManager), 0);
uint256 refund1 = amount1ToMint - amount1;
TransferHelper.safeTransfer(USDC, msg.sender, refund1);
}
}
//Function to collect the reward for providing funds into the liquidity pool.
//NFT claimed by the user during providing liquidity is burned or taken from the users account.
//The contract must have the NFT before the user can collect the reward.
function CollectAllfee(uint256 TokenID) external returns (uint256 amount0, uint256 amount1)
{
//SafeTransferFrom is called: Callers NFT must be transferred to this address.
nonfungiblePositionManager.safeTransferFrom(msg.sender, address(this),TokenID);
//populate the collectfee params from the Position Manager.
INonfungiblePositionManager.CollectParams memory params =
INonfungiblePositionManager.CollectParams({
tokenId: TokenID, //Token ID of the NFT
recipient: address(this), //Address who will receive the rewards.
amount0Max: type(uint128).max, //Maximum amount of Token0 to collect. Set to Max so that we collect all the fees.
amount1Max: type(uint128).max //Maximum amount of Token1 to collect. Set to Max so that we collect all the fees.
});
// Call the collectfee function from Position Manager and pass the Params.
(amount0, amount1) = nonfungiblePositionManager.collect(params);
// Send the fees back to the owner.
_sendToOwner(TokenID, amount0, amount1);
}
function _sendToOwner(uint256 tokenId, uint256 amount0, uint256 amount1) internal
{
// get owner of contract.
address owner = deposit[tokenId].owner;
address token0 = deposit[tokenId].token0;
address token1 = deposit[tokenId].token1;
// send collected fees to owner.
TransferHelper.safeTransfer(token0, owner, amount0);
TransferHelper.safeTransfer(token1, owner, amount1);
}
// When the owner collects the Reward the NFT is transferred to the contract address. This function returns it back.
function retrieveNFT(uint256 tokenId) external
{
// must be the owner of the NFT.
require(msg.sender == deposit[tokenId].owner, 'Not the owner');
// transfer ownership to original owner.
nonfungiblePositionManager.safeTransferFrom(address(this), msg.sender, tokenId);
//remove information related to tokenId.
delete deposit[tokenId];
}
}
I am getting the following error when I compile the contract.
Stack too deep when compiling inline assembly: Variable headStart is 1 slot(s) too deep inside the stack.
Can anyone suggest any possible solutions? Thank You!!!