From 8eab9aed0ad867ec2dc9ab39985c7799b1d9e6b4 Mon Sep 17 00:00:00 2001 From: JD Date: Fri, 20 Sep 2024 15:08:29 +0100 Subject: [PATCH] feat: add swap quote provider --- src/quote/flashmint/wrapped/provider.ts | 31 +++++--- src/quote/provider/index.ts | 10 ++- src/utils/component-swap-data.test.ts | 12 ++- src/utils/component-swap-data.ts | 99 +++++++++++++------------ 4 files changed, 88 insertions(+), 64 deletions(-) diff --git a/src/quote/flashmint/wrapped/provider.ts b/src/quote/flashmint/wrapped/provider.ts index f436cccd..4411998f 100644 --- a/src/quote/flashmint/wrapped/provider.ts +++ b/src/quote/flashmint/wrapped/provider.ts @@ -1,5 +1,6 @@ import { BigNumber } from '@ethersproject/bignumber' +import { SwapQuoteProvider } from 'quote/swap' import { ComponentSwapData, ComponentWrapData, @@ -14,6 +15,7 @@ import { getRpcProvider } from 'utils/rpc-provider' import { QuoteProvider, QuoteToken } from '../../interfaces' export interface FlashMintWrappedQuoteRequest { + chainId: number isMinting: boolean inputToken: QuoteToken outputToken: QuoteToken @@ -31,42 +33,47 @@ export interface FlashMintWrappedQuote { export class WrappedQuoteProvider implements QuoteProvider { - constructor(private readonly rpcUrl: string) {} + constructor( + private readonly rpcUrl: string, + private readonly swapQuoteProvider: SwapQuoteProvider + ) {} async getQuote( request: FlashMintWrappedQuoteRequest ): Promise { - const { inputToken, indexTokenAmount, isMinting, outputToken, slippage } = - request - console.log( - inputToken.symbol, - outputToken.symbol, - indexTokenAmount.toString(), + const { + chainId, + inputToken, + indexTokenAmount, isMinting, - slippage - ) + outputToken, + slippage, + } = request const indexToken = isMinting ? outputToken : inputToken const indexTokenSymbol = indexToken.symbol const componentSwapData = isMinting ? await getIssuanceComponentSwapData( { + chainId, indexTokenSymbol, indexToken: indexToken.address, inputToken: inputToken.address, indexTokenAmount, }, - this.rpcUrl + this.rpcUrl, + this.swapQuoteProvider ) : await getRedemptionComponentSwapData( { + chainId, indexTokenSymbol, indexToken: indexToken.address, outputToken: outputToken.address, indexTokenAmount, }, - this.rpcUrl + this.rpcUrl, + this.swapQuoteProvider ) - console.log(componentSwapData) const componentWrapData = getWrapData(indexToken.symbol) // FIXME: add check // if (componentSwapData.length !== componentWrapData.length) return null diff --git a/src/quote/provider/index.ts b/src/quote/provider/index.ts index d001a19a..31f89910 100644 --- a/src/quote/provider/index.ts +++ b/src/quote/provider/index.ts @@ -186,8 +186,14 @@ export class FlashMintQuoteProvider ) } case FlashMintContractType.wrapped: { - const wrappedQuoteProvider = new WrappedQuoteProvider(rpcUrl) - const wrappedQuote = await wrappedQuoteProvider.getQuote(request) + const wrappedQuoteProvider = new WrappedQuoteProvider( + rpcUrl, + swapQuoteProvider + ) + const wrappedQuote = await wrappedQuoteProvider.getQuote({ + ...request, + chainId, + }) if (!wrappedQuote) return null const builder = new WrappedTransactionBuilder(rpcUrl) const txRequest: FlashMintWrappedBuildRequest = { diff --git a/src/utils/component-swap-data.test.ts b/src/utils/component-swap-data.test.ts index 164ee6b5..62683351 100644 --- a/src/utils/component-swap-data.test.ts +++ b/src/utils/component-swap-data.test.ts @@ -6,11 +6,13 @@ import { getRedemptionComponentSwapData, } from 'utils/component-swap-data' import { wei } from 'utils/numbers' -import { LocalhostProviderUrl } from 'tests/utils' +import { IndexZeroExSwapQuoteProvider, LocalhostProviderUrl } from 'tests/utils' import { isSameAddress } from 'utils/addresses' import { Exchange } from 'utils/swap-data' +const chainId = 1 const rpcUrl = LocalhostProviderUrl +const swapQuoteProvider = IndexZeroExSwapQuoteProvider const indexTokenSymbol = USDCY.symbol const indexToken = USDCY.address! @@ -20,12 +22,14 @@ describe('getIssuanceComponentSwapData()', () => { test('returns correct swap data based on input token USDC', async () => { const componentSwapData = await getIssuanceComponentSwapData( { + chainId, indexTokenSymbol, indexToken, inputToken: usdc, indexTokenAmount: wei(1), }, - rpcUrl + rpcUrl, + swapQuoteProvider ) // FIXME: should be 5 for USDCY expect(componentSwapData.length).toBe(6) @@ -80,12 +84,14 @@ describe('getRedemptionComponentSwapData()', () => { test('returns correct swap data based for output token USDC', async () => { const componentSwapData = await getRedemptionComponentSwapData( { + chainId, indexTokenSymbol, indexToken, outputToken: usdc, indexTokenAmount: wei(1), }, - rpcUrl + rpcUrl, + swapQuoteProvider ) // FIXME: should be 5 for USDCY expect(componentSwapData.length).toBe(6) diff --git a/src/utils/component-swap-data.ts b/src/utils/component-swap-data.ts index 44ce7cfe..763c7bcc 100644 --- a/src/utils/component-swap-data.ts +++ b/src/utils/component-swap-data.ts @@ -5,6 +5,7 @@ import { mainnet } from 'viem/chains' import { AddressZero } from 'constants/addresses' import { USDC } from 'constants/tokens' +import { SwapQuote, SwapQuoteProvider } from 'quote' import { isSameAddress } from 'utils/addresses' import { getIssuanceModule } from 'utils/issuanceModules' import { getRpcProvider } from 'utils/rpc-provider' @@ -32,6 +33,7 @@ export interface ComponentSwapData { } interface ComponentSwapDataRequest { + chainId: number indexTokenSymbol: string indexToken: string indexTokenAmount: BigNumber @@ -137,9 +139,16 @@ interface IssuanceRequest extends ComponentSwapDataRequest { export async function getIssuanceComponentSwapData( request: IssuanceRequest, - rpcUrl: string + rpcUrl: string, + swapQuoteProvider: SwapQuoteProvider ): Promise { - const { indexTokenSymbol, indexToken, indexTokenAmount, inputToken } = request + const { + chainId, + indexTokenSymbol, + indexToken, + indexTokenAmount, + inputToken, + } = request const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) const [issuanceComponents] = await issuance.getRequiredComponentIssuanceUnits( indexToken, @@ -147,6 +156,7 @@ export async function getIssuanceComponentSwapData( ) const underlyingERC20sPromises: Promise[] = issuanceComponents.map((component: string) => getUnderlyingErc20(component)) + const wrappedTokens = await Promise.all(underlyingERC20sPromises) // TODO: // const buyAmountsPromises = issuanceComponents.map( // (component: string, index: number) => @@ -154,35 +164,22 @@ export async function getIssuanceComponentSwapData( // ) // const buyAmounts = await Promise.all(buyAmountsPromises) const buyAmounts = issuanceComponents.map(() => BigNumber.from(0)) - const wrappedTokens = await Promise.all(underlyingERC20sPromises) - console.log(wrappedTokens) - // TODO: get swap data; add swap quote provider - const swaps: Promise<{ swapData: SwapData } | null>[] = - issuanceComponents.map((_: string, index: number) => { + const swapPromises: Promise[] = issuanceComponents.map( + (_: string, index: number) => { const wrappedToken = wrappedTokens[index] const underlyingERC20 = wrappedToken.underlyingErc20 - console.log( - underlyingERC20.symbol === USDC.symbol, - underlyingERC20.symbol, - USDC.symbol - ) - console.log( - isSameAddress(underlyingERC20.address, inputToken), - underlyingERC20.address, - inputToken - ) if (isSameAddress(underlyingERC20.address, inputToken)) return null - // const buyUnderlyingAmount = buyAmounts[index] - // const mintParams = { - // buyToken: underlyingERC20.address, - // buyAmount: buyUnderlyingAmount, - // sellToken: inputToken, - // includedSources: 'Uniswap_V3', - // } - // TODO: add swap quote provider - return null - }) - const swapData = await Promise.all(swaps) + return swapQuoteProvider.getSwapQuote({ + chainId, + inputToken, + outputToken: underlyingERC20.address, + outputAmount: buyAmounts[index].toString(), + // TODO: needed for USDCY? + // includedSources: 'Uniswap_V3', + }) + } + ) + const swapData = await Promise.all(swapPromises) return buildComponentSwapData( issuanceComponents, wrappedTokens, @@ -197,10 +194,16 @@ interface RedemptionRequest extends ComponentSwapDataRequest { export async function getRedemptionComponentSwapData( request: RedemptionRequest, - rpcUrl: string + rpcUrl: string, + swapQuoteProvider: SwapQuoteProvider ): Promise { - const { indexTokenSymbol, indexToken, indexTokenAmount, outputToken } = - request + const { + chainId, + indexTokenSymbol, + indexToken, + indexTokenAmount, + outputToken, + } = request const issuance = getIssuanceContract(indexTokenSymbol, rpcUrl) const [issuanceComponents] = await issuance.getRequiredComponentRedemptionUnits( @@ -210,7 +213,6 @@ export async function getRedemptionComponentSwapData( const underlyingERC20sPromises: Promise[] = issuanceComponents.map((component: string) => getUnderlyingErc20(component)) const wrappedTokens = await Promise.all(underlyingERC20sPromises) - console.log(wrappedTokens) // TODO: check google docs // const buyAmountsPromises = issuanceComponents.map( // (component: string, index: number) => @@ -223,24 +225,27 @@ export async function getRedemptionComponentSwapData( // ) // const buyAmounts = await Promise.all(buyAmountsPromises) const buyAmounts = issuanceComponents.map(() => BigNumber.from(0)) - const swaps = issuanceComponents.map((_: string, index: number) => { - const wrappedToken = wrappedTokens[index] - const underlyingERC20 = wrappedToken.underlyingErc20 - if (isSameAddress(underlyingERC20.address, outputToken)) return null - // const buyUnderlyingAmount = buyAmounts[index] - // const redeemParams = { - // buyToken: outputToken, - // sellAmount: buyUnderlyingAmount, - // sellToken: underlyingERC20.address, - // includedSources: 'Uniswap_V3', - // } - return null - }) + const swapPromises: Promise[] = issuanceComponents.map( + (_: string, index: number) => { + const wrappedToken = wrappedTokens[index] + const underlyingERC20 = wrappedToken.underlyingErc20 + if (isSameAddress(underlyingERC20.address, outputToken)) return null + return swapQuoteProvider.getSwapQuote({ + chainId, + inputToken: underlyingERC20.address, + inputAmount: buyAmounts[index].toString(), + outputToken, + // TODO: needed for USDCY? + // includedSources: 'Uniswap_V3', + }) + } + ) + const swapData = await Promise.all(swapPromises) return buildComponentSwapData( issuanceComponents, wrappedTokens, buyAmounts, - swaps + swapData ) } @@ -248,7 +253,7 @@ function buildComponentSwapData( issuanceComponents: string[], wrappedTokens: WrappedToken[], buyAmounts: BigNumber[], - swapDataResults: any + swapDataResults: (SwapQuote | null)[] ): ComponentSwapData[] { return issuanceComponents.map((_: string, index: number) => { const wrappedToken = wrappedTokens[index]