From d3beb017d200629eae6458c1c2093ae03c09485a Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Tue, 30 Apr 2024 15:56:56 +0100 Subject: [PATCH 1/3] feat: ERC20 quotes can now be obtained via the nftx api this is a huge refactor and rewrite of the pricing api, covering @nftx/api, @nftx/core, and @nftx/trade the individual quoting methods (fetchVaultBuyPrice etc) have been removed in favour of two methods: fetchQuote, and fetchPrice fetchPrice returns an off-chain price, fetchQuote returns an on-chain quote with executable data the different types of pricing calls can now be chosen via the `type` parameter, which can be buy/sell/swap/mint/redeem and the new type: erc20 BREAKING CHANGE: price methods have been removed in favour of fetchPrice and fetchQuote BREAKING CHANGE: tradeErc20 method now takes a quote object (obtained from fetchQuote) instead of a price object (from fetchTokenBuyPrice) BREAKING CHANGE: fetchTokenBuyPrice has been replaced with fetchAmmQuote - though fetchQuote/fetchPrice should be preferred --- packages/api/src/prices/fetchPrice.ts | 21 +++ packages/api/src/prices/fetchQuote.ts | 153 ++++++++++++------ packages/api/src/prices/fetchVaultBuyPrice.ts | 21 --- packages/api/src/prices/fetchVaultBuyQuote.ts | 27 ---- .../api/src/prices/fetchVaultMintPrice.ts | 21 --- .../api/src/prices/fetchVaultMintQuote.ts | 27 ---- .../api/src/prices/fetchVaultRedeemPrice.ts | 21 --- .../api/src/prices/fetchVaultRedeemQuote.ts | 27 ---- .../api/src/prices/fetchVaultSellPrice.ts | 21 --- .../api/src/prices/fetchVaultSellQuote.ts | 27 ---- .../api/src/prices/fetchVaultSwapPrice.ts | 24 --- .../api/src/prices/fetchVaultSwapQuote.ts | 30 ---- packages/api/src/prices/index.ts | 12 +- .../prices/__tests__/priceVaultBuy.test.ts | 18 +-- .../prices/__tests__/priceVaultSell.test.ts | 16 +- .../prices/__tests__/quoteVaultBuy.test.ts | 10 +- .../prices/__tests__/quoteVaultSell.test.ts | 8 +- packages/core/src/prices/priceVaultBuy.ts | 26 +-- packages/core/src/prices/priceVaultMint.ts | 12 +- packages/core/src/prices/priceVaultRedeem.ts | 12 +- packages/core/src/prices/priceVaultSell.ts | 21 +-- packages/core/src/prices/quoteVaultBuy.ts | 20 +-- packages/core/src/prices/quoteVaultSell.ts | 20 +-- .../{quoteToPrice.ts => ammQuoteToPrice.ts} | 45 ++++-- .../price/{fetchQuote.ts => fetchAmmQuote.ts} | 83 +++------- .../trade/src/price/fetchTokenBuyPrice.ts | 48 ------ .../trade/src/price/fetchTokenSellPrice.ts | 48 ------ .../trade/src/price/fetchTokenSpotPrice.ts | 33 ---- packages/trade/src/price/index.ts | 8 +- packages/trade/src/price/parseQuoteToken.ts | 3 +- packages/trade/src/price/types.ts | 60 ++++++- packages/trade/src/trade/tradeErc20.ts | 10 +- packages/types/src/price.ts | 25 +-- 33 files changed, 340 insertions(+), 618 deletions(-) create mode 100644 packages/api/src/prices/fetchPrice.ts delete mode 100644 packages/api/src/prices/fetchVaultBuyPrice.ts delete mode 100644 packages/api/src/prices/fetchVaultBuyQuote.ts delete mode 100644 packages/api/src/prices/fetchVaultMintPrice.ts delete mode 100644 packages/api/src/prices/fetchVaultMintQuote.ts delete mode 100644 packages/api/src/prices/fetchVaultRedeemPrice.ts delete mode 100644 packages/api/src/prices/fetchVaultRedeemQuote.ts delete mode 100644 packages/api/src/prices/fetchVaultSellPrice.ts delete mode 100644 packages/api/src/prices/fetchVaultSellQuote.ts delete mode 100644 packages/api/src/prices/fetchVaultSwapPrice.ts delete mode 100644 packages/api/src/prices/fetchVaultSwapQuote.ts rename packages/trade/src/price/{quoteToPrice.ts => ammQuoteToPrice.ts} (64%) rename packages/trade/src/price/{fetchQuote.ts => fetchAmmQuote.ts} (68%) delete mode 100644 packages/trade/src/price/fetchTokenBuyPrice.ts delete mode 100644 packages/trade/src/price/fetchTokenSellPrice.ts delete mode 100644 packages/trade/src/price/fetchTokenSpotPrice.ts diff --git a/packages/api/src/prices/fetchPrice.ts b/packages/api/src/prices/fetchPrice.ts new file mode 100644 index 00000000..1f8c5af7 --- /dev/null +++ b/packages/api/src/prices/fetchPrice.ts @@ -0,0 +1,21 @@ +import { MarketplacePrice } from '@nftx/types'; +import fetchQuote, { + BuyArgs, + MintArgs, + RedeemArgs, + SellArgs, + TokenArgs, + SwapArgs, +} from './fetchQuote'; + +function fetchPrice(args: BuyArgs): Promise; +function fetchPrice(args: SellArgs): Promise; +function fetchPrice(args: SwapArgs): Promise; +function fetchPrice(args: MintArgs): Promise; +function fetchPrice(args: RedeemArgs): Promise; +function fetchPrice(args: TokenArgs): Promise; +function fetchPrice(args: any) { + return fetchQuote({ ...args, quoteType: 'price' }); +} + +export default fetchPrice; diff --git a/packages/api/src/prices/fetchQuote.ts b/packages/api/src/prices/fetchQuote.ts index 017be901..6d773dd8 100644 --- a/packages/api/src/prices/fetchQuote.ts +++ b/packages/api/src/prices/fetchQuote.ts @@ -1,82 +1,131 @@ import type { Address, + BigIntish, MarketplacePrice, MarketplaceQuote, + Permit2Quote, TokenIds, + QuoteToken, } from '@nftx/types'; import { queryApi } from '../utils'; import config from '@nftx/config'; import { getTokenIdAmounts, getUniqueTokenIds } from '@nftx/utils'; -type CommonArgs = { - type: 'buy' | 'sell' | 'swap' | 'mint' | 'redeem'; +export type BuyArgs = { + network?: number; + type: 'buy'; + vaultId: string; + tokenIds: TokenIds; +}; +export type SellArgs = { + network?: number; + type: 'sell'; + vaultId: string; + tokenIds: TokenIds; +}; +export type SwapArgs = { + network?: number; + type: 'swap'; + vaultId: string; + mintTokenIds: TokenIds; + redeemTokenIds: TokenIds; +}; +export type MintArgs = { + network?: number; + type: 'mint'; vaultId: string; - buyTokenIds?: TokenIds; - sellTokenIds?: TokenIds; + tokenIds: TokenIds; +}; +export type RedeemArgs = { network?: number; + type: 'redeem'; + vaultId: string; + tokenIds: TokenIds; }; -type PriceArgs = CommonArgs & { - quoteType: 'price'; +export type TokenArgs = { + network?: number; + type: 'erc20'; + buyToken: QuoteToken; + sellToken: QuoteToken; + buyAmount?: BigIntish; + sellAmount?: BigIntish; }; -type QuoteArgs = CommonArgs & { - quoteType: 'quote'; + +type PriceArgs = { quoteType: 'price' }; +type QuoteArgs = { + quoteType?: 'quote'; userAddress: Address; slippagePercentage?: number; + permit2?: Permit2Quote; }; -function fetchQuote(args: PriceArgs): Promise; -function fetchQuote(args: QuoteArgs): Promise; -function fetchQuote(args: PriceArgs | QuoteArgs) { +function fetchQuote(args: BuyArgs & PriceArgs): Promise; +function fetchQuote(args: BuyArgs & QuoteArgs): Promise; +function fetchQuote(args: SellArgs & PriceArgs): Promise; +function fetchQuote(args: SellArgs & QuoteArgs): Promise; +function fetchQuote(args: SwapArgs & PriceArgs): Promise; +function fetchQuote(args: SwapArgs & QuoteArgs): Promise; +function fetchQuote(args: MintArgs & PriceArgs): Promise; +function fetchQuote(args: MintArgs & QuoteArgs): Promise; +function fetchQuote(args: RedeemArgs & PriceArgs): Promise; +function fetchQuote(args: RedeemArgs & QuoteArgs): Promise; +function fetchQuote(args: TokenArgs & PriceArgs): Promise; +function fetchQuote(args: TokenArgs & QuoteArgs): Promise; +function fetchQuote(args: any) { const { - quoteType, type, - vaultId, - buyTokenIds: buyTokensAndAmounts, + quoteType = 'quote', network = config.network, - sellTokenIds: sellTokensAndAmounts, + vaultId, + userAddress, + slippagePercentage, } = args; - const buyTokenIds = buyTokensAndAmounts - ? getUniqueTokenIds(buyTokensAndAmounts) - : undefined; - const buyAmounts = buyTokensAndAmounts - ? getTokenIdAmounts(buyTokensAndAmounts) - : undefined; - const sellTokenIds = sellTokensAndAmounts - ? getUniqueTokenIds(sellTokensAndAmounts) - : undefined; - const sellAmounts = sellTokensAndAmounts - ? getTokenIdAmounts(sellTokensAndAmounts) - : undefined; + const url = `/${network}/quote/${type}`; + + const query: Record = { + vaultId, + userAddress, + slippagePercentage, + }; + + switch (type) { + case 'buy': + case 'redeem': + query.buyTokenIds = getUniqueTokenIds(args.tokenIds); + query.buyAmounts = getTokenIdAmounts(args.tokenIds); + break; + case 'sell': + case 'mint': + query.sellTokenIds = getUniqueTokenIds(args.tokenIds); + query.sellAmounts = getTokenIdAmounts(args.tokenIds); + break; + case 'swap': + query.sellTokenIds = getUniqueTokenIds(args.mintTokenIds); + query.sellAmounts = getTokenIdAmounts(args.mintTokenIds); + query.buyTokenIds = getUniqueTokenIds(args.redeemTokenIds); + query.buyAmounts = getTokenIdAmounts(args.redeemTokenIds); + break; + case 'erc20': + query.sellToken = args.sellToken; + query.sellAmount = args.sellAmount; + query.buyToken = args.buyToken; + query.buyAmount = args.buyAmount; + break; + } - if (quoteType === 'price') { - return queryApi({ - url: `/${network}/price`, - query: { - type, - vaultId, - buyTokenIds, - buyAmounts, - sellTokenIds, - sellAmounts, - }, - }); + if (args.permit2 && quoteType === 'quote') { + query.permit2 = args.permit2; } - const { userAddress, slippagePercentage } = args; + type QuoteType = typeof quoteType extends 'price' + ? MarketplacePrice + : MarketplaceQuote; - return queryApi({ - url: `/${network}/quote`, - query: { - type, - vaultId, - buyTokenIds, - buyAmounts, - sellTokenIds, - sellAmounts, - userAddress, - slippagePercentage, - }, + return queryApi({ + url, + query, + method: quoteType === 'price' ? 'GET' : 'POST', }); } diff --git a/packages/api/src/prices/fetchVaultBuyPrice.ts b/packages/api/src/prices/fetchVaultBuyPrice.ts deleted file mode 100644 index 5357be25..00000000 --- a/packages/api/src/prices/fetchVaultBuyPrice.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultBuyPrice = ({ - tokenIds, - vaultId, - network, -}: { - vaultId: string; - tokenIds: TokenIds; - network?: number; -}) => - fetchQuote({ - quoteType: 'price', - type: 'buy', - vaultId, - buyTokenIds: tokenIds, - network, - }); - -export default fetchVaultBuyPrice; diff --git a/packages/api/src/prices/fetchVaultBuyQuote.ts b/packages/api/src/prices/fetchVaultBuyQuote.ts deleted file mode 100644 index c55ba5cb..00000000 --- a/packages/api/src/prices/fetchVaultBuyQuote.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Address, TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultBuyQuote = ({ - tokenIds, - vaultId, - network, - userAddress, - slippagePercentage, -}: { - vaultId: string; - tokenIds: TokenIds; - userAddress: Address; - network?: number; - slippagePercentage?: number; -}) => - fetchQuote({ - quoteType: 'quote', - type: 'buy', - vaultId, - buyTokenIds: tokenIds, - network, - userAddress, - slippagePercentage, - }); - -export default fetchVaultBuyQuote; diff --git a/packages/api/src/prices/fetchVaultMintPrice.ts b/packages/api/src/prices/fetchVaultMintPrice.ts deleted file mode 100644 index 413499a2..00000000 --- a/packages/api/src/prices/fetchVaultMintPrice.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultMintPrice = ({ - tokenIds, - vaultId, - network, -}: { - vaultId: string; - tokenIds: TokenIds; - network?: number; -}) => - fetchQuote({ - quoteType: 'price', - type: 'mint', - vaultId, - sellTokenIds: tokenIds, - network, - }); - -export default fetchVaultMintPrice; diff --git a/packages/api/src/prices/fetchVaultMintQuote.ts b/packages/api/src/prices/fetchVaultMintQuote.ts deleted file mode 100644 index 227b9f54..00000000 --- a/packages/api/src/prices/fetchVaultMintQuote.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Address, TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultMintQuote = ({ - tokenIds, - vaultId, - network, - userAddress, - slippagePercentage, -}: { - vaultId: string; - tokenIds: TokenIds; - userAddress: Address; - network?: number; - slippagePercentage: number; -}) => - fetchQuote({ - quoteType: 'quote', - type: 'mint', - vaultId, - sellTokenIds: tokenIds, - network, - userAddress, - slippagePercentage, - }); - -export default fetchVaultMintQuote; diff --git a/packages/api/src/prices/fetchVaultRedeemPrice.ts b/packages/api/src/prices/fetchVaultRedeemPrice.ts deleted file mode 100644 index ff00dc08..00000000 --- a/packages/api/src/prices/fetchVaultRedeemPrice.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultRedeemPrice = ({ - tokenIds, - vaultId, - network, -}: { - vaultId: string; - tokenIds: TokenIds; - network?: number; -}) => - fetchQuote({ - quoteType: 'price', - type: 'redeem', - vaultId, - buyTokenIds: tokenIds, - network, - }); - -export default fetchVaultRedeemPrice; diff --git a/packages/api/src/prices/fetchVaultRedeemQuote.ts b/packages/api/src/prices/fetchVaultRedeemQuote.ts deleted file mode 100644 index 2214c70a..00000000 --- a/packages/api/src/prices/fetchVaultRedeemQuote.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Address, TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultRedeemQuote = ({ - tokenIds, - vaultId, - network, - userAddress, - slippagePercentage, -}: { - vaultId: string; - tokenIds: TokenIds; - userAddress: Address; - network?: number; - slippagePercentage?: number; -}) => - fetchQuote({ - quoteType: 'quote', - type: 'redeem', - vaultId, - buyTokenIds: tokenIds, - network, - userAddress, - slippagePercentage, - }); - -export default fetchVaultRedeemQuote; diff --git a/packages/api/src/prices/fetchVaultSellPrice.ts b/packages/api/src/prices/fetchVaultSellPrice.ts deleted file mode 100644 index 26f81e81..00000000 --- a/packages/api/src/prices/fetchVaultSellPrice.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultSellPrice = ({ - tokenIds, - vaultId, - network, -}: { - vaultId: string; - tokenIds: TokenIds; - network?: number; -}) => - fetchQuote({ - quoteType: 'price', - type: 'sell', - vaultId, - sellTokenIds: tokenIds, - network, - }); - -export default fetchVaultSellPrice; diff --git a/packages/api/src/prices/fetchVaultSellQuote.ts b/packages/api/src/prices/fetchVaultSellQuote.ts deleted file mode 100644 index 6ed94dbf..00000000 --- a/packages/api/src/prices/fetchVaultSellQuote.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Address, TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultSellQuote = ({ - tokenIds, - vaultId, - network, - userAddress, - slippagePercentage, -}: { - vaultId: string; - tokenIds: TokenIds; - userAddress: Address; - network?: number; - slippagePercentage?: number; -}) => - fetchQuote({ - quoteType: 'quote', - type: 'sell', - vaultId, - sellTokenIds: tokenIds, - network, - userAddress, - slippagePercentage, - }); - -export default fetchVaultSellQuote; diff --git a/packages/api/src/prices/fetchVaultSwapPrice.ts b/packages/api/src/prices/fetchVaultSwapPrice.ts deleted file mode 100644 index b0ba2275..00000000 --- a/packages/api/src/prices/fetchVaultSwapPrice.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultSwapPrice = ({ - sellTokenIds, - buyTokenIds, - vaultId, - network, -}: { - vaultId: string; - sellTokenIds: TokenIds; - buyTokenIds: TokenIds; - network?: number; -}) => - fetchQuote({ - quoteType: 'price', - type: 'swap', - vaultId, - buyTokenIds, - sellTokenIds, - network, - }); - -export default fetchVaultSwapPrice; diff --git a/packages/api/src/prices/fetchVaultSwapQuote.ts b/packages/api/src/prices/fetchVaultSwapQuote.ts deleted file mode 100644 index 67583b04..00000000 --- a/packages/api/src/prices/fetchVaultSwapQuote.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Address, TokenIds } from '@nftx/types'; -import fetchQuote from './fetchQuote'; - -const fetchVaultSwapQuote = ({ - vaultId, - network, - userAddress, - buyTokenIds, - sellTokenIds, - slippagePercentage, -}: { - vaultId: string; - sellTokenIds: TokenIds; - buyTokenIds: TokenIds; - userAddress: Address; - network?: number; - slippagePercentage?: number; -}) => - fetchQuote({ - quoteType: 'quote', - type: 'swap', - vaultId, - buyTokenIds, - sellTokenIds, - network, - userAddress, - slippagePercentage, - }); - -export default fetchVaultSwapQuote; diff --git a/packages/api/src/prices/index.ts b/packages/api/src/prices/index.ts index 58ae1d36..98ddbfbc 100644 --- a/packages/api/src/prices/index.ts +++ b/packages/api/src/prices/index.ts @@ -1,10 +1,2 @@ -export { default as fetchVaultBuyPrice } from './fetchVaultBuyPrice'; -export { default as fetchVaultBuyQuote } from './fetchVaultBuyQuote'; -export { default as fetchVaultSellPrice } from './fetchVaultSellPrice'; -export { default as fetchVaultSellQuote } from './fetchVaultSellQuote'; -export { default as fetchVaultSwapPrice } from './fetchVaultSwapPrice'; -export { default as fetchVaultSwapQuote } from './fetchVaultSwapQuote'; -export { default as fetchVaultMintPrice } from './fetchVaultMintPrice'; -export { default as fetchVaultMintQuote } from './fetchVaultMintQuote'; -export { default as fetchVaultRedeemPrice } from './fetchVaultRedeemPrice'; -export { default as fetchVaultRedeemQuote } from './fetchVaultRedeemQuote'; +export { default as fetchQuote } from './fetchQuote'; +export { default as fetchPrice } from './fetchPrice'; diff --git a/packages/core/src/prices/__tests__/priceVaultBuy.test.ts b/packages/core/src/prices/__tests__/priceVaultBuy.test.ts index 1fafe6f6..0fee3146 100644 --- a/packages/core/src/prices/__tests__/priceVaultBuy.test.ts +++ b/packages/core/src/prices/__tests__/priceVaultBuy.test.ts @@ -6,15 +6,15 @@ type PriceVaultBuy = ReturnType; let priceVaultBuy: PriceVaultBuy; let run: () => ReturnType; -let fetchTokenBuyPrice: jest.Mock; +let fetchAmmQuote: jest.Mock; let quoteVaultBuy: jest.Mock; let tokenIds: `${number}`[]; let vault: Parameters[0]['vault']; let holdings: Parameters[0]['holdings']; beforeEach(() => { - fetchTokenBuyPrice = jest.fn(({ amount }: { amount: bigint }) => ({ - price: amount, + fetchAmmQuote = jest.fn(({ buyAmount }: { buyAmount: bigint }) => ({ + price: buyAmount, })); quoteVaultBuy = jest.fn().mockResolvedValue({ vTokenPrice: 0.6 }); tokenIds = ['0', '1']; @@ -66,7 +66,7 @@ beforeEach(() => { }, ]; - priceVaultBuy = makePriceVaultBuy({ fetchTokenBuyPrice, quoteVaultBuy }); + priceVaultBuy = makePriceVaultBuy({ fetchAmmQuote, quoteVaultBuy }); run = () => priceVaultBuy({ holdings, @@ -92,9 +92,9 @@ describe('when there are more than 5 token ids', () => { it('fetches the price of buying the required vToken', async () => { const result = await run(); - expect(fetchTokenBuyPrice).toBeCalled(); - expect(fetchTokenBuyPrice.mock.calls[0][0].tokenAddress).toBe('0x01'); - expect(fetchTokenBuyPrice.mock.calls[0][0].amount).toBe(WeiPerEther * 6n); + expect(fetchAmmQuote).toBeCalled(); + expect(fetchAmmQuote.mock.calls[0][0].buyToken).toBe('0x01'); + expect(fetchAmmQuote.mock.calls[0][0].buyAmount).toBe(WeiPerEther * 6n); expect(result.vTokenPrice).toBe(parseEther('6')); }); it('gets the vault fee price for buying n items', async () => { @@ -141,7 +141,7 @@ describe('when there are 5 or less token ids', () => { it('does not fetch any prices', async () => { await run(); - expect(fetchTokenBuyPrice).not.toBeCalled(); + expect(fetchAmmQuote).not.toBeCalled(); }); it('uses the price stored on the vault', async () => { const result = await run(); @@ -185,6 +185,6 @@ describe('when vault is an 1155', () => { it('does not fetch any prices', async () => { await run(); - expect(fetchTokenBuyPrice).not.toBeCalled(); + expect(fetchAmmQuote).not.toBeCalled(); }); }); diff --git a/packages/core/src/prices/__tests__/priceVaultSell.test.ts b/packages/core/src/prices/__tests__/priceVaultSell.test.ts index 8bc27712..36c772b0 100644 --- a/packages/core/src/prices/__tests__/priceVaultSell.test.ts +++ b/packages/core/src/prices/__tests__/priceVaultSell.test.ts @@ -6,13 +6,13 @@ type PriceVaultSell = ReturnType; let priceVaultSell: PriceVaultSell; let run: () => ReturnType; -let fetchTokenSellPrice: jest.Mock; +let fetchAmmQuote: jest.Mock; let tokenIds: `${number}`[]; let vault: Parameters[0]['vault']; beforeEach(() => { - fetchTokenSellPrice = jest.fn(({ amount }: { amount: bigint }) => ({ - price: amount, + fetchAmmQuote = jest.fn(({ sellAmount }: { sellAmount: bigint }) => ({ + price: sellAmount, })); tokenIds = ['0', '1']; vault = { @@ -49,7 +49,7 @@ beforeEach(() => { ] as any, }; - priceVaultSell = makePriceVaultSell({ fetchTokenSellPrice }); + priceVaultSell = makePriceVaultSell({ fetchAmmQuote }); run = () => priceVaultSell({ network: 1, tokenIds, vault }); }); @@ -68,9 +68,9 @@ describe('when there are more than 5 token ids', () => { it('fetches the price of selling the required vToken', async () => { const result = await run(); - expect(fetchTokenSellPrice).toBeCalled(); - expect(fetchTokenSellPrice.mock.calls[0][0].tokenAddress).toBe('0x01'); - expect(fetchTokenSellPrice.mock.calls[0][0].amount).toBe(WeiPerEther * 6n); + expect(fetchAmmQuote).toBeCalled(); + expect(fetchAmmQuote.mock.calls[0][0].sellToken).toBe('0x01'); + expect(fetchAmmQuote.mock.calls[0][0].sellAmount).toBe(WeiPerEther * 6n); expect(result.vTokenPrice).toBe(parseEther('6')); }); it('gets the vault fee price for buying n items', async () => { @@ -92,7 +92,7 @@ describe('when there are 5 or less token ids', () => { it('does not fetch any prices', async () => { await run(); - expect(fetchTokenSellPrice).not.toBeCalled(); + expect(fetchAmmQuote).not.toBeCalled(); }); it('uses the price stored on the vault', async () => { const result = await run(); diff --git a/packages/core/src/prices/__tests__/quoteVaultBuy.test.ts b/packages/core/src/prices/__tests__/quoteVaultBuy.test.ts index eefea64d..3e427e2d 100644 --- a/packages/core/src/prices/__tests__/quoteVaultBuy.test.ts +++ b/packages/core/src/prices/__tests__/quoteVaultBuy.test.ts @@ -2,7 +2,7 @@ import { WeiPerEther, Zero } from '@nftx/constants'; import { makeQuoteVaultBuy } from '../quoteVaultBuy'; import { formatEther, parseEther } from 'viem'; -let fetchTokenBuyPrice: jest.Mock; +let fetchAmmQuote: jest.Mock; let fetchVTokenToEth: jest.Mock; let fetchPremiumPrice: jest.Mock; let quoteVaultBuy: ReturnType; @@ -17,9 +17,9 @@ let holdings: Args['holdings']; let slippagePercentage: Args['slippagePercentage']; beforeEach(() => { - fetchTokenBuyPrice = jest.fn(async ({ amount }) => ({ - price: amount, - methodParameters: { calldata: '0x9' }, + fetchAmmQuote = jest.fn(async ({ buyAmount }) => ({ + price: buyAmount, + methodParameters: { executeCalldata: '0x9' }, })); fetchVTokenToEth = jest.fn().mockResolvedValue(WeiPerEther / 2n); fetchPremiumPrice = jest.fn().mockResolvedValue([Zero, Zero]); @@ -46,7 +46,7 @@ beforeEach(() => { quoteVaultBuy = makeQuoteVaultBuy({ fetchPremiumPrice, - fetchTokenBuyPrice, + fetchAmmQuote, fetchVTokenToEth, }); run = () => diff --git a/packages/core/src/prices/__tests__/quoteVaultSell.test.ts b/packages/core/src/prices/__tests__/quoteVaultSell.test.ts index 2177211d..4b6a3fc0 100644 --- a/packages/core/src/prices/__tests__/quoteVaultSell.test.ts +++ b/packages/core/src/prices/__tests__/quoteVaultSell.test.ts @@ -2,7 +2,7 @@ import { WeiPerEther, Zero } from '@nftx/constants'; import { makeQuoteVaultSell } from '../quoteVaultSell'; import { formatEther, parseEther } from 'viem'; -let fetchTokenSellPrice: jest.Mock; +let fetchAmmQuote: jest.Mock; let fetchVTokenToEth: jest.Mock; let quoteVaultSell: ReturnType; type QuoteVaultSell = typeof quoteVaultSell; @@ -16,8 +16,8 @@ let slippagePercentage: Args['slippagePercentage']; let provider: any; beforeEach(() => { - fetchTokenSellPrice = jest.fn(async ({ amount }) => ({ - price: amount, + fetchAmmQuote = jest.fn(async ({ sellAmount }) => ({ + price: sellAmount, methodParameters: { calldata: '0x9' }, })); fetchVTokenToEth = jest.fn().mockResolvedValue(WeiPerEther / 2n); @@ -37,7 +37,7 @@ beforeEach(() => { }; quoteVaultSell = makeQuoteVaultSell({ - fetchTokenSellPrice, + fetchAmmQuote, fetchVTokenToEth, }); run = () => diff --git a/packages/core/src/prices/priceVaultBuy.ts b/packages/core/src/prices/priceVaultBuy.ts index 9ef4f12b..12347a1f 100644 --- a/packages/core/src/prices/priceVaultBuy.ts +++ b/packages/core/src/prices/priceVaultBuy.ts @@ -1,4 +1,4 @@ -import { fetchTokenBuyPrice } from '@nftx/trade'; +import { fetchAmmQuote } from '@nftx/trade'; import type { MarketplacePrice, Provider, @@ -16,7 +16,7 @@ import { } from '@nftx/utils'; import { InsufficientLiquidityError, ValidationError } from '@nftx/errors'; -type FetchTokenBuyPrice = typeof fetchTokenBuyPrice; +type FetchAmmQuote = typeof fetchAmmQuote; type QuoteVaultBuy = typeof quoteVaultBuy; const checkLiquidity =

(price: P) => { @@ -68,14 +68,14 @@ const getRoughPrice = async ({ network, tokenIds, vault, - fetchTokenBuyPrice, + fetchAmmQuote, now, }: { tokenIds: TokenIds; holdings: Pick[]; vault: Pick; network: number; - fetchTokenBuyPrice: FetchTokenBuyPrice; + fetchAmmQuote: FetchAmmQuote; now: number; }) => { // Calculate the price manually @@ -89,10 +89,11 @@ const getRoughPrice = async ({ price: vTokenPrice, route, routeString, - } = await fetchTokenBuyPrice({ + } = await fetchAmmQuote({ network, - tokenAddress: vault.id, - amount: buyAmount, + buyToken: vault.id, + buyAmount, + sellToken: 'ETH', }); const feePrice = calculateTotalFeePrice( vault.fees.redeemFee, @@ -128,10 +129,10 @@ const getRoughPrice = async ({ export const makePriceVaultBuy = ({ - fetchTokenBuyPrice, + fetchAmmQuote, quoteVaultBuy, }: { - fetchTokenBuyPrice: FetchTokenBuyPrice; + fetchAmmQuote: FetchAmmQuote; quoteVaultBuy: QuoteVaultBuy; }) => async ({ @@ -202,11 +203,14 @@ export const makePriceVaultBuy = network, tokenIds, vault, - fetchTokenBuyPrice, + fetchAmmQuote: fetchAmmQuote, now, }).then(checkLiquidity); }; -const priceVaultBuy = makePriceVaultBuy({ fetchTokenBuyPrice, quoteVaultBuy }); +const priceVaultBuy = makePriceVaultBuy({ + fetchAmmQuote, + quoteVaultBuy, +}); export default priceVaultBuy; diff --git a/packages/core/src/prices/priceVaultMint.ts b/packages/core/src/prices/priceVaultMint.ts index 6ae0e209..62046d40 100644 --- a/packages/core/src/prices/priceVaultMint.ts +++ b/packages/core/src/prices/priceVaultMint.ts @@ -1 +1,11 @@ -export { default } from './priceVaultSell'; +import priceVaultSell from './priceVaultSell'; + +type Args = Parameters[0]; + +const priceVaultMint = async (args: Args) => { + const quote = await priceVaultSell(args); + quote.type = 'mint'; + return quote; +}; + +export default priceVaultMint; diff --git a/packages/core/src/prices/priceVaultRedeem.ts b/packages/core/src/prices/priceVaultRedeem.ts index 22207dd4..18f2052f 100644 --- a/packages/core/src/prices/priceVaultRedeem.ts +++ b/packages/core/src/prices/priceVaultRedeem.ts @@ -1 +1,11 @@ -export { default } from './priceVaultBuy'; +import priceVaultBuy from './priceVaultBuy'; + +type Args = Parameters[0]; + +const priceVaultRedeem = async (args: Args) => { + const quote = await priceVaultBuy(args); + quote.type = 'redeem'; + return quote; +}; + +export default priceVaultRedeem; diff --git a/packages/core/src/prices/priceVaultSell.ts b/packages/core/src/prices/priceVaultSell.ts index f1eeeaba..18c4aba7 100644 --- a/packages/core/src/prices/priceVaultSell.ts +++ b/packages/core/src/prices/priceVaultSell.ts @@ -1,4 +1,4 @@ -import { fetchTokenSellPrice } from '@nftx/trade'; +import { fetchAmmQuote } from '@nftx/trade'; import type { MarketplacePrice, TokenIds, Vault } from '@nftx/types'; import { parseEther } from 'viem'; import { calculateTotalFeePrice } from './common'; @@ -10,7 +10,7 @@ import { ValidationError, } from '@nftx/errors'; -type FetchTokenSellPrice = typeof fetchTokenSellPrice; +type FetchAmmQuote = typeof fetchAmmQuote; const checkLiquidity = (price: MarketplacePrice) => { if (!price.vTokenPrice) { @@ -39,12 +39,12 @@ const getRoughPrice = async ({ network, tokenIds, vault, - fetchTokenSellPrice, + fetchAmmQuote, }: { network: number; tokenIds: TokenIds; vault: Pick; - fetchTokenSellPrice: FetchTokenSellPrice; + fetchAmmQuote: FetchAmmQuote; }) => { const totalTokenIds = getTotalTokenIds(tokenIds); const { vTokenToEth } = vault; @@ -54,9 +54,10 @@ const getRoughPrice = async ({ price: vTokenPrice, route, routeString, - } = await fetchTokenSellPrice({ - tokenAddress: vault.id, - amount: sellAmount, + } = await fetchAmmQuote({ + sellToken: vault.id, + sellAmount, + buyToken: 'ETH', network, }); const feePrice = calculateTotalFeePrice( @@ -84,7 +85,7 @@ const getRoughPrice = async ({ }; export const makePriceVaultSell = - ({ fetchTokenSellPrice }: { fetchTokenSellPrice: FetchTokenSellPrice }) => + ({ fetchAmmQuote }: { fetchAmmQuote: FetchAmmQuote }) => async ({ bypassIndexedPrice, network, @@ -113,8 +114,8 @@ export const makePriceVaultSell = network, tokenIds, vault, - fetchTokenSellPrice, + fetchAmmQuote, }).then(checkLiquidity); }; -export default makePriceVaultSell({ fetchTokenSellPrice }); +export default makePriceVaultSell({ fetchAmmQuote }); diff --git a/packages/core/src/prices/quoteVaultBuy.ts b/packages/core/src/prices/quoteVaultBuy.ts index f9c8a3b4..bfe6eb47 100644 --- a/packages/core/src/prices/quoteVaultBuy.ts +++ b/packages/core/src/prices/quoteVaultBuy.ts @@ -1,5 +1,5 @@ import { MARKETPLACE_ZAP, WeiPerEther, Zero } from '@nftx/constants'; -import { fetchTokenBuyPrice } from '@nftx/trade'; +import { fetchAmmQuote } from '@nftx/trade'; import type { Address, MarketplaceQuote, @@ -32,7 +32,7 @@ import { import { ValidationError } from '@nftx/errors'; import { MarketplaceZap } from '@nftx/abi'; -type FetchTokenBuyPrice = typeof fetchTokenBuyPrice; +type FetchAmmQuote = typeof fetchAmmQuote; type FetchVTokenToEth = typeof fetchVTokenToEth; type FetchPremiumPrice = typeof fetchPremiumPrice; @@ -90,11 +90,11 @@ const transformItem = async ({ export const makeQuoteVaultBuy = ({ fetchPremiumPrice, - fetchTokenBuyPrice, + fetchAmmQuote, fetchVTokenToEth, }: { fetchVTokenToEth: FetchVTokenToEth; - fetchTokenBuyPrice: FetchTokenBuyPrice; + fetchAmmQuote: FetchAmmQuote; fetchPremiumPrice: FetchPremiumPrice; }) => async ({ @@ -135,13 +135,13 @@ export const makeQuoteVaultBuy = price: vTokenPrice, route, routeString, - methodParameters: { calldata: executeCalldata }, - } = await fetchTokenBuyPrice({ + methodParameters: { executeCalldata }, + } = await fetchAmmQuote({ network, - tokenAddress: vault.id, - amount: buyAmount, + buyToken: vault.id, + buyAmount, + sellToken: 'WETH', userAddress: getChainConstant(MARKETPLACE_ZAP, network), - quote: 'WETH', slippagePercentage, }); @@ -225,7 +225,7 @@ export const makeQuoteVaultBuy = const quoteVaultBuy = makeQuoteVaultBuy({ fetchPremiumPrice, - fetchTokenBuyPrice, + fetchAmmQuote, fetchVTokenToEth, }); diff --git a/packages/core/src/prices/quoteVaultSell.ts b/packages/core/src/prices/quoteVaultSell.ts index 3f74ad4c..2081ab92 100644 --- a/packages/core/src/prices/quoteVaultSell.ts +++ b/packages/core/src/prices/quoteVaultSell.ts @@ -1,5 +1,5 @@ import { MARKETPLACE_ZAP, WeiPerEther, Zero } from '@nftx/constants'; -import { fetchTokenSellPrice, getApproveContracts } from '@nftx/trade'; +import { fetchAmmQuote, getApproveContracts } from '@nftx/trade'; import type { Address, MarketplaceQuote, @@ -21,16 +21,16 @@ import { calculateFeePricePerItem, calculateTotalFeePrice } from './common'; import { MintFeeExceedsValueError, ValidationError } from '@nftx/errors'; import { MarketplaceZap } from '@nftx/abi'; -type FetchTokenSellPrice = typeof fetchTokenSellPrice; +type FetchAmmQuote = typeof fetchAmmQuote; type FetchVTokenToEth = typeof fetchVTokenToEth; export const makeQuoteVaultSell = ({ - fetchTokenSellPrice, + fetchAmmQuote, fetchVTokenToEth, }: { fetchVTokenToEth: FetchVTokenToEth; - fetchTokenSellPrice: FetchTokenSellPrice; + fetchAmmQuote: FetchAmmQuote; }) => async ({ network, @@ -66,13 +66,13 @@ export const makeQuoteVaultSell = price: vTokenPrice, route, routeString, - methodParameters: { calldata: executeCalldata }, - } = await fetchTokenSellPrice({ - tokenAddress: vault.id, - amount: sellAmount, + methodParameters: { executeCalldata }, + } = await fetchAmmQuote({ + sellToken: vault.id, + sellAmount, + buyToken: 'WETH', network, userAddress: getChainConstant(MARKETPLACE_ZAP, network), - quote: 'WETH', slippagePercentage, }); const vTokenPricePerItem = (vTokenPrice * WeiPerEther) / sellAmount; @@ -184,7 +184,7 @@ export const makeQuoteVaultSell = }; const quoteVaultSell = makeQuoteVaultSell({ - fetchTokenSellPrice, + fetchAmmQuote, fetchVTokenToEth, }); diff --git a/packages/trade/src/price/quoteToPrice.ts b/packages/trade/src/price/ammQuoteToPrice.ts similarity index 64% rename from packages/trade/src/price/quoteToPrice.ts rename to packages/trade/src/price/ammQuoteToPrice.ts index f67767fc..6095adae 100644 --- a/packages/trade/src/price/quoteToPrice.ts +++ b/packages/trade/src/price/ammQuoteToPrice.ts @@ -1,11 +1,11 @@ -import type { Price } from '@nftx/types'; -import type { NftxQuote } from './fetchQuote'; +import type { ApproveContract, MarketplaceQuote } from '@nftx/types'; import { NFTX_ROUTER, WeiPerEther, Zero } from '@nftx/constants'; import { formatEther } from 'viem'; import { getChainConstant } from '@nftx/utils'; import { getApproveContracts } from '../approve'; +import { NftxQuote } from './types'; -const nftxQuoteToPrice = (quote: NftxQuote) => { +const ammQuoteToPrice = (quote: NftxQuote) => { const { network, sellToken } = quote; const isEth = `${sellToken}` === 'ETH'; @@ -13,7 +13,7 @@ const nftxQuoteToPrice = (quote: NftxQuote) => { return total + BigInt(r[0].amountIn); }, Zero); - const route: Price['route'] = quote.route.map((route) => { + const route: MarketplaceQuote['route'] = quote.route.map((route) => { const type = route[0].type; const amountIn = BigInt(route[0].amountIn); const amountOut = BigInt(route[route.length - 1].amountOut); @@ -46,7 +46,7 @@ const nftxQuoteToPrice = (quote: NftxQuote) => { }; }); - const approveContracts: Price['approveContracts'] = []; + const approveContracts: ApproveContract[] = []; // If you're paying ETH we don't need any approvals if (!isEth) { @@ -63,19 +63,38 @@ const nftxQuoteToPrice = (quote: NftxQuote) => { ); } - const price: Price = { - price: BigInt(quote.quote), - estimatedGas: BigInt(quote.gasUseEstimate), - gasPrice: BigInt(quote.gasPriceWei), - methodParameters: { - ...quote.methodParameters, - }, + const methodParameters: MarketplaceQuote['methodParameters'] | undefined = + quote.methodParameters + ? { + amountsIn: [], + amountsOut: [], + premiumLimit: '', + standard: 'ERC20', + tokenIdsIn: [], + tokenIdsOut: [], + vaultAddress: '0x', + vaultId: '', + executeCalldata: quote.methodParameters.calldata, + to: quote.methodParameters.to, + value: quote.methodParameters.value, + } + : undefined; + + const price: MarketplaceQuote = { approveContracts, + estimatedGas: BigInt(quote.gasUseEstimate), + feePrice: Zero, + items: [], + premiumPrice: Zero, + price: BigInt(quote.quote), route, + type: 'erc20', + vTokenPrice: BigInt(quote.quote), routeString: quote.routeString, + methodParameters: methodParameters as MarketplaceQuote['methodParameters'], }; return price; }; -export default nftxQuoteToPrice; +export default ammQuoteToPrice; diff --git a/packages/trade/src/price/fetchQuote.ts b/packages/trade/src/price/fetchAmmQuote.ts similarity index 68% rename from packages/trade/src/price/fetchQuote.ts rename to packages/trade/src/price/fetchAmmQuote.ts index c04b827b..ca14043d 100644 --- a/packages/trade/src/price/fetchQuote.ts +++ b/packages/trade/src/price/fetchAmmQuote.ts @@ -1,5 +1,10 @@ -import type { Address, BigIntish, Permit2Quote } from '@nftx/types'; -import type { QuoteToken } from './types'; +import type { + Address, + BigIntish, + MarketplaceQuote, + Permit2Quote, + QuoteToken, +} from '@nftx/types'; import config from '@nftx/config'; import { NFTX_ROUTER, WeiPerEther } from '@nftx/constants'; import { getChainConstant } from '@nftx/utils'; @@ -9,66 +14,10 @@ import { QuoteSlippageError, ValidationError, } from '@nftx/errors'; +import { NftxQuote } from './types'; +import ammQuoteToPrice from './ammQuoteToPrice'; -type RouteToken = { - chainId: number; - decimals: string; - address: Address; - symbol: string; -}; - -type RouteElV3 = { - type: 'v3-pool'; - address: Address; - tokenIn: RouteToken; - tokenOut: RouteToken; - fee: `${number}`; - liquidity: `${number}`; - sqrtRatioX96: `${number}`; - tickCurrent: `${number}`; - amountIn: `${number}`; - amountOut: `${number}`; -}; -type RouteElV2 = { - type: 'v2-pool'; - address: Address; - tokenIn: RouteToken; - tokenOut: RouteToken; - reserve0: { token: RouteToken; quotient: `${number}` }; - reserve1: { token: RouteToken; quotient: `${number}` }; - amountIn: `${number}`; - amountOut: `${number}`; -}; - -type RouteEl = RouteElV3 | RouteElV2; - -export type NftxQuote = { - network: number; - sellToken: Address; - buyToken: Address; - methodParameters: { - calldata: Address; - value: Address; - to: Address; - }; - blockNumber: `${number}`; - amount: `${number}`; - amountDecimals: `${number}`; - quote: `${number}`; - quoteDecimals: `${number}`; - quoteGasAdjusted: `${number}`; - quoteGasAdjustedDecimals: `${number}`; - gasUseEstimate: `${number}`; - gasUseEstimateUSD: `${number}`; - simulationStatus: string; - simulationError: boolean; - gasPriceWei: `${number}`; - route: RouteEl[][]; - routeString: string; - quoteId: string; -}; - -const fetchQuote = async (args: { +const fetchAmmQuote = async (args: { network?: number; buyToken: QuoteToken; buyAmount?: BigIntish; @@ -77,7 +26,7 @@ const fetchQuote = async (args: { slippagePercentage?: number; userAddress?: Address; permit2?: Permit2Quote; -}): Promise => { +}): Promise => { const { network = config.network, buyAmount, @@ -109,6 +58,7 @@ const fetchQuote = async (args: { searchParams.append('tokenInChainId', `${network}`); searchParams.append('tokenOutAddress', buyToken); searchParams.append('tokenOutChainId', `${network}`); + if (buyAmount) { searchParams.append('amount', BigInt(buyAmount).toString()); searchParams.append('type', 'exactOut'); @@ -120,6 +70,7 @@ const fetchQuote = async (args: { searchParams.append('buyAmount', WeiPerEther.toString()); searchParams.append('type', 'exactOut'); } + if (userAddress) { searchParams.append('recipient', userAddress); searchParams.append('deadline', '300'); @@ -132,7 +83,9 @@ const fetchQuote = async (args: { } else { searchParams.append('intent', 'quote'); } + searchParams.append('protocols', 'v3'); + if (permit2) { searchParams.append('permitSignature', permit2.signature); searchParams.append('permitAmount', permit2.amount.toString()); @@ -164,12 +117,12 @@ const fetchQuote = async (args: { data.methodParameters.to = getChainConstant(NFTX_ROUTER, network); } - return { + return ammQuoteToPrice({ ...data, network, sellToken, buyToken, - }; + }); }; -export default fetchQuote; +export default fetchAmmQuote; diff --git a/packages/trade/src/price/fetchTokenBuyPrice.ts b/packages/trade/src/price/fetchTokenBuyPrice.ts deleted file mode 100644 index 7b5720f3..00000000 --- a/packages/trade/src/price/fetchTokenBuyPrice.ts +++ /dev/null @@ -1,48 +0,0 @@ -import config from '@nftx/config'; -import { WeiPerEther } from '@nftx/constants'; -import type { Address, BigIntish, Permit2Quote, Price } from '@nftx/types'; -import type { QuoteToken } from './types'; -import fetchQuote from './fetchQuote'; -import nftxQuoteToPrice from './quoteToPrice'; - -/** Fetches a buy price for a given token. - */ -const fetchTokenBuyPrice = async (args: { - network?: number; - /** The token you want to buy (address, ETH, USDC, or WETH) */ - tokenAddress: QuoteToken; - /** The amount to buy (defaults to 1e18) */ - amount?: BigIntish; - /** The token you want to quote in (address, ETH, USDC, or WETH) (defaults to ETH) */ - quote?: QuoteToken; - /** Address of the wallet doing the buy. Required if you want to receive the calldata to make a trade */ - userAddress?: Address; - /** The max amount of slippage (0-1) */ - slippagePercentage?: number; - /** Permit2 parameters in order to do an off-chain permission. If ommitted, you will need to have done an on-chain approval with permit2 */ - permit2?: Permit2Quote; -}): Promise => { - const { - userAddress, - network = config.network, - tokenAddress: buyToken, - quote: sellToken = 'ETH', - amount: buyAmount = WeiPerEther, - slippagePercentage, - permit2, - } = args; - - const quote = await fetchQuote({ - buyToken, - sellToken, - buyAmount, - network, - userAddress, - slippagePercentage, - permit2, - }); - - return nftxQuoteToPrice(quote); -}; - -export default fetchTokenBuyPrice; diff --git a/packages/trade/src/price/fetchTokenSellPrice.ts b/packages/trade/src/price/fetchTokenSellPrice.ts deleted file mode 100644 index af3d7b31..00000000 --- a/packages/trade/src/price/fetchTokenSellPrice.ts +++ /dev/null @@ -1,48 +0,0 @@ -import config from '@nftx/config'; -import { WeiPerEther } from '@nftx/constants'; -import type { Address, BigIntish, Permit2Quote } from '@nftx/types'; -import type { QuoteToken } from './types'; -import fetchQuote from './fetchQuote'; -import nftxQuoteToPrice from './quoteToPrice'; - -/** Fetches a sell price for a given token. - */ -const fetchTokenSellPrice = async (args: { - network?: number; - /** The token you want to sell (address, ETH, USDC, or WETH) */ - tokenAddress: QuoteToken; - /** The amount to sell (defaults to 1e18) */ - amount?: BigIntish; - /** The token you want to quote in (address, ETH, USDC, or WETH) (defaults to ETH) */ - quote?: QuoteToken; - /** Address of the wallet doing the sell. Required if you want to receive the calldata to make a trade */ - userAddress?: Address; - /** The max amount of slippage (0-1) */ - slippagePercentage?: number; - /** Permit2 parameters in order to do an off-chain permission. If ommitted, you will need to have done an on-chain approval with permit2 */ - permit2?: Permit2Quote; -}) => { - const { - network = config.network, - tokenAddress: sellToken, - amount: sellAmount = WeiPerEther, - quote: buyToken = 'ETH', - userAddress, - slippagePercentage, - permit2, - } = args; - - const quote = await fetchQuote({ - buyToken, - sellToken, - sellAmount, - network, - userAddress, - slippagePercentage, - permit2, - }); - - return nftxQuoteToPrice(quote); -}; - -export default fetchTokenSellPrice; diff --git a/packages/trade/src/price/fetchTokenSpotPrice.ts b/packages/trade/src/price/fetchTokenSpotPrice.ts deleted file mode 100644 index e60b57ba..00000000 --- a/packages/trade/src/price/fetchTokenSpotPrice.ts +++ /dev/null @@ -1,33 +0,0 @@ -import config from '@nftx/config'; -import { WeiPerEther } from '@nftx/constants'; -import type { QuoteToken } from './types'; -import fetchTokenBuyPrice from './fetchTokenBuyPrice'; - -/** Fetches a spot price for a given token. - */ -const fetchTokenSpotPrice = async (args: { - network?: number; - tokenAddress: QuoteToken; - quote?: QuoteToken; - amount?: bigint; -}) => { - const { - network = config.network, - tokenAddress, - quote = 'ETH', - amount = WeiPerEther, - } = args; - - const price = await fetchTokenBuyPrice({ - tokenAddress, - quote, - network, - amount: 1n, - }); - - price.price = price.price * amount; - - return price; -}; - -export default fetchTokenSpotPrice; diff --git a/packages/trade/src/price/index.ts b/packages/trade/src/price/index.ts index 69506f4c..1af818ed 100644 --- a/packages/trade/src/price/index.ts +++ b/packages/trade/src/price/index.ts @@ -1,8 +1,2 @@ -export { default as fetchTokenBuyPrice } from './fetchTokenBuyPrice'; -export { default as fetchEthPrice } from './fetchEthPrice'; -export { default as fetchTokenSellPrice } from './fetchTokenSellPrice'; -export { default as fetchTokenSpotPrice } from './fetchTokenSpotPrice'; -export { default as fetchSpread } from './fetchSpread'; export { default as parseQuoteToken } from './parseQuoteToken'; -export { default as fetchQuote } from './fetchQuote'; -export * from './types'; +export { default as fetchAmmQuote } from './fetchAmmQuote'; diff --git a/packages/trade/src/price/parseQuoteToken.ts b/packages/trade/src/price/parseQuoteToken.ts index 11a4368f..655674c6 100644 --- a/packages/trade/src/price/parseQuoteToken.ts +++ b/packages/trade/src/price/parseQuoteToken.ts @@ -1,7 +1,6 @@ import { USDC, WETH_TOKEN } from '@nftx/constants'; -import type { QuoteToken } from './types'; import { getChainConstant } from '@nftx/utils'; -import type { Address } from '@nftx/types'; +import type { Address, QuoteToken } from '@nftx/types'; const parseQuoteToken = (address: QuoteToken, network: number) => { switch (address) { diff --git a/packages/trade/src/price/types.ts b/packages/trade/src/price/types.ts index b029dd8e..bba9eb09 100644 --- a/packages/trade/src/price/types.ts +++ b/packages/trade/src/price/types.ts @@ -1,3 +1,59 @@ -import type { Address } from '@nftx/types'; +import { Address } from '@nftx/types'; -export type QuoteToken = Address | 'ETH' | 'USDC' | 'WETH'; +type RouteToken = { + chainId: number; + decimals: string; + address: Address; + symbol: string; +}; + +type RouteElV3 = { + type: 'v3-pool'; + address: Address; + tokenIn: RouteToken; + tokenOut: RouteToken; + fee: `${number}`; + liquidity: `${number}`; + sqrtRatioX96: `${number}`; + tickCurrent: `${number}`; + amountIn: `${number}`; + amountOut: `${number}`; +}; +type RouteElV2 = { + type: 'v2-pool'; + address: Address; + tokenIn: RouteToken; + tokenOut: RouteToken; + reserve0: { token: RouteToken; quotient: `${number}` }; + reserve1: { token: RouteToken; quotient: `${number}` }; + amountIn: `${number}`; + amountOut: `${number}`; +}; + +type RouteEl = RouteElV3 | RouteElV2; + +export type NftxQuote = { + network: number; + sellToken: Address; + buyToken: Address; + methodParameters: { + calldata: Address; + value: Address; + to: Address; + }; + blockNumber: `${number}`; + amount: `${number}`; + amountDecimals: `${number}`; + quote: `${number}`; + quoteDecimals: `${number}`; + quoteGasAdjusted: `${number}`; + quoteGasAdjustedDecimals: `${number}`; + gasUseEstimate: `${number}`; + gasUseEstimateUSD: `${number}`; + simulationStatus: string; + simulationError: boolean; + gasPriceWei: `${number}`; + route: RouteEl[][]; + routeString: string; + quoteId: string; +}; diff --git a/packages/trade/src/trade/tradeErc20.ts b/packages/trade/src/trade/tradeErc20.ts index 102b08ac..701b4230 100644 --- a/packages/trade/src/trade/tradeErc20.ts +++ b/packages/trade/src/trade/tradeErc20.ts @@ -1,5 +1,5 @@ import config from '@nftx/config'; -import type { Price, Provider, Signer } from '@nftx/types'; +import type { MarketplaceQuote, Provider, Signer } from '@nftx/types'; const tradeErc20 = async ({ provider, @@ -9,20 +9,20 @@ const tradeErc20 = async ({ provider: Provider; signer: Signer; network?: number; - quote: Pick; + quote: Pick; }) => { const [address] = await signer.getAddresses(); const account = address; const { - methodParameters: { calldata, to, value }, + methodParameters: { executeCalldata, to, value }, } = quote; if (config.debug) { console.debug({ method: 'tradeErc20', to, - data: calldata, + data: executeCalldata, value: BigInt(value), account, }); @@ -30,7 +30,7 @@ const tradeErc20 = async ({ const hash = await signer.sendTransaction({ to, - data: calldata, + data: executeCalldata, value: BigInt(value), account, chain: provider.chain, diff --git a/packages/types/src/price.ts b/packages/types/src/price.ts index a08e5bef..04f0ba8a 100644 --- a/packages/types/src/price.ts +++ b/packages/types/src/price.ts @@ -15,22 +15,6 @@ type PriceRoute = Array<{ }>; }>; -/** A price object returned by all pricing methods (@nftx/trade) */ -export type Price = { - price: bigint; - estimatedGas?: bigint; - gasPrice?: bigint; - /** - * A list of sources providing liquidity for the given price - */ - sources?: Array<{ name: string; proportion: string }>; - route?: PriceRoute; - routeString?: string; - priceImpact?: number; - methodParameters: { calldata: Address; value: Address; to: Address }; - approveContracts: ApproveContract[]; -}; - export type ApproveContract = { label: string; /** The approval type */ @@ -47,9 +31,9 @@ export type ApproveContract = { standard?: 'ERC721' | 'ERC1155' | 'ERC20'; }; -/** A price object for buying/selling/swapping an NFT through the marketplace zap */ +/** A price object for buying/selling/swapping an NFT through the marketplace zap or trading an ERC20 through our AMM */ export type MarketplacePrice = { - type: 'buy' | 'sell' | 'swap' | 'mint' | 'redeem'; + type: 'buy' | 'sell' | 'swap' | 'mint' | 'redeem' | 'erc20'; /** The total price in ETH */ price: bigint; /** The ETH price of the vToken being bought/sold */ @@ -60,6 +44,8 @@ export type MarketplacePrice = { premiumPrice: bigint; route?: PriceRoute; routeString?: string; + /** A list of sources providing liquidity for the price */ + sources?: Array<{ name: string; proportion: string }>; }; type MarketplaceParameters = { @@ -100,3 +86,6 @@ export type MarketplaceQuote = MarketplacePrice & { methodParameters: MarketplaceParameters; approveContracts: ApproveContract[]; }; + +/** Acceptable tokens / currencies you can get quotes in */ +export type QuoteToken = Address | 'ETH' | 'USDC' | 'WETH'; From 55b9cdbc11180421918648347e5e1c0fa0bbb073 Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Tue, 30 Apr 2024 16:04:03 +0100 Subject: [PATCH 2/3] refactor: move fetchEthPrice / fetchSpread / fetchSpotPrice to @nftx/api these methods were previously in @nftx/trade and interacted directly with the AMM router as we now have an API layer for requesting ERC20 quotes, these methods have been moved to @nftx/api BREAKING CHANGE: these methods are no longer available in @nftx/trade --- packages/api/src/prices/fetchEthPrice.ts | 13 +++++++ packages/api/src/prices/fetchSpotPrice.ts | 41 +++++++++++++++++++++++ packages/api/src/prices/fetchSpread.ts | 36 ++++++++++++++++++++ packages/api/src/prices/index.ts | 3 ++ packages/trade/src/price/fetchEthPrice.ts | 39 --------------------- packages/trade/src/price/fetchSpread.ts | 38 --------------------- 6 files changed, 93 insertions(+), 77 deletions(-) create mode 100644 packages/api/src/prices/fetchEthPrice.ts create mode 100644 packages/api/src/prices/fetchSpotPrice.ts create mode 100644 packages/api/src/prices/fetchSpread.ts delete mode 100644 packages/trade/src/price/fetchEthPrice.ts delete mode 100644 packages/trade/src/price/fetchSpread.ts diff --git a/packages/api/src/prices/fetchEthPrice.ts b/packages/api/src/prices/fetchEthPrice.ts new file mode 100644 index 00000000..3a39042b --- /dev/null +++ b/packages/api/src/prices/fetchEthPrice.ts @@ -0,0 +1,13 @@ +import fetchSpotPrice from './fetchSpotPrice'; + +const fetchEthPrice = async ({ network }: { network?: number }) => { + const { price } = await fetchSpotPrice({ + network, + tokenAddress: 'ETH', + quoteToken: 'USDC', + }); + + return price; +}; + +export default fetchEthPrice; diff --git a/packages/api/src/prices/fetchSpotPrice.ts b/packages/api/src/prices/fetchSpotPrice.ts new file mode 100644 index 00000000..6793ec12 --- /dev/null +++ b/packages/api/src/prices/fetchSpotPrice.ts @@ -0,0 +1,41 @@ +import type { QuoteToken } from '@nftx/types'; +import fetchPrice from './fetchPrice'; +import { WeiPerEther } from '@nftx/constants'; + +const fetchSpotPrice = async ({ + tokenAddress, + amount = WeiPerEther, + network, + quoteToken, +}: { + tokenAddress: QuoteToken; + network?: number; + quoteToken?: QuoteToken; + amount?: bigint; +}) => { + let fraction = WeiPerEther; + let error: any = new Error('Failed to fetch spot price'); + + do { + try { + const quote = await fetchPrice({ + network, + type: 'erc20', + buyToken: tokenAddress, + sellToken: quoteToken || 'ETH', + buyAmount: fraction, + }); + + quote.price = (quote.price * amount) / fraction; + + return quote; + } catch (e) { + error = e; + fraction /= 100n; + } + } while (fraction > 0n); + + throw error; +}; + +export default fetchSpotPrice; diff --git a/packages/api/src/prices/fetchSpread.ts b/packages/api/src/prices/fetchSpread.ts new file mode 100644 index 00000000..3b955f5e --- /dev/null +++ b/packages/api/src/prices/fetchSpread.ts @@ -0,0 +1,36 @@ +import { WeiPerEther, Zero } from '@nftx/constants'; +import { QuoteToken } from '@nftx/types'; +import fetchPrice from './fetchPrice'; + +const fetchSpread = async ({ + tokenAddress, + network, + quoteToken, +}: { + tokenAddress: QuoteToken; + network?: number; + quoteToken?: QuoteToken; +}) => { + try { + const { price: buyPrice } = await fetchPrice({ + network, + type: 'erc20', + buyToken: tokenAddress, + sellToken: quoteToken || 'ETH', + buyAmount: WeiPerEther, + }); + const { price: sellPrice } = await fetchPrice({ + network, + type: 'erc20', + sellToken: tokenAddress, + buyToken: quoteToken || 'ETH', + sellAmount: WeiPerEther, + }); + + return buyPrice - sellPrice; + } catch { + return Zero; + } +}; + +export default fetchSpread; diff --git a/packages/api/src/prices/index.ts b/packages/api/src/prices/index.ts index 98ddbfbc..481f92df 100644 --- a/packages/api/src/prices/index.ts +++ b/packages/api/src/prices/index.ts @@ -1,2 +1,5 @@ export { default as fetchQuote } from './fetchQuote'; export { default as fetchPrice } from './fetchPrice'; +export { default as fetchEthPrice } from './fetchEthPrice'; +export { default as fetchSpotPrice } from './fetchSpotPrice'; +export { default as fetchSpread } from './fetchSpread'; diff --git a/packages/trade/src/price/fetchEthPrice.ts b/packages/trade/src/price/fetchEthPrice.ts deleted file mode 100644 index dc024cd7..00000000 --- a/packages/trade/src/price/fetchEthPrice.ts +++ /dev/null @@ -1,39 +0,0 @@ -import config from '@nftx/config'; -import { WeiPerEther } from '@nftx/constants'; -import { getChainConstant } from '@nftx/utils'; -import fetchQuote from './fetchQuote'; - -const fetchEthPriceFromNftxRouter = async ({ - network, -}: { - network: number; -}) => { - const { quote } = await fetchQuote({ - network, - buyToken: 'ETH', - sellToken: 'USDC', - buyAmount: WeiPerEther, - }); - - return BigInt(quote); -}; - -/** Fetches the current ETH price in $ terms. - * For test networks and testing in general, you can configure a hardcoded price using nftx.js's configure method. - */ -const fetchEthPrice = (args: { network?: number }) => { - const { network = config.network } = args; - - const hardcodedPrice = getChainConstant( - config.contracts.ethPrice, - network, - null - ); - if (hardcodedPrice != null) { - return BigInt(hardcodedPrice); - } - - return fetchEthPriceFromNftxRouter({ network }); -}; - -export default fetchEthPrice; diff --git a/packages/trade/src/price/fetchSpread.ts b/packages/trade/src/price/fetchSpread.ts deleted file mode 100644 index ad70955f..00000000 --- a/packages/trade/src/price/fetchSpread.ts +++ /dev/null @@ -1,38 +0,0 @@ -import config from '@nftx/config'; -import { Zero } from '@nftx/constants'; -import type { Address } from '@nftx/types'; -import fetchTokenBuyPrice from './fetchTokenBuyPrice'; -import fetchTokenSellPrice from './fetchTokenSellPrice'; -import type { QuoteToken } from './types'; - -/** - * Fetches the spread for a given token. This is the difference between buy price and sell price. - */ -const fetchSpread = async (args: { - network?: number; - /** The token you want the spread for (address / ETH / USDC / WETH) */ - tokenAddress: Address; - /** The currency you want the quote in (address / ETH / USDC / WETH) (defaults to ETH) */ - quote?: QuoteToken; -}): Promise => { - const { network = config.network, tokenAddress, quote } = args; - - try { - const { price: buyPrice } = await fetchTokenBuyPrice({ - network, - tokenAddress, - quote, - }); - const { price: sellPrice } = await fetchTokenSellPrice({ - network, - tokenAddress, - quote, - }); - - return buyPrice - sellPrice; - } catch { - return Zero; - } -}; - -export default fetchSpread; From 4c11b2ef25c9b97d3e259c9f4e3312b211999981 Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Tue, 30 Apr 2024 16:05:17 +0100 Subject: [PATCH 3/3] feat(@nftx/trade): add fulfill function this function takes any quote from fetchQuote and fulfills it, regardless of whether it's a sell/buy, a mint/redeem, or an ERC20 quote --- packages/api/src/prices/fetchEthPrice.ts | 1 + packages/api/src/prices/fetchPrice.ts | 1 + packages/api/src/prices/fetchQuote.ts | 5 ++- packages/api/src/prices/fetchSpotPrice.ts | 9 +++-- packages/api/src/prices/fetchSpread.ts | 1 + packages/trade/src/price/ammQuoteToPrice.ts | 36 ++++++++++--------- packages/trade/src/trade/fulfill.ts | 40 +++++++++++++++++++++ packages/trade/src/trade/index.ts | 1 + packages/types/src/price.ts | 2 +- 9 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 packages/trade/src/trade/fulfill.ts diff --git a/packages/api/src/prices/fetchEthPrice.ts b/packages/api/src/prices/fetchEthPrice.ts index 3a39042b..e685205b 100644 --- a/packages/api/src/prices/fetchEthPrice.ts +++ b/packages/api/src/prices/fetchEthPrice.ts @@ -1,5 +1,6 @@ import fetchSpotPrice from './fetchSpotPrice'; +/** Returns the current price of ETH in USDC terms */ const fetchEthPrice = async ({ network }: { network?: number }) => { const { price } = await fetchSpotPrice({ network, diff --git a/packages/api/src/prices/fetchPrice.ts b/packages/api/src/prices/fetchPrice.ts index 1f8c5af7..a34abd09 100644 --- a/packages/api/src/prices/fetchPrice.ts +++ b/packages/api/src/prices/fetchPrice.ts @@ -8,6 +8,7 @@ import fetchQuote, { SwapArgs, } from './fetchQuote'; +/** Returns an off-chain price for a transaction */ function fetchPrice(args: BuyArgs): Promise; function fetchPrice(args: SellArgs): Promise; function fetchPrice(args: SwapArgs): Promise; diff --git a/packages/api/src/prices/fetchQuote.ts b/packages/api/src/prices/fetchQuote.ts index 6d773dd8..0845a5ff 100644 --- a/packages/api/src/prices/fetchQuote.ts +++ b/packages/api/src/prices/fetchQuote.ts @@ -59,6 +59,7 @@ type QuoteArgs = { permit2?: Permit2Quote; }; +/** Returns an on-chain quote for a transaction. The response object can be passed into @nftx/trade's fulfill method to execute the quote */ function fetchQuote(args: BuyArgs & PriceArgs): Promise; function fetchQuote(args: BuyArgs & QuoteArgs): Promise; function fetchQuote(args: SellArgs & PriceArgs): Promise; @@ -122,10 +123,12 @@ function fetchQuote(args: any) { ? MarketplacePrice : MarketplaceQuote; + const method = quoteType === 'price' ? 'GET' : 'POST'; + return queryApi({ url, query, - method: quoteType === 'price' ? 'GET' : 'POST', + method, }); } diff --git a/packages/api/src/prices/fetchSpotPrice.ts b/packages/api/src/prices/fetchSpotPrice.ts index 6793ec12..dc850bcb 100644 --- a/packages/api/src/prices/fetchSpotPrice.ts +++ b/packages/api/src/prices/fetchSpotPrice.ts @@ -1,7 +1,9 @@ import type { QuoteToken } from '@nftx/types'; import fetchPrice from './fetchPrice'; import { WeiPerEther } from '@nftx/constants'; +import { UnknownError } from '@nftx/errors'; +/** Returns the spot price for buying a token */ const fetchSpotPrice = async ({ tokenAddress, amount = WeiPerEther, @@ -13,8 +15,10 @@ const fetchSpotPrice = async ({ quoteToken?: QuoteToken; amount?: bigint; }) => { + // We start by attempting to quote 1 whole token, but if it fails (e.g. due to insufficient liquidity), + // we can try to quote a smaller fraction of the token until we find a price that works. let fraction = WeiPerEther; - let error: any = new Error('Failed to fetch spot price'); + let error: any = new UnknownError('Failed to fetch spot price'); do { try { @@ -26,7 +30,8 @@ const fetchSpotPrice = async ({ buyAmount: fraction, }); - quote.price = (quote.price * amount) / fraction; + // Extrapolate the price to determine the spot price for 1 whole token, then scale it by the desired amount. + quote.price = quote.vTokenPrice = (quote.price * amount) / fraction; return quote; } catch (e) { diff --git a/packages/api/src/prices/fetchSpread.ts b/packages/api/src/prices/fetchSpread.ts index 3b955f5e..a380d806 100644 --- a/packages/api/src/prices/fetchSpread.ts +++ b/packages/api/src/prices/fetchSpread.ts @@ -2,6 +2,7 @@ import { WeiPerEther, Zero } from '@nftx/constants'; import { QuoteToken } from '@nftx/types'; import fetchPrice from './fetchPrice'; +/** Returns the difference between a buy and sell of a single token */ const fetchSpread = async ({ tokenAddress, network, diff --git a/packages/trade/src/price/ammQuoteToPrice.ts b/packages/trade/src/price/ammQuoteToPrice.ts index 6095adae..83b1c63c 100644 --- a/packages/trade/src/price/ammQuoteToPrice.ts +++ b/packages/trade/src/price/ammQuoteToPrice.ts @@ -63,22 +63,24 @@ const ammQuoteToPrice = (quote: NftxQuote) => { ); } - const methodParameters: MarketplaceQuote['methodParameters'] | undefined = - quote.methodParameters - ? { - amountsIn: [], - amountsOut: [], - premiumLimit: '', - standard: 'ERC20', - tokenIdsIn: [], - tokenIdsOut: [], - vaultAddress: '0x', - vaultId: '', - executeCalldata: quote.methodParameters.calldata, - to: quote.methodParameters.to, - value: quote.methodParameters.value, - } - : undefined; + let methodParameters = + undefined as any as MarketplaceQuote['methodParameters']; + + if (quote.methodParameters) { + methodParameters = { + amountsIn: [], + amountsOut: [], + premiumLimit: '', + standard: 'ERC20', + tokenIdsIn: [], + tokenIdsOut: [], + vaultAddress: '0x', + vaultId: '', + executeCalldata: quote.methodParameters.calldata, + to: quote.methodParameters.to, + value: quote.methodParameters.value, + }; + } const price: MarketplaceQuote = { approveContracts, @@ -91,7 +93,7 @@ const ammQuoteToPrice = (quote: NftxQuote) => { type: 'erc20', vTokenPrice: BigInt(quote.quote), routeString: quote.routeString, - methodParameters: methodParameters as MarketplaceQuote['methodParameters'], + methodParameters: methodParameters, }; return price; diff --git a/packages/trade/src/trade/fulfill.ts b/packages/trade/src/trade/fulfill.ts new file mode 100644 index 00000000..02048b71 --- /dev/null +++ b/packages/trade/src/trade/fulfill.ts @@ -0,0 +1,40 @@ +import { MarketplaceQuote, Provider, Signer } from '@nftx/types'; +import tradeErc20 from './tradeErc20'; +import swap from './swap'; +import redeem from './redeem'; +import mint from './mint'; +import sell from './sell'; +import buy from './buy'; +import { UnknownError } from '@nftx/errors'; + +/** Fulfills any quote returned by @nftx/api's fetchQuote method */ +const fulfill = ({ + network, + quote, + signer, + provider, +}: { + quote: Pick; + network: number; + signer: Signer; + provider: Provider; +}) => { + switch (quote.type) { + case 'buy': + return buy({ provider, quote, signer, network }); + case 'sell': + return sell({ provider, quote, signer, network }); + case 'mint': + return mint({ provider, quote, signer }); + case 'redeem': + return redeem({ quote, provider, signer }); + case 'swap': + return swap({ quote, provider, signer, network }); + case 'erc20': + return tradeErc20({ provider, quote, signer, network }); + default: + throw new UnknownError(`Unknown quote type: ${quote.type}`); + } +}; + +export default fulfill; diff --git a/packages/trade/src/trade/index.ts b/packages/trade/src/trade/index.ts index 633d5bbb..80ec6ef2 100644 --- a/packages/trade/src/trade/index.ts +++ b/packages/trade/src/trade/index.ts @@ -4,3 +4,4 @@ export { default as redeem } from './redeem'; export { default as sell } from './sell'; export { default as swap } from './swap'; export { default as tradeErc20 } from './tradeErc20'; +export { default as fulfill } from './fulfill'; diff --git a/packages/types/src/price.ts b/packages/types/src/price.ts index 04f0ba8a..964f3b50 100644 --- a/packages/types/src/price.ts +++ b/packages/types/src/price.ts @@ -31,7 +31,7 @@ export type ApproveContract = { standard?: 'ERC721' | 'ERC1155' | 'ERC20'; }; -/** A price object for buying/selling/swapping an NFT through the marketplace zap or trading an ERC20 through our AMM */ +/** A price object for buying/selling/swapping an NFT through the marketplace zap or trading an ERC20 through NFTX's AMM */ export type MarketplacePrice = { type: 'buy' | 'sell' | 'swap' | 'mint' | 'redeem' | 'erc20'; /** The total price in ETH */