Protocol Repo
Notes on V3 protocol contracts.
Overview
NFTX is a protocol for incentivizing liquid markets for illiquid Non-Fungible Tokens (NFTs). Users deposit their NFTs into an NFTX vault to mint a fungible ERC20 token (vToken). This vToken can be burned to redeem any NFT from the vault by paying a redeem fee in ETH. vTokens can also be used to earn yield by:
Depositing into InventoryStaking to earn ETH (from vault fees) + vTokens (from early withdrawal fees)
Pairing the vTokens with ETH and providing concentrated (or infinite-range) liquidity into the NFTX AMM to earn trading fees and additional ETH (from vault fees).
Core contracts:
NFTXVaultUpgradeableV3
NFTXVaultFactoryUpgradeableV3
NFTXFeeDistributorV3
NFTXInventoryStakingV3Upgradeable
UniswapV3FactoryUpgradeable
UniswapV3PoolUpgradeable
NonfungiblePositionManager
NFTXRouter
Zaps:
CreateVaultZap
MarketplaceUniversalRouterZap
MigratorZap
Core Contracts
1. NFTXVaultUpgradeableV3
Main operations:
i. mint
: Deposits NFTs into the vault in exchange for minting vault tokens.
ii. redeem
: Burns vault tokens in exchange for redeeming NFTs from the vault.
iii. swap
: Swaps an array of NFTs into a desired array of NFTs from the vault.
All of the above operations require the user to pay vault fees in ETH, calculated as a % of the ~20 min TWAP of the vToken from the AMM pool (with fee tier = FeeDistributor.rewardFeeTier()
). If the pool doesn't exist, no vault fees are charged.
The vault fees collected are sent to the NFTXFeeDistributorV3
in the same transaction to be distributed to inventory stakers and liquidity providers (LPs).
Users wishing to redeem (or swap out) a newly deposited NFT must pay an ETH "premium fee" in addition to ETH vault fees. The premium fee for an NFT is highest right after it is deposited and goes down exponentially until it reaches zero. The proceeds from a premium fee are shared between the original NFT depositor of the redeemed tokenId and the vault stakers/LPs. Additional features:
Flash-minting. The fee for flash-minting is paid in ETH and is the highest of the vault's mint, redeem, and swap fees. The ETH proceeds from the fee are sent to inventory stakers for the vault.
Using Eligibility modules to only allow certain tokenIds into the vault.
2. NFTXVaultFactoryUpgradeableV3
Allows for the deployment of Vaults as Beacon Proxies.
3. NFTXFeeDistributorV3
Allows distribution of WETH vault fees to multiple receivers, including inventory stakers and NFTX AMM liquidity providers in rewardFeeTier
pools.
4. NFTXInventoryStakingV3Upgradeable
Allows users to stake vTokens to create or increase xNFT positions. xNFTs earn fees in WETH and vToken, which are distributed proportionally based on vTokenShares.
NFTs can also be directly staked via Inventory, which internally mints vToken without deducting any vault fees. Because users can use this to game and avoid the mint fees, a redeem timelock is placed on the xNFT.
There is an option for stakers to "early withdraw" a position (while it is timelocked) by paying a % of the position's vToken as a penalty, which then gets distributed among the vault's stakers/LPs. This penalty begins at 100% and goes down linearly to zero over the duration of the timelock.
Stakers can collect and withdraw the WETH accumulated by their position.
During withdrawal/off-ramping, stakers can redeem NFTs from the vault with their underlying vToken balance. No vault fees are paid if (A) their xNFT position was initially created by depositing NFTs or (B) if it was made with vToken but a full timelock was enforced.
Users can combine multiple xNFT "child positions" into one "parent position" after all the timelocks have ended.
5. UniswapV3FactoryUpgradeable
Forked from Uniswap V3, this contract was converted into an upgradeable contract and allows for the deployment of NFTX AMM pools as Create2 Beacon Proxies.
6. UniswapV3PoolUpgradeable
Forked from Uniswap V3, this contract includes an added function, distributeRewards
, that enables the FeeDistributer to pass on WETH vault fees to the LPs in the current tick, proportional to their share of the liquidity. If the pool's fee tier matches the global rewardFeeTier
, cardinality is set during the initialization of the pool so that it can provide a TWAP for the vault fee calculations. The costs of initializing the observation slots are forwarded to the first swappers.
7. NonfungiblePositionManager
Forked from Uniswap V3, this contract represents NFTX AMM positions as ERC721 NFTs, and allows the NFTXRouter to "timelock" positions from having liquidity withdrawn before the duration of the timelock is reached. While timelocked, a position can still have its liquidity increased, which further extends the timelock.
Vault fees accumulated as WETH are distributed the same way as normal LP fees.
8. NFTXRouter
This contract facilitates the addition and removal of liquidity on the NFTX AMM. It handles any minting or burning of vToken while adding or removing concentrated liquidity in one transaction.
NFTs can be directly converted into vToken liquidity via the NFTXRouter, which internally mints vTokens without charging any vault fees. As a result of vault fees not being charged for LPs, there is a timelock placed on new LP positions to make it more difficult for users to game the system.
During withdrawal/off-ramping, LPs have the option to redeem NFTs from the vault with their underlying vToken balance. No vault fees are paid if the position was created by depositing NFTs.
NFTs can be directly bought and sold from the pool in exchange for ETH via the AMM.
Zaps
1. CreateVaultZap
An amalgamation of vault creation steps merged and optimized into a single contract call. Allows for the creation of a new Vault, minting vTokens in exchange for NFTs, deploying a new NFTX AMM pool, depositing the minted vToken and ETH into the AMM pool to mint a liquidity position NFT, and depositing the remaining vTokens into inventory staking to mint an xNFT.
2. MarketplaceUniversalRouterZap
A Marketplace Zap that utilizes Uniswap's Universal Router to facilitate token swaps via Sushiswap and the NFTX AMM. Enables deducting creator royalties via ERC2981.
sell721
/sell1155
: sell NFT tokenIds for ETH.idsIn --{--mint-> [vault] -> vTokens --sell-> [UniversalRouter] --}-> ETH
swap721
/swap1155
: Swap an array of NFTs from the sender's wallet into a desired array of NFTs from a vault by paying ETH for vault fees.buyNFTsWithETH
: buy NFT tokenIds with ETH.ETH --{-sell-> [UniversalRouter] -> vTokens + ETH --redeem-> [vault] --}-> idsOut
buyNFTsWithERC20
: buy NFT tokenIds with ERC20.ERC20 --{-sell-> [UniversalRouter] -> ETH -> [UniversalRouter] -> vTokens + ETH --redeem-> [vault] --}-> idsOut
3. MigratorZap
Allows users to migrate their NFTX V2 positions to NFTX V3:
from V2 vTokens in Sushiswap liquidity to V3 vTokens in the NFTX AMM.
from V2 xTokens (vTokens in V2 inventory staking) to V3 vTokens inventory staked as xNFTs.
from V2 vTokens to V3 vTokens inventory staked as xNFTs.
Project Setup
We use Foundry for tests and Hardhat for contract deployments. Refer to installation instructions for Foundry here.
Copy .env.sample
into .env
and fill out the env variables.
Tests
Deployment
Tags are defined in the deploy script at the end like: func.tags = ["<tag>"]
Verify Contracts
yarn verify:goerli
Note: For some UniswapV3 contracts, there might be some errors while verifying, so run this for those contracts:
yarn verify:goerli --license "GPL-2.0" --force-license --solc-input
Last updated