diff --git a/packages/api/package.json b/packages/api/package.json index 9ab97fa77..73a04b31d 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@sora-substrate/api", - "version": "1.21.8", + "version": "1.21.9", "license": "Apache-2.0", "main": "./build/index.js", "typings": "./build/index.d.ts", @@ -10,6 +10,6 @@ "dependencies": { "@open-web3/orml-api-derive": "1.1.4", "@polkadot/api": "9.14.2", - "@sora-substrate/types": "1.21.8" + "@sora-substrate/types": "1.21.9" } } diff --git a/packages/connection/package.json b/packages/connection/package.json index 51a033328..02cdca72d 100644 --- a/packages/connection/package.json +++ b/packages/connection/package.json @@ -1,6 +1,6 @@ { "name": "@sora-substrate/connection", - "version": "1.21.8", + "version": "1.21.9", "license": "Apache-2.0", "main": "./build/index.js", "typings": "./build/index.d.ts", @@ -8,6 +8,6 @@ "access": "public" }, "dependencies": { - "@sora-substrate/api": "1.21.8" + "@sora-substrate/api": "1.21.9" } } diff --git a/packages/liquidity-proxy/package.json b/packages/liquidity-proxy/package.json index afb044ad7..be62160fa 100644 --- a/packages/liquidity-proxy/package.json +++ b/packages/liquidity-proxy/package.json @@ -1,6 +1,6 @@ { "name": "@sora-substrate/liquidity-proxy", - "version": "1.21.8", + "version": "1.21.9", "license": "Apache-2.0", "main": "./build/index.js", "typings": "./build/index.d.ts", @@ -8,6 +8,6 @@ "access": "public" }, "dependencies": { - "@sora-substrate/math": "1.21.8" + "@sora-substrate/math": "1.21.9" } } diff --git a/packages/liquidity-proxy/src/types.ts b/packages/liquidity-proxy/src/types.ts index 522a300df..08e838894 100644 --- a/packages/liquidity-proxy/src/types.ts +++ b/packages/liquidity-proxy/src/types.ts @@ -51,8 +51,12 @@ export type SwapQuote = ( outputAssetAddress: string, value: NumberLike, isExchangeB: boolean, + selectedSources: LiquiditySourceTypes[], deduceFee: boolean -) => SwapResult; +) => { + result: SwapResult; + dexId: number; +}; export type QuotePaths = { [key: string]: Array; diff --git a/packages/math/package.json b/packages/math/package.json index 22f94be3e..362937141 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -1,6 +1,6 @@ { "name": "@sora-substrate/math", - "version": "1.21.8", + "version": "1.21.9", "license": "Apache-2.0", "main": "./build/index.js", "typings": "./build/index.d.ts", diff --git a/packages/type-definitions/package.json b/packages/type-definitions/package.json index e3d13e0f4..f4a9be0d1 100644 --- a/packages/type-definitions/package.json +++ b/packages/type-definitions/package.json @@ -1,6 +1,6 @@ { "name": "@sora-substrate/type-definitions", - "version": "1.21.8", + "version": "1.21.9", "license": "Apache-2.0", "main": "./build/index.js", "typings": "./build/index.d.ts", diff --git a/packages/types/package.json b/packages/types/package.json index 783e68adc..8facaa33d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sora-substrate/types", - "version": "1.21.8", + "version": "1.21.9", "license": "Apache-2.0", "main": "./build/index.js", "typings": "./build/index.d.ts", @@ -13,7 +13,7 @@ "@polkadot/api": "9.14.2", "@polkadot/typegen": "9.14.2", "@polkadot/types": "9.14.2", - "@sora-substrate/type-definitions": "1.21.8" + "@sora-substrate/type-definitions": "1.21.9" }, "devDependencies": { "@types/websocket": "^1.0.0", diff --git a/packages/util/package.json b/packages/util/package.json index e267f57c6..0703c7711 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@sora-substrate/util", - "version": "1.21.8", + "version": "1.21.9", "license": "Apache-2.0", "main": "./build/index.js", "typings": "./build/index.d.ts", @@ -9,11 +9,11 @@ }, "dependencies": { "@polkadot/ui-keyring": "2.12.1", - "@sora-substrate/api": "1.21.8", - "@sora-substrate/connection": "1.21.8", - "@sora-substrate/liquidity-proxy": "1.21.8", - "@sora-substrate/math": "1.21.8", - "@sora-substrate/types": "1.21.8", + "@sora-substrate/api": "1.21.9", + "@sora-substrate/connection": "1.21.9", + "@sora-substrate/liquidity-proxy": "1.21.9", + "@sora-substrate/math": "1.21.9", + "@sora-substrate/types": "1.21.9", "axios": "^0.21.1", "crypto-js": "^4.0.0", "lodash": "^4.17.15" diff --git a/packages/util/src/swap/index.ts b/packages/util/src/swap/index.ts index f56ea7399..4f5887c8b 100644 --- a/packages/util/src/swap/index.ts +++ b/packages/util/src/swap/index.ts @@ -1,4 +1,5 @@ import { assert } from '@polkadot/util'; +import isEmpty from 'lodash/fp/isEmpty'; import { combineLatest, map, distinctUntilChanged } from 'rxjs'; import { NumberLike, FPNumber, CodecString } from '@sora-substrate/math'; import { @@ -33,6 +34,12 @@ import { Api } from '../api'; import type { AccountAsset, Asset } from '../assets/types'; import type { ReceiverHistoryItem, SwapTransferBatchData } from './types'; +type SwapQuoteData = { + quote: SwapQuote; + isAvailable: boolean; + liquiditySources: LiquiditySourceTypes[]; +}; + const comparator = (prev: T, curr: T): boolean => JSON.stringify(prev) === JSON.stringify(curr); const toAssetId = (o: Observable): Observable => @@ -453,32 +460,31 @@ export class SwapModule { * Get observable liquidity proxy quote function for two assets * @param firstAssetAddress First swap token address * @param secondAssetAddress Second swap token address - * @param selectedSources Selected liquidity sources for swap (not selected by default) + * @param sources Liquidity sources available for swap (all sources by default) * @param dexId Selected Dex Id */ public async getSwapQuoteObservable( firstAssetAddress: string, secondAssetAddress: string, - selectedSources: LiquiditySourceTypes[] = [], + sources: LiquiditySourceTypes[] = [], dexId = DexId.XOR - ): Promise> { - const dexReservesObservable = await this.subscribeOnReserves( - firstAssetAddress, - secondAssetAddress, - selectedSources, - dexId - ); + ): Promise> { + const dexReservesObservable = await this.subscribeOnReserves(firstAssetAddress, secondAssetAddress, sources, dexId); - const quoteFnObservable = dexReservesObservable.pipe( + const swapQuoteObservable = dexReservesObservable.pipe( map((payload) => { - return ( + const { assetPaths, liquiditySources } = payload.sources; + const isAvailable = !isEmpty(assetPaths) && Object.values(assetPaths).every((paths) => !isEmpty(paths)); + + const quote: SwapQuote = ( inputAssetAddress: string, outputAssetAddress: string, value: NumberLike, isExchangeB: boolean, + selectedSources: LiquiditySourceTypes[] = [], deduceFee = true - ) => - this.getResult( + ) => { + const result = this.getResult( inputAssetAddress, outputAssetAddress, value, @@ -488,10 +494,101 @@ export class SwapModule { dexId, deduceFee ); + + return { result, dexId }; + }; + + return { + quote, + isAvailable, + liquiditySources, + }; + }) + ); + + return swapQuoteObservable; + } + + /** + * Get observable liquidity proxy quote function for two assets across all Dexes + * @param firstAssetAddress First swap token address + * @param secondAssetAddress Second swap token address + * @param sources Liquidity sources for swap (all sources by default) + */ + public async getDexesSwapQuoteObservable( + firstAssetAddress: string, + secondAssetAddress: string, + sources: LiquiditySourceTypes[] = [] + ): Promise> { + const observables: Observable[] = []; + + for (const { dexId } of this.root.dex.dexList) { + const swapQuoteDataObservable = await this.getSwapQuoteObservable( + firstAssetAddress, + secondAssetAddress, + sources, + dexId + ); + + observables.push(swapQuoteDataObservable); + } + + const aggregated = combineLatest(observables).pipe( + map((swapQuoteData) => { + const isAvailable = swapQuoteData.some(({ isAvailable }) => !!isAvailable); + const liquiditySources = [...new Set(swapQuoteData.map(({ liquiditySources }) => liquiditySources).flat(1))]; + const quote: SwapQuote = ( + inputAssetAddress: string, + outputAssetAddress: string, + value: NumberLike, + isExchangeB: boolean, + selectedSources: LiquiditySourceTypes[] = [], + deduceFee = true + ) => { + let bestDexId: number = DexId.XOR; + + const results = swapQuoteData.reduce<{ [dexId: number]: SwapResult }>((buffer, { quote }) => { + const { dexId, result } = quote( + inputAssetAddress, + outputAssetAddress, + value, + isExchangeB, + selectedSources, + deduceFee + ); + + return { ...buffer, [dexId]: result }; + }, {}); + + for (const currentDexId in results) { + const currAmount = FPNumber.fromCodecValue(results[currentDexId].amount); + const bestAmount = FPNumber.fromCodecValue(results[bestDexId].amount); + + if (currAmount.isZero()) continue; + + if ( + (FPNumber.isLessThan(currAmount, bestAmount) && isExchangeB) || + (FPNumber.isLessThan(bestAmount, currAmount) && !isExchangeB) + ) { + bestDexId = +currentDexId; + } + } + + return { + dexId: bestDexId, + result: results[bestDexId], + }; + }; + + return { + quote, + isAvailable, + liquiditySources, + }; }) ); - return quoteFnObservable; + return aggregated; } private calcTxParams(