# 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

```json
"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&#x20;

### 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.

<figure><img src="https://4170596333-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDFcTRdnGK6Aly8ggv62l%2Fuploads%2FZTj1a4cESnqP7on9qxDD%2Fimage.png?alt=media&#x26;token=1108023e-be1b-446a-91d4-c1383af17ed4" alt=""><figcaption></figcaption></figure>

`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\
&#x20;`5 * 5000000000000000 = 25000000000000000`&#x20;

which would equate to&#x20;

`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.

<figure><img src="https://4170596333-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDFcTRdnGK6Aly8ggv62l%2Fuploads%2FxSRvqHhqM4SXfIEuJ1bl%2Fimage.png?alt=media&#x26;token=b745be59-3b4c-48b9-9697-3a529e4dedc4" alt=""><figcaption><p>Checking the getVTokenPremium721 for tokenId 310</p></figcaption></figure>

{% hint style="info" %}
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`.
{% endhint %}

{% code title="Add this to your Get all NFT holdings query" overflow="wrap" %}

```graphql
holdings(first: 1000, orderBy: tokenId, orderDirection: asc, where: { dateAdded_lt: "$(now - 3600)"})
```

{% endcode %}

{% hint style="success" %}
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.
{% endhint %}

## 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**](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:**

```graphql
{
  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`).

<figure><img src="https://4170596333-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDFcTRdnGK6Aly8ggv62l%2Fuploads%2FpwRRxyqESas8b4cguUSP%2Fimage.png?alt=media&#x26;token=290a29f0-5670-4c2c-a627-2c76a91b1e23" alt=""><figcaption><p>This token has a premium of <code>4983601988461962229</code> vTokens.</p></figcaption></figure>

<figure><img src="https://4170596333-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDFcTRdnGK6Aly8ggv62l%2Fuploads%2FAnyoxLMsd2QzxkRC7gsq%2Fimage.png?alt=media&#x26;token=52cca60a-fbcf-4852-8728-75071a3acef2" alt=""><figcaption><p>This token has a premium of <code>0</code> vTokens.</p></figcaption></figure>

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).

<figure><img src="https://4170596333-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDFcTRdnGK6Aly8ggv62l%2Fuploads%2FKAXAgFGMUsRQ6qWcXGKJ%2Fimage.png?alt=media&#x26;token=faea9fa0-6037-4a59-a6f8-56e0764c9da4" alt=""><figcaption></figcaption></figure>

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` &#x20;

<figure><img src="https://4170596333-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDFcTRdnGK6Aly8ggv62l%2Fuploads%2Fu5Q8eL4Ev1CD5KNZirGB%2Fimage.png?alt=media&#x26;token=2e42a455-6ffc-4b19-8984-e43a0f78bcbf" alt=""><figcaption><p>We're passing the sum of the fee and NFT Premium amounts to find the ETH value</p></figcaption></figure>

`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><br>

<table data-full-width="true"><thead><tr><th width="260.3333333333333">Query Parameters</th><th width="264">Value</th><th>Description</th></tr></thead><tbody><tr><td><code>tokenInChainId</code></td><td><code>11155111</code></td><td>Sepolia: 11155111, Mainnet: 1, Arbitrum 42161</td></tr><tr><td><code>tokenOutChainId</code></td><td><code>11155111</code></td><td>Sepolia: 11155111, Mainnet: 1, Arbitrum 42161</td></tr><tr><td><code>protocols</code></td><td><code>v3</code></td><td>The Universal router is configured to also work with SushiSwap V2 router, however all V3 liquidity will be on the NFTX AMM which is <code>v3</code></td></tr><tr><td><code>slippageTolerance</code></td><td><code>10</code></td><td></td></tr><tr><td><code>deadline</code></td><td><code>3000</code></td><td>The quote is valid for 3000 seconds</td></tr><tr><td><code>recipient</code></td><td><code>0x50f69c6556338965bf77C16ADb462Fdb5bE01C09</code></td><td>The recipient is set to the <code>marketplaceZap</code> address, as that will be the recipient of the tokens to then redeem the NFT from the vault.</td></tr><tr><td><code>enableUniversalRouter</code></td><td><code>true</code></td><td>Required to return the quote <code>callData</code></td></tr><tr><td><code>tokenInAddress</code></td><td><code>0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6</code></td><td>The token you are paying with, this should be set to wETH even if paying with ETH. </td></tr><tr><td><code>amount</code></td><td><code>2000000000000000000</code></td><td>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.</td></tr><tr><td><code>type</code></td><td><code>exactOut</code></td><td>When buying we only want the exact number of tokens out.</td></tr><tr><td><code>tokenOutAddress</code></td><td><code>0x6f4d645d1645e65db2E7f9Aa11Eb5Fc45a65592A</code></td><td>The <code>vToken</code> for the NFT we want to buy.</td></tr></tbody></table>

Response

{% code fullWidth="true" %}

```json
{
    "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"
}
```

{% endcode %}

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

<div data-full-width="true"><figure><img src="https://4170596333-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDFcTRdnGK6Aly8ggv62l%2Fuploads%2FUQxy8eBvmHFKOFG7VocA%2Fimage.png?alt=media&#x26;token=1c4f66bd-aa19-41ad-9bae-2814463ff30c" alt=""><figcaption></figcaption></figure></div>
