Skip to content

Commit

Permalink
Merge pull request #57 from IndexCoop/feat/add-swap-quote-interface
Browse files Browse the repository at this point in the history
feat: add swap quote interface
  • Loading branch information
janndriessen authored May 23, 2024
2 parents 1f413b6 + 6af9b11 commit e877433
Show file tree
Hide file tree
Showing 22 changed files with 143 additions and 75 deletions.
28 changes: 24 additions & 4 deletions src/quote/swap/adapters/zeroex.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import axios, { AxiosRequestHeaders } from 'axios'

import { ChainId } from 'constants/chains'
import { SwapQuoteProvider, SwapQuoteRequest } from 'quote/swap/interfaces'
import {
SwapQuote,
SwapQuoteProvider,
SwapQuoteRequest,
} from 'quote/swap/interfaces'
import { Exchange } from 'utils'

type ZeroExApiSwapRequest = {
buyAmount?: string
Expand Down Expand Up @@ -103,8 +108,8 @@ export class ZeroExSwapQuoteProvider implements SwapQuoteProvider {
*/
public async getSwapQuote(
request: SwapQuoteRequest
): Promise<ZeroExApiSwapResponse | null> {
const { chainId } = request
): Promise<SwapQuote | null> {
const { chainId, inputToken, outputToken, slippage } = request
const params = this.getParams(request)
const path = this.swapPathOverride ?? '/swap/v1/quote'
const query = new URLSearchParams(params).toString()
Expand All @@ -118,7 +123,22 @@ export class ZeroExSwapQuoteProvider implements SwapQuoteProvider {
try {
const response = await axios.get(url, config)
const res: ZeroExApiSwapResponse = response.data
return res
return {
chainId,
inputToken,
outputToken,
inputAmount: res.sellAmount,
outputAmount: res.buyAmount,
callData: res.data,
slippage: slippage ?? 0,
// TODO: add swap data
swapData: {
exchange: Exchange.UniV3,
path: ['', ''],
fees: [300],
pool: '0x0000000000000000000000000000000000000000',
},
}
} catch (err: unknown) {
return null
}
Expand Down
14 changes: 12 additions & 2 deletions src/quote/swap/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { ZeroExApiSwapResponse } from 'utils'
import { SwapData } from 'utils'

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface SwapQuote extends ZeroExApiSwapResponse {}
export interface SwapQuote {
chainId: number
inputToken: string
outputToken: string
inputAmount: string
outputAmount: string
callData: string
slippage: number
swapData: SwapData
}

export interface SwapQuoteRequest {
chainId: number
inputToken: string
Expand Down
38 changes: 22 additions & 16 deletions src/quote/zeroEx/componentsQuoteProvider.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BigNumber } from '@ethersproject/bignumber'

import { ZeroExApiSwapResponse } from 'utils/0x'
import { QuoteToken } from '../quoteToken'
import { SwapQuoteProvider, SwapQuoteRequest } from 'quote/swap'
import { SwapQuote, SwapQuoteProvider, SwapQuoteRequest } from 'quote/swap'
import { Exchange } from 'utils'

export type ComponentQuotesResult = {
componentQuotes: string[]
Expand Down Expand Up @@ -43,7 +43,7 @@ export class ComponentsQuoteProvider {
const inputTokenAddress = this.getTokenAddressOrWeth(inputToken)
const outputTokenAddress = this.getTokenAddressOrWeth(outputToken)

const quotePromises: Promise<ZeroExApiSwapResponse | null>[] = []
const quotePromises: Promise<SwapQuote | null>[] = []

for (let i = 0; i < components.length; i += 1) {
const index = i
Expand All @@ -55,7 +55,7 @@ export class ComponentsQuoteProvider {

if (buyToken === sellToken) {
const amount = isMinting ? buyAmount : sellAmount
const fakeResponse = this.getFakeZeroExResponse(amount)
const fakeResponse = this.getFakeSwapQuote(amount)
quotePromises.push(fakeResponse)
} else {
const params: SwapQuoteRequest = {
Expand All @@ -75,14 +75,14 @@ export class ComponentsQuoteProvider {
}

const resultsWithNull = await Promise.all(quotePromises)
const results: ZeroExApiSwapResponse[] = resultsWithNull.filter(
const results: SwapQuote[] = resultsWithNull.filter(
(e): e is Exclude<typeof e, null> => e !== null
)
if (results.length !== resultsWithNull.length) return null
const componentQuotes = results.map((result) => result.data)
const componentQuotes = results.map((result) => result.callData)
const inputOutputTokenAmount = results
.map((result) =>
BigNumber.from(isMinting ? result.sellAmount : result.buyAmount)
BigNumber.from(isMinting ? result.inputAmount : result.outputAmount)
)
.reduce((prevValue, currValue) => {
return currValue.add(prevValue)
Expand All @@ -94,19 +94,25 @@ export class ComponentsQuoteProvider {
}

/**
* This is just a helper function to return a fake ZeroEx response when the
* This is just a helper function to return a fake swap quote when the
* component and input/output token are the same.
*/
async getFakeZeroExResponse(
amount: BigNumber
): Promise<ZeroExApiSwapResponse> {
async getFakeSwapQuote(amount: BigNumber): Promise<SwapQuote> {
return Promise.resolve({
buyAmount: amount.toString(),
buyTokenAddress: '',
sellAmount: amount.toString(),
sellTokenAddress: '',
chainId: 1,
inputToken: '',
outputToken: '',
inputAmount: amount.toString(),
outputAmount: amount.toString(),
// Needs valid formatted hash - as otherwise validation will fail
data: '0x0000000000000000000000000000000000000000',
callData: '0x0000000000000000000000000000000000000000',
slippage: 0,
swapData: {
exchange: Exchange.UniV3,
path: ['', ''],
fees: [300],
pool: '0x0000000000000000000000000000000000000000',
},
})
}

Expand Down
20 changes: 12 additions & 8 deletions src/quote/zeroEx/provider.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import 'dotenv/config'

import { LocalhostProvider, QuoteTokens, ZeroExApiSwapQuote } from 'tests/utils'
import {
IndexZeroExSwapQuoteProvider,
LocalhostProvider,
QuoteTokens,
} from 'tests/utils'
import { wei } from 'utils'
import { ZeroExQuoteProvider } from './provider'

const provider = LocalhostProvider
const zeroExApi = ZeroExApiSwapQuote
const swapQuoteProvider = IndexZeroExSwapQuoteProvider

const { dpi, dseth, eth, mvi } = QuoteTokens

Expand All @@ -18,7 +22,7 @@ describe('ZeroExQuoteProvider', () => {
const indexTokenAmount = wei(1)
const inputToken = eth
const outputToken = dseth
const quoteProvider = new ZeroExQuoteProvider(provider, zeroExApi)
const quoteProvider = new ZeroExQuoteProvider(provider, swapQuoteProvider)
const quote = await quoteProvider.getQuote({
isMinting: true,
inputToken,
Expand All @@ -37,7 +41,7 @@ describe('ZeroExQuoteProvider', () => {
const indexTokenAmount = wei(1)
const inputToken = dseth
const outputToken = eth
const quoteProvider = new ZeroExQuoteProvider(provider, zeroExApi)
const quoteProvider = new ZeroExQuoteProvider(provider, swapQuoteProvider)
const quote = await quoteProvider.getQuote({
isMinting: false,
inputToken,
Expand Down Expand Up @@ -67,7 +71,7 @@ describe('ZeroExQuoteProvider', () => {
indexTokenAmount: wei(1),
slippage: 0.5,
}
const quoteProvider = new ZeroExQuoteProvider(provider, zeroExApi)
const quoteProvider = new ZeroExQuoteProvider(provider, swapQuoteProvider)
const quote = await quoteProvider.getQuote(request)
if (!quote) fail()
expect(quote.componentQuotes.length).toBeGreaterThan(0)
Expand All @@ -86,7 +90,7 @@ describe('ZeroExQuoteProvider', () => {
indexTokenAmount: wei(1),
slippage: 0.5,
}
const quoteProvider = new ZeroExQuoteProvider(provider, zeroExApi)
const quoteProvider = new ZeroExQuoteProvider(provider, swapQuoteProvider)
const quote = await quoteProvider.getQuote(request)
if (!quote) fail()
expect(quote.componentQuotes.length).toBeGreaterThan(0)
Expand All @@ -105,7 +109,7 @@ describe('ZeroExQuoteProvider', () => {
indexTokenAmount: wei(1),
slippage: 0.5,
}
const quoteProvider = new ZeroExQuoteProvider(provider, zeroExApi)
const quoteProvider = new ZeroExQuoteProvider(provider, swapQuoteProvider)
const quote = await quoteProvider.getQuote(request)
if (!quote) fail()
expect(quote.componentQuotes.length).toBeGreaterThan(0)
Expand All @@ -124,7 +128,7 @@ describe('ZeroExQuoteProvider', () => {
indexTokenAmount: wei(1),
slippage: 0.5,
}
const quoteProvider = new ZeroExQuoteProvider(provider, zeroExApi)
const quoteProvider = new ZeroExQuoteProvider(provider, swapQuoteProvider)
const quote = await quoteProvider.getQuote(request)
if (!quote) fail()
expect(quote.componentQuotes.length).toBeGreaterThan(0)
Expand Down
4 changes: 2 additions & 2 deletions src/tests/arbitrum/btc2x.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { IndexCoopBitcoin2xIndex } from 'constants/tokens'
import {
getArbitrumTestFactory,
getSignerAccount,
LocalhostProviderArbitrum,
QuoteTokens,
TestFactory,
wei,
ZeroExApiArbitrumSwapQuote,
} from 'tests/utils'

const { eth } = QuoteTokens
Expand All @@ -21,7 +21,7 @@ describe('BTC2X (Arbitrum)', () => {
beforeEach(async () => {
const provider = LocalhostProviderArbitrum
const signer = getSignerAccount(3, provider)
factory = new TestFactory(provider, signer, ZeroExApiArbitrumSwapQuote)
factory = getArbitrumTestFactory(provider, signer)
})

test('can mint with ETH', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/tests/arbitrum/btc3x.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { IndexCoopBitcoin3xIndex } from 'constants/tokens'
import {
getArbitrumTestFactory,
getSignerAccount,
LocalhostProviderArbitrum,
QuoteTokens,
TestFactory,
wei,
ZeroExApiArbitrumSwapQuote,
} from 'tests/utils'

const { eth } = QuoteTokens
Expand All @@ -21,7 +21,7 @@ describe.skip('BTC3X (Arbitrum)', () => {
beforeEach(async () => {
const provider = LocalhostProviderArbitrum
const signer = getSignerAccount(5, provider)
factory = new TestFactory(provider, signer, ZeroExApiArbitrumSwapQuote)
factory = getArbitrumTestFactory(provider, signer)
})

test('can mint with ETH', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/tests/arbitrum/eth2x.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { IndexCoopEthereum2xIndex } from 'constants/tokens'
import {
getArbitrumTestFactory,
getSignerAccount,
LocalhostProviderArbitrum,
QuoteTokens,
TestFactory,
wei,
ZeroExApiArbitrumSwapQuote,
} from 'tests/utils'

const { eth } = QuoteTokens
Expand All @@ -21,7 +21,7 @@ describe('ETH2X (Arbitrum)', () => {
beforeEach(async () => {
const provider = LocalhostProviderArbitrum
const signer = getSignerAccount(0, provider)
factory = new TestFactory(provider, signer, ZeroExApiArbitrumSwapQuote)
factory = getArbitrumTestFactory(provider, signer)
})

test('can mint with ETH', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/tests/arbitrum/eth3x.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { IndexCoopEthereum3xIndex } from 'constants/tokens'
import {
getArbitrumTestFactory,
getSignerAccount,
LocalhostProviderArbitrum,
QuoteTokens,
TestFactory,
wei,
ZeroExApiArbitrumSwapQuote,
} from 'tests/utils'

const { eth } = QuoteTokens
Expand All @@ -21,7 +21,7 @@ describe('ETH3X (Arbitrum)', () => {
beforeEach(async () => {
const provider = LocalhostProviderArbitrum
const signer = getSignerAccount(2, provider)
factory = new TestFactory(provider, signer, ZeroExApiArbitrumSwapQuote)
factory = getArbitrumTestFactory(provider, signer)
})

test.only('can mint with ETH', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/tests/arbitrum/ibtc1x.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { IndexCoopInverseBitcoinIndex } from 'constants/tokens'
import {
getArbitrumTestFactory,
getSignerAccount,
LocalhostProviderArbitrum,
QuoteTokens,
TestFactory,
wei,
ZeroExApiArbitrumSwapQuote,
} from 'tests/utils'

const { eth } = QuoteTokens
Expand All @@ -21,7 +21,7 @@ describe('iBTC1X (Arbitrum)', () => {
beforeEach(async () => {
const provider = LocalhostProviderArbitrum
const signer = getSignerAccount(4, provider)
factory = new TestFactory(provider, signer, ZeroExApiArbitrumSwapQuote)
factory = getArbitrumTestFactory(provider, signer)
})

test('can mint with ETH', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/tests/arbitrum/ieth1x.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { IndexCoopInverseEthereumIndex } from 'constants/tokens'
import {
getArbitrumTestFactory,
getSignerAccount,
LocalhostProviderArbitrum,
QuoteTokens,
TestFactory,
wei,
ZeroExApiArbitrumSwapQuote,
} from 'tests/utils'

const { eth } = QuoteTokens
Expand All @@ -21,7 +21,7 @@ describe('iETH1X (Arbitrum)', () => {
beforeEach(async () => {
const provider = LocalhostProviderArbitrum
const signer = getSignerAccount(1, provider)
factory = new TestFactory(provider, signer, ZeroExApiArbitrumSwapQuote)
factory = getArbitrumTestFactory(provider, signer)
})

test('can mint with ETH', async () => {
Expand Down
5 changes: 2 additions & 3 deletions src/tests/btc2x.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import {
getMainnetTestFactory,
LocalhostProvider,
QuoteTokens,
SignerAccount4,
TestFactory,
wei,
ZeroExApiSwapQuote,
} from './utils'

const { btc2x, eth } = QuoteTokens
const zeroExApi = ZeroExApiSwapQuote

describe('BTC2X (mainnet)', () => {
let factory: TestFactory
beforeEach(async () => {
const provider = LocalhostProvider
const signer = SignerAccount4
factory = new TestFactory(provider, signer, zeroExApi)
factory = getMainnetTestFactory(provider, signer)
})

test('can mint with ETH', async () => {
Expand Down
Loading

0 comments on commit e877433

Please sign in to comment.