import { AnyApi , FixedPointNumber , forceToCurrencyName , MaybeCurrency , Token } from '@acala-network/sdk-core' ; import { TradingPair , TradingPairStatus } from '@acala-network/types/interfaces' ; import { StorageKey } from '@polkadot/types' ; import { memoize } from '@polkadot/util' ; import { BehaviorSubject , combineLatest , firstValueFrom , Observable } from 'rxjs' ; import { map , switchMap , filter } from 'rxjs/operators' ; import { TradingPairNotFound } from '..' ; import { TokenProvider } from '../base-provider' ; import { BaseSDK } from '../types' ; import { createStorages } from './storage' ; import { PoolDetail , LiquidityPoolStatus , PoolInfo , UserLiquidity } from './types' ; import { getEstimateAddLiquidityResult } from './utils/get-estimate-add-liquidity-result' ; import { getEstimateRemoveLiquidityResult } from './utils/get-estimate-remove-liquidity-result' ; export class Liquidity implements BaseSDK { private api : AnyApi ; private storages : ReturnType < typeof createStorages > ; private tokenProvider : TokenProvider ; public isReady$ : BehaviorSubject < boolean > ; constructor ( api , tokenProvider ) { this . api = api ; this . storages = createStorages ( this . api ) ; this . tokenProvider = tokenProvider ; this . isReady$ = new BehaviorSubject < boolean > ( true ) ; } public get isReady ( ) : Promise < boolean > { return firstValueFrom ( this . isReady$ . pipe ( filter ( ( i ) => i ) ) ) ; } name description public subscribePoolListByStatus = memoize ( ( status = LiquidityPoolStatus . ENABLED ) : Observable < PoolInfo [ ] > => { const filterByPoolInfos = ( list ) : [ StorageKey < [ TradingPair ] > , TradingPairStatus , LiquidityPoolStatus ] [ ] => { return list . filter ( ( item ) => { if ( status === LiquidityPoolStatus . ALL ) { return true ; } return item [ 2 ] === status ; } ) ; } ; const transformStatus = ( list ) => { return list . map ( ( item ) => { const rawStatus = item [ 1 ] ; const status = rawStatus . isEnabled ? LiquidityPoolStatus . ENABLED : rawStatus . isDisabled ? LiquidityPoolStatus . DISABLED : rawStatus . isProvisioning ? LiquidityPoolStatus . PROVISION : LiquidityPoolStatus . PROVISION ; return [ ... item , status ] as [ StorageKey < [ TradingPair ] > , TradingPairStatus , LiquidityPoolStatus ] ; } ) ; } ; const tokenCollections = new Set < string > ( ) ; return this . storages . tradingPairs ( ) . observable . pipe ( map ( transformStatus ) , map ( filterByPoolInfos ) , switchMap ( ( data ) => { data . forEach ( ( item ) => { const token1 = item [ 0 ] . args [ 0 ] [ 0 ] ; const token2 = item [ 0 ] . args [ 0 ] [ 1 ] ; tokenCollections . add ( forceToCurrencyName ( token1 ) ) ; tokenCollections . add ( forceToCurrencyName ( token2 ) ) ; } ) ; return combineLatest ( Object . fromEntries ( Array . from ( tokenCollections ) . map ( ( item ) => { return [ item , this . tokenProvider . subscribeToken ( item ) ] ; } ) ) ) . pipe ( map ( ( tokens ) => { return data . map ( ( item ) => { const token1 = tokens [ forceToCurrencyName ( item [ 0 ] . args [ 0 ] [ 0 ] ) ] ; const token2 = tokens [ forceToCurrencyName ( item [ 0 ] . args [ 0 ] [ 1 ] ) ] ; return { token : Token . fromTokens ( token1 , token2 ) , pair : [ token1 , token2 ] as [ Token , Token ] , status : item [ 2 ] } ; } ) ; } ) ) ; } ) ) ; } ) ; name description public subscribePoolInfo = memoize ( ( token ) : Observable < PoolInfo > => { const targetName = forceToCurrencyName ( token ) ; return this . subscribePoolListByStatus ( LiquidityPoolStatus . ALL ) . pipe ( map ( ( list ) => { const target = list . find ( ( item ) => forceToCurrencyName ( item . token ) === targetName ) ; if ( target ) return target ; throw new TradingPairNotFound ( targetName ) ; } ) ) ; } ) ; name description public subscribePoolDetail = memoize ( ( token ) : Observable < PoolDetail > => { const detail$ = ( info ) => { const issuance$ = this . storages . issuance ( info . token ) . observable . pipe ( map ( ( data ) => FixedPointNumber . fromInner ( data . toString ( ) , info . token . decimals ) ) ) ; const poolSize$ = this . storages . liquidityPool ( info . token ) . observable . pipe ( map ( ( data ) => { return [ FixedPointNumber . fromInner ( data [ 0 ] . toString ( ) , info . pair [ 0 ] . decimals ) , FixedPointNumber . fromInner ( data [ 1 ] . toString ( ) , info . pair [ 1 ] . decimals ) ] as [ FixedPointNumber , FixedPointNumber ] ; } ) ) ; return combineLatest ( { issuance : issuance$ , poolSize : poolSize$ } ) . pipe ( map ( ( { issuance , poolSize } ) => { return { share : issuance , info , amounts : poolSize } ; } ) ) ; } ; return this . subscribePoolInfo ( token ) . pipe ( switchMap ( detail$ ) ) ; } ) ; name description public subscribeAllEnabledPools ( ) { return this . subscribePoolListByStatus ( LiquidityPoolStatus . ENABLED ) . pipe ( switchMap ( ( data ) => { return combineLatest ( Object . fromEntries ( data . map ( ( item ) => [ forceToCurrencyName ( item . token ) , this . subscribePoolDetail ( item . token ) ] ) ) ) ; } ) ) ; } name description public subscribeUserPool = memoize ( ( address , token ) : Observable < UserLiquidity > => { const getUserLiquidity$ = ( poolDetail ) => { return this . storages . balance ( address , poolDetail . info . token ) . observable . pipe ( map ( ( balance ) => { const userShare = FixedPointNumber . fromInner ( balance . free . toString ( ) , poolDetail . info . token . decimals ) ; const ratio = userShare . div ( poolDetail . share ) ; const owned = [ poolDetail . amounts [ 0 ] . mul ( ratio ) , poolDetail . amounts [ 1 ] . mul ( ratio ) ] as [ FixedPointNumber , FixedPointNumber ] ; return { share : userShare , ratio , poolDetail , owned } ; } ) ) ; } ; return this . subscribePoolDetail ( token ) . pipe ( switchMap ( getUserLiquidity$ ) ) ; } ) ; name description public subscribeAllUserPools = memoize ( ( address ) : Observable < Record < string , UserLiquidity > > => { return this . subscribePoolListByStatus ( LiquidityPoolStatus . ENABLED ) . pipe ( switchMap ( ( data ) => { return combineLatest ( Object . fromEntries ( data . map ( ( item ) => [ forceToCurrencyName ( item . token ) , this . subscribeUserPool ( address , item . token ) ] ) ) ) ; } ) ) ; } ) ; name description public subscribeEstimateAddLiquidityResult = memoize ( ( tokenA , tokenB , inputA , inputB , slippage ? ) => { const dexShareToken = Token . fromTokens ( tokenA , tokenB ) ; return this . subscribePoolDetail ( dexShareToken ) . pipe ( map ( ( detail ) => { return getEstimateAddLiquidityResult ( detail , tokenA , tokenB , inputA , inputB , slippage || 0 ) ; } ) ) ; } ) ; name description public subscribeEstimateRemoveLiquidityResult = memoize ( ( dexShareToken , removeShare , slippage ? ) => { return this . subscribePoolDetail ( dexShareToken ) . pipe ( map ( ( detail ) => { return getEstimateRemoveLiquidityResult ( detail , removeShare , slippage || 0 ) ; } ) ) ; } ) ; name description public susbscribePoolSizeOfShares = memoize ( ( dexShareToken , share ) => { return this . subscribePoolDetail ( dexShareToken ) . pipe ( map ( ( detail ) => { const { share : totalShare , amounts } = detail ; const ratio = share . div ( totalShare ) ; return { share : share , ratio : ratio , amounts : [ amounts [ 0 ] . mul ( ratio ) , amounts [ 1 ] . mul ( ratio ) ] as [ FixedPointNumber , FixedPointNumber ] , poolDetail : detail } ; } ) ) ; } ) ; }