Marketplace Integration

These are the pseudo steps to integrate NFTX NFTs and liquidity into your application

  1. Retrieve all NFTX vaults from the subgraph, including global fees, and holdings

  2. Use the asset > id in the subgraph response to link the NFTX vault with the appropriate NFT collection in your application

  3. Verify that the vault has features > enableRedeems set to true so specific NFTs can be bought from the vault.

  4. Check if the vault uses default fees using usesFactoryFees , if true use the global fees to calculate the tokens required to buy an NFT, if false use the fees > redeemFee associated with the vault. To buy an NFT you need 1 token + vTokenToEth value of the fee.

  5. Using the NFTX Universal Router API, check the ETH cost to buy the required tokens. As the number of tokens required increases (i.e. the user adds more items to their basket) the average price of the NFT will increase due to price impact on the token buy.

  6. When the user buys, call the MarketplaceUniversalRouterZap to complete the purchase.

Contract Addresses

Sepolia Contract Addresses

"sepolia": {
    "CreateVaultZap": "0x50f69c6556338965bf77C16ADb462Fdb5bE01C09",
    "MarketplaceUniversalRouterZap": "0x5Af324A8c90966Bef28386A67c6bE0A16aA03c19",
    "NFTXFeeDistributorV3": "0x66EF5B4b6ee05639194844CE4867515665F14fED",
    "NFTXInventoryStakingV3Upgradeable": "0xfBFf0635f7c5327FD138E1EBa72BD9877A6a7C1C",
    "NFTXRouter": "0xD36ece08F76c50EC3F01db65BBc5Ef5Aa5fbE849",
    "nftxUniversalRouter": "0x12156cCA1958B6591CC49EaE03a5553458a4b424",
    "NFTXVaultFactoryUpgradeableV3": "0x31C56CaF49125043e80B4d3C7f8734f949d8178C",
    "NonfungiblePositionManager": "0x55BDc76262B1e6e791D0636A0bC61cee23CDFa87",
    "permit2": "0x000000000022d473030f116ddee9f6b43ac78ba3",
    "QuoterV2": "0xb8EB27ca4715f7A04228c6F83935379D1f5AbABd",
    "SwapRouter": "0xa7069da6a7e600e0348620484fD2B1f24E075d5f",
    "TickLens": "0xA13E04fAEe08E784A44C27e9E77Ca7a02D45BFd7",
    "UniswapV3FactoryUpgradeable": "0xDD2dce9C403f93c10af1846543870D065419E70b",
    "WETH": "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"
  }

New features of note in V3

There have been some updates to the way in which the NFTX V3 protocol works if you are used to NFTX v2. The two updates that are most relevant for

Vault Fees paid in ETH

In V2 the fees paid were done using the native vToken. This meant if there was a 5% fee for buying an NFT, you needed to swap 1.05 vTokens with 1 vToken being burned and the other 0.05 vTokens distributed to liquidity and inventory providers

In NFTX V3 the fees are now paid in ETH as part of the transaction. Using the same 5% fee example, users will now buy 1 vToken through the NFTX Universal AMM Router, and the additional fee amount can be worked out by passing the fee amount to the vault contract vTokenToEth function.

redeemFee = 5000000000000000

vTokenAmount = 49995022529647 wei

The vTokenToETH function is based on the TWAP and does not flucuate based on the available liquidity in the pool. For example, if you were buying 5 items the fee amount would be 5 * 5000000000000000 = 25000000000000000

which would equate to

5 * 49995022529647 = 249975112648235

Buying more tokens doesn't impact the spread for the vTokenToETH calculation

Premium NFT auctions

When an NFT is "minted" into the vault on Sepolia a premium fee of 500% placed on the item at an exponential reduction to 0% over 3600 seconds. The fee is reduced every second. mainnet duration will be 10 hours (36000 seconds)

To find the current fee required for the NFT pass the ID of the NFT into the getVTokenPremium function on the vault contract. The response includes the premium amount and the depositor address. The depositor will receieve the majority of the premium fee, with the liquidity and inventory providers also sharing in a portion, but this address is not required for the marketplace integration, only the premium amount.

If you want to omit the premium NFTs from your integration you can add a filter to the subgraph call to require the holdings.dateAdded is less than now - 3600.

Add this to your Get all NFT holdings query
holdings(first: 1000, orderBy: tokenId, orderDirection: asc, where: { dateAdded_lt: "$(now - 3600)"})

Remember that the premium reduces every second even though the blocks are only every 12 seconds. This will mean that what ever calculation you make for the buyer, the actual buy price is going to be lower because more time has elapsed before the next block is mined. This additional ETH is returned to the user as part of the transaction.

Get all NFT holdings

Send a request to the NFTX V3 subgraph for all vaults.

Graph Endpoints

Sepolia: https://thegraph.com/hosted-service/subgraph/nftx-project/nftx-v3-vaults-sepolia

Mainnet Graph Endpoint: Not yet deployed.

Arbitrum Graph Endpoint: Not yet deployed.

Graph request:

{
  globals {
    fees {
      mintFee
      randomRedeemFee
      targetRedeemFee
      randomSwapFee
      targetSwapFee
    }
  }
  vaults(
    first: 1000
    where: {vaultId_gte: 0, isFinalized: true, totalHoldings_gt: 0}
  ) {
    vaultId
    id
    is1155
    isFinalized
    totalHoldings
    totalMints
    totalRedeems
    totalFees
    totalSwaps
    createdAt
    holdings(first: 1000, orderBy: tokenId, orderDirection: asc) {
      id
      tokenId
      amount
      dateAdded
    }
   # holdings(
   #     first: 1000, 
   #     orderBy: tokenId, 
   #     orderDirection: asc,
   #     where: { dateAdded_lt: "$(now - 3600)"}
   #     ) {
   #   id
   #   tokenId
   #   amount
   #   dateAdded
   # }
    token {
      id
      name
      symbol
    }
    fees {
      mintFee
      redeemFee
      swapFee
    }
    usesFactoryFees
    asset {
      id
      name
      symbol
    }
    manager {
      id
    }
    createdBy {
      id
    }
    eligibilityModule {
      id
      eligibleIds
      eligibleRange
    }
    features {
      enableMint
      enableRedeem
      enableSwap
    }
  }
}

Buying an NFT(s) from the vaults

When buying items from the vault you need

  1. redeemFee amount to calculate the ETH fee required (subgraph).

  2. vTokenPremium amount to calculate the ETH required for new items in the vault (onchain)

  3. quoteDecimals amount to calculate the cost of the vTokens for redeeming the NFTs (this comes from the NFTX Router API request)

  4. nftIds of the NFT you want to purchase

  5. to address of the person buying

In this example we're buying 2 NFTs, 351, 346 from the following vault. Vault Name: Fringoooor vaultId: 0 vTokenAddress: 0x6f4d645d1645e65db2E7f9Aa11Eb5Fc45a65592A

Calculate the NFT premium cost

When an NFT goes into the vault there is a 3600 second premium placed upon it which reduces each second exponentially from 500% - 0%.

To calculate the premium for an NFT being bought you can pass the nftId to the getVTokenPremium721 function. The response will contain the amount of additional vToken that is set as the premium.

Below is an example of two tokens, one with almost the highest premium (token 351) and the other no premium (token 346).

These values should be added to the ETH fee (see next step).

Calculate ETH fee

Find the fee amount for redeemFee, either by using the subgraph response or by making an onchain call to the vault contract (see below).

In this example we are buying two NFTs, we need to double the redeemFee from 50000000000000000 to 100000000000000000

Calculate Fee + Premium to ETH

Add the redeemFee and the nftPremiumFee together and pass that to the vTokenToETH function on the vault contract.

100000000000000000 + 4983601988461962229 = 5083601988461962229

580380454730343582 wei is equivalent to 0.5080380454730343582 ETH.

This amount is then added to the cost of the token buy quote price.

Calculate the token price

NFTX is running a Universal Router API to return the best price across all of the pools and positions.

When requesting

https://api.nftx.xyz/v3/eth-sepolia/quote?tokenInAddress=ETH&tokenInChainId=11155111&tokenOutAddress=0xea0bb4de9f595439059af786614daf2ffada72d5&tokenOutChainId=11155111&amount=1000000000000000000&type=exactOut&recipient=0x4eAc46c2472b32dc7158110825A7443D35a90168&deadline=300&enableUniversalRouter=true&slippageTolerance=0.01&protocols=v3

Query Parameters
Value
Description

tokenInChainId

11155111

Sepolia: 11155111, Mainnet: 1, Arbitrum 42161

tokenOutChainId

11155111

Sepolia: 11155111, Mainnet: 1, Arbitrum 42161

protocols

v3

The Universal router is configured to also work with SushiSwap V2 router, however all V3 liquidity will be on the NFTX AMM which is v3

slippageTolerance

10

deadline

3000

The quote is valid for 3000 seconds

recipient

0x50f69c6556338965bf77C16ADb462Fdb5bE01C09

The recipient is set to the marketplaceZap address, as that will be the recipient of the tokens to then redeem the NFT from the vault.

enableUniversalRouter

true

Required to return the quote callData

tokenInAddress

0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6

The token you are paying with, this should be set to wETH even if paying with ETH.

amount

2000000000000000000

The number of tokens you are buying. Remember, this is 1:1 with the NFT, the fees are now paid as ETH. We are buying 2 tokens in this example.

type

exactOut

When buying we only want the exact number of tokens out.

tokenOutAddress

0x6f4d645d1645e65db2E7f9Aa11Eb5Fc45a65592A

The vToken for the NFT we want to buy.

Response

{
    "methodParameters": {
        "calldata": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000064b944600000000000000000000000000000000000000000000000000000000000000001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000010000000000000000000000000011f9ce2c92ed333115d1cb1078a1f7bbfa7de0d50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000003a19200263a412600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b6f4d645d1645e65db2e7f9aa11eb5fc45a65592a002710b4fbf271143f4fbf7b91a5ded31805e42b2208d6000000000000000000000000000000000000000000",
        "value": "0x00",
        "to": "0x375e894364bEBed5388115A6BbEEfb9290C610de"
    },
    "blockNumber": "9376996",
    "amount": "2000000000000000000",
    "amountDecimals": "2",
    "quote": "237864347912006086",
    "quoteDecimals": "0.237864347912006086",
    "quoteGasAdjusted": "251903648500922086",
    "quoteGasAdjustedDecimals": "0.251903648500922086",
    "gasUseEstimateQuote": "14039300588916000",
    "gasUseEstimateQuoteDecimals": "0.014039300588916",
    "gasUseEstimate": "113000",
    "gasUseEstimateUSD": "28.078601",
    "simulationStatus": "UNATTEMPTED",
    "simulationError": false,
    "gasPriceWei": "124241598132",
    "route": [
        [
            {
                "type": "v3-pool",
                "address": "0x5E8A2433C047881D18B63AB239d02af4F4b33229",
                "tokenIn": {
                    "chainId": 5,
                    "decimals": "18",
                    "address": "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6",
                    "symbol": "WETH"
                },
                "tokenOut": {
                    "chainId": 5,
                    "decimals": "18",
                    "address": "0x6f4d645d1645e65db2E7f9Aa11Eb5Fc45a65592A",
                    "symbol": "FRINGOOOOOOOOR"
                },
                "fee": "10000",
                "liquidity": "22290182843350301072",
                "sqrtRatioX96": "26770813048747472398292166448",
                "tickCurrent": "-21702",
                "amountIn": "237864347912006086",
                "amountOut": "2000000000000000000"
            }
        ]
    ],
    "routeString": "[V3] 100.00% = WETH -- 1% [0x5E8A2433C047881D18B63AB239d02af4F4b33229] --> FRINGOOOOOOOOR",
    "quoteId": "1ec15"
}

Add the quoteDecimals to the Fee + Premium ETH amount calculated in the previous step.

0.237864347912006086 + 0.574672096207709349 = 0.8125364441 ETH.

Buy NFT with MarketplaceUniversalRouterZap

With all the data collected, call the buyNFTsWithETH function on the MarketplaceUniversalRouterZap

  • ETH amount calcuated earlier: premium + fee + token.

  • The vaultId retrieved from the subgraph (or onchain against the vault contract)

  • idsOut are the NFTs being bought

  • executeCallData is the calldata in the response from the NFTX Universal Router API

  • to address is who is buying the NFT (or who should receive the NFT)

  • deductRoyalty is an optional inclusion, false for the sake of this implementation

Last updated