-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for exact output (#5240)
- Loading branch information
1 parent
e031ca3
commit 2265307
Showing
6 changed files
with
127 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,68 @@ | ||
import { Currency, CurrencyAmount, Trade, TradeType } from '@pancakeswap/sdk' | ||
import { Currency, CurrencyAmount, Pair, Trade, TradeType } from '@pancakeswap/sdk' | ||
|
||
import { BETTER_TRADE_LESS_HOPS_THRESHOLD } from './constants' | ||
import { getAllCommonPairs } from './getAllCommonPairs' | ||
import { BestTradeOptions } from './types' | ||
import { isTradeBetter } from './utils/trade' | ||
|
||
export async function getBestTradeFromV2<TInput extends Currency, TOutput extends Currency>( | ||
amountIn: CurrencyAmount<TInput>, | ||
output: TOutput, | ||
options: BestTradeOptions, | ||
): Promise<Trade<TInput, TOutput, TradeType> | null> { | ||
const { provider, ...restOptions } = options | ||
const { maxHops = 3 } = restOptions | ||
const allowedPairs = await getAllCommonPairs(amountIn.currency, output, { provider }) | ||
|
||
if (!allowedPairs.length) { | ||
return null | ||
} | ||
export const getBestTradeFromV2ExactIn = createGetBestTradeFromV2(TradeType.EXACT_INPUT) | ||
|
||
export const getBestTradeFromV2ExactOut = createGetBestTradeFromV2(TradeType.EXACT_OUTPUT) | ||
|
||
export async function getBestTradeFromV2< | ||
TInput extends Currency, | ||
TOutput extends Currency, | ||
TTradeType extends TradeType, | ||
>(amountIn: CurrencyAmount<TInput>, output: TOutput, tradeType: TTradeType, options: BestTradeOptions) { | ||
const getBestTrade = tradeType === TradeType.EXACT_INPUT ? getBestTradeFromV2ExactIn : getBestTradeFromV2ExactOut | ||
|
||
if (maxHops === 1) { | ||
return Trade.bestTradeExactIn(allowedPairs, amountIn, output, restOptions)[0] ?? null | ||
return getBestTrade(amountIn, output, options) | ||
} | ||
|
||
function createGetBestTradeFromV2<TTradeType extends TradeType>(tradeType: TTradeType) { | ||
function getBestTrade<In extends Currency, Out extends Currency>( | ||
pairs: Pair[], | ||
amountIn: CurrencyAmount<In>, | ||
output: Out, | ||
options: Omit<BestTradeOptions, 'provider'>, | ||
) { | ||
if (tradeType === TradeType.EXACT_INPUT) { | ||
return Trade.bestTradeExactIn(pairs, amountIn, output, options) | ||
} | ||
return Trade.bestTradeExactOut(pairs, output, amountIn, options) | ||
} | ||
|
||
// search through trades with varying hops, find best trade out of them | ||
let bestTradeSoFar: Trade<TInput, TOutput, TradeType> | null = null | ||
for (let i = 1; i <= maxHops; i++) { | ||
const currentTrade: Trade<TInput, TOutput, TradeType> | null = | ||
Trade.bestTradeExactIn(allowedPairs, amountIn, output, { | ||
...restOptions, | ||
maxHops: i, | ||
maxNumResults: 1, | ||
})[0] ?? null | ||
// if current trade is best yet, save it | ||
if (isTradeBetter(bestTradeSoFar, currentTrade, BETTER_TRADE_LESS_HOPS_THRESHOLD)) { | ||
bestTradeSoFar = currentTrade | ||
return async function bestTradeFromV2<In extends Currency, Out extends Currency>( | ||
amountIn: CurrencyAmount<In>, | ||
output: Out, | ||
options: BestTradeOptions, | ||
) { | ||
const { provider, ...restOptions } = options | ||
const { maxHops = 3 } = restOptions | ||
const allowedPairs = await getAllCommonPairs(amountIn.currency, output, { provider }) | ||
|
||
if (!allowedPairs.length) { | ||
return null | ||
} | ||
|
||
if (maxHops === 1) { | ||
return getBestTrade(allowedPairs, amountIn, output, restOptions)[0] ?? null | ||
} | ||
|
||
// search through trades with varying hops, find best trade out of them | ||
let bestTradeSoFar: ReturnType<typeof getBestTrade<In, Out>>[number] | null = null | ||
for (let i = 1; i <= maxHops; i++) { | ||
const currentTrade: ReturnType<typeof getBestTrade<In, Out>>[number] | null = | ||
getBestTrade(allowedPairs, amountIn, output, { | ||
...restOptions, | ||
maxHops: i, | ||
maxNumResults: 1, | ||
})[0] ?? null | ||
// if current trade is best yet, save it | ||
if (isTradeBetter(bestTradeSoFar, currentTrade, BETTER_TRADE_LESS_HOPS_THRESHOLD)) { | ||
bestTradeSoFar = currentTrade | ||
} | ||
} | ||
return bestTradeSoFar | ||
} | ||
return bestTradeSoFar | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,42 @@ | ||
import { Currency, CurrencyAmount, TradeType } from '@pancakeswap/sdk' | ||
|
||
import { getBestTradeFromV2 } from './getBestTradeFromV2' | ||
import { getBestTradeFromV2ExactIn, getBestTradeFromV2ExactOut } from './getBestTradeFromV2' | ||
import { getBestTradeWithStableSwap } from './getBestTradeWithStableSwap' | ||
import { getStableSwapPairs } from './getStableSwapPairs' | ||
import { TradeWithStableSwap, BestTradeOptions } from './types' | ||
|
||
export async function getBestTrade<TInput extends Currency, TOutput extends Currency>( | ||
amountIn: CurrencyAmount<TInput>, | ||
output: TOutput, | ||
options: BestTradeOptions, | ||
): Promise<TradeWithStableSwap<TInput, TOutput, TradeType> | null> { | ||
const { provider } = options | ||
// TODO invariant check input and output on the same chain | ||
const { | ||
currency: { chainId }, | ||
} = amountIn | ||
|
||
const bestTradeV2 = await getBestTradeFromV2(amountIn, output, options) | ||
if (!bestTradeV2) { | ||
return null | ||
} | ||
import { BestTradeOptions } from './types' | ||
|
||
const stableSwapPairs = getStableSwapPairs(chainId) | ||
const bestTradeWithStableSwap = await getBestTradeWithStableSwap(bestTradeV2, stableSwapPairs, { provider }) | ||
const { outputAmount: outputAmountWithStableSwap } = bestTradeWithStableSwap | ||
export const getBestTradeExactIn = createGetBestTrade(TradeType.EXACT_INPUT) | ||
|
||
// If stable swap is not as good as best trade got from v2, then use v2 | ||
if (outputAmountWithStableSwap.lessThan(bestTradeV2.outputAmount)) { | ||
return bestTradeV2 | ||
} | ||
export const getBestTradeExactOut = createGetBestTrade(TradeType.EXACT_OUTPUT) | ||
|
||
function createGetBestTrade<TTradeType extends TradeType>(tradeType: TTradeType) { | ||
const getBestTradeFromV2 = | ||
tradeType === TradeType.EXACT_INPUT ? getBestTradeFromV2ExactIn : getBestTradeFromV2ExactOut | ||
return async function getBestTrade<In extends Currency, Out extends Currency>( | ||
amountIn: CurrencyAmount<In>, | ||
output: Out, | ||
options: BestTradeOptions, | ||
) { | ||
const { provider } = options | ||
// TODO invariant check input and output on the same chain | ||
const { | ||
currency: { chainId }, | ||
} = amountIn | ||
|
||
const bestTradeV2 = await getBestTradeFromV2(amountIn, output, options) | ||
if (!bestTradeV2) { | ||
return null | ||
} | ||
|
||
return bestTradeWithStableSwap | ||
const stableSwapPairs = getStableSwapPairs(chainId) | ||
const bestTradeWithStableSwap = await getBestTradeWithStableSwap(bestTradeV2, stableSwapPairs, { provider }) | ||
const { outputAmount: outputAmountWithStableSwap } = bestTradeWithStableSwap | ||
|
||
// If stable swap is not as good as best trade got from v2, then use v2 | ||
if (outputAmountWithStableSwap.lessThan(bestTradeV2.outputAmount)) { | ||
return bestTradeV2 | ||
} | ||
|
||
return bestTradeWithStableSwap | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,3 @@ | ||
import { Currency, CurrencyAmount, Pair, Route, TradeType } from '@pancakeswap/sdk' | ||
|
||
export * from './chain' | ||
|
||
export * from './bestTrade' | ||
|
||
export interface StableSwapPair extends Pair { | ||
stableSwapAddress: string | ||
} | ||
|
||
export interface RouteWithStableSwap<TInput extends Currency, TOutput extends Currency> extends Route<TInput, TOutput> { | ||
pairs: (Pair | StableSwapPair)[] | ||
} | ||
|
||
export interface TradeWithStableSwap<TInput extends Currency, TOutput extends Currency, TTradeType extends TradeType> { | ||
tradeType: TTradeType | ||
route: RouteWithStableSwap<TInput, TOutput> | ||
inputAmount: CurrencyAmount<TInput> | ||
outputAmount: CurrencyAmount<TOutput> | ||
} | ||
export * from './stableSwap' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Currency, CurrencyAmount, Pair, Route, TradeType } from '@pancakeswap/sdk' | ||
|
||
export interface StableSwapPair extends Pair { | ||
stableSwapAddress: string | ||
} | ||
|
||
export interface RouteWithStableSwap<TInput extends Currency, TOutput extends Currency> extends Route<TInput, TOutput> { | ||
pairs: (Pair | StableSwapPair)[] | ||
} | ||
|
||
export interface TradeWithStableSwap<TInput extends Currency, TOutput extends Currency, TTradeType extends TradeType> { | ||
tradeType: TTradeType | ||
route: RouteWithStableSwap<TInput, TOutput> | ||
inputAmount: CurrencyAmount<TInput> | ||
outputAmount: CurrencyAmount<TOutput> | ||
} |