import { AffiliateFeeAmount , AffiliateFeeType , AltRfqMakerAssetOfferings , artifacts , AssetSwapperContractAddresses , BlockParamLiteral , ContractAddresses , ERC20BridgeSource , FakeTakerContract , GetMarketOrdersRfqOpts , Orderbook , RfqFirmQuoteValidator , SwapQuote , SwapQuoteConsumer , SwapQuoteGetOutputOpts , SwapQuoter , SwapQuoteRequestOpts , SwapQuoterOpts , } from '@0x/asset-swapper' ; import { NATIVE_FEE_TOKEN_BY_CHAIN_ID } from '@0x/asset-swapper/lib/src/utils/market_operation_utils/constants' ; import { ChainId } from '@0x/contract-addresses' ; import { WETH9Contract } from '@0x/contract-wrappers' ; import { ETH_TOKEN_ADDRESS , RevertError } from '@0x/protocol-utils' ; import { getTokenMetadataIfExists , TokenMetadatasForChains } from '@0x/token-metadata' ; import { MarketOperation , PaginatedCollection } from '@0x/types' ; import { BigNumber , decodeThrownErrorAsRevertError } from '@0x/utils' ; import { TxData , Web3Wrapper } from '@0x/web3-wrapper' ; import axios from 'axios' ; import { SupportedProvider } from 'ethereum-types' ; import * as _ from 'lodash' ; import { ALT_RFQ_MM_API_KEY , ALT_RFQ_MM_ENDPOINT , ASSET_SWAPPER_MARKET_ORDERS_OPTS , ASSET_SWAPPER_MARKET_ORDERS_OPTS_NO_VIP , CHAIN_ID , RFQT_REQUEST_MAX_RESPONSE_MS , RFQ_PROXY_ADDRESS , RFQ_PROXY_PORT , SWAP_QUOTER_OPTS , UNWRAP_QUOTE_GAS , UNWRAP_WETH_GAS , WRAP_ETH_GAS , WRAP_QUOTE_GAS , } from '../config' ; import { DEFAULT_VALIDATION_GAS_LIMIT , GAS_LIMIT_BUFFER_MULTIPLIER , NULL_ADDRESS , NULL_BYTES , ONE , ONE_MINUTE_MS , ZERO , } from '../constants' ; import { GasEstimationError , InsufficientFundsError } from '../errors' ; import { logger } from '../logger' ; import { AffiliateFee , BucketedPriceDepth , CalaculateMarketDepthParams , GetSwapQuoteParams , GetSwapQuoteResponse , Price , SwapQuoteResponsePartialTransaction , TokenMetadata , TokenMetadataOptionalSymbol , } from '../types' ; import { altMarketResponseToAltOfferings } from '../utils/alt_mm_utils' ; import { marketDepthUtils } from '../utils/market_depth_utils' ; import { METRICS_PROXY } from '../utils/metrics_service' ; import { paginationUtils } from '../utils/pagination_utils' ; import { PairsManager } from '../utils/pairs_manager' ; import { createResultCache } from '../utils/result_cache' ; import { RfqDynamicBlacklist } from '../utils/rfq_dyanmic_blacklist' ; import { SAMPLER_METRICS } from '../utils/sampler_metrics' ; import { serviceUtils } from '../utils/service_utils' ; import { utils } from '../utils/utils' ; export class SwapService { private readonly _provider : SupportedProvider ; private readonly _fakeTaker : FakeTakerContract ; private readonly _swapQuoteConsumer : SwapQuoteConsumer ; private readonly _web3Wrapper : Web3Wrapper ; private readonly _wethContract : WETH9Contract ; private readonly _contractAddresses : ContractAddresses ; private readonly _firmQuoteValidator : RfqFirmQuoteValidator | undefined ; private readonly _swapQuoterOpts : Partial < SwapQuoterOpts > ; private _altRfqMarketsCache : any ; private _swapQuoter : SwapQuoter ; private static _getSwapQuotePrice ( buyAmount , buyTokenDecimals , sellTokenDecimals , swapQuote , affiliateFee ) { const { makerAmount , totalTakerAmount } = swapQuote . bestCaseQuoteInfo ; const { totalTakerAmount : guaranteedTotalTakerAmount , makerAmount : guaranteedMakerAmount } = swapQuote . worstCaseQuoteInfo ; const unitMakerAmount = Web3Wrapper . toUnitAmount ( makerAmount , buyTokenDecimals ) ; const unitTakerAmount = Web3Wrapper . toUnitAmount ( totalTakerAmount , sellTokenDecimals ) ; const guaranteedUnitMakerAmount = Web3Wrapper . toUnitAmount ( guaranteedMakerAmount , buyTokenDecimals ) ; const guaranteedUnitTakerAmount = Web3Wrapper . toUnitAmount ( guaranteedTotalTakerAmount , sellTokenDecimals ) ; const affiliateFeeUnitMakerAmount = guaranteedUnitMakerAmount . times ( affiliateFee . buyTokenPercentageFee ) ; const isSelling = buyAmount === undefined ; const roundingStrategy = isSelling ? BigNumber . ROUND_FLOOR : BigNumber . ROUND_CEIL ; const price = isSelling ? unitMakerAmount . minus ( affiliateFeeUnitMakerAmount ) . dividedBy ( unitTakerAmount ) . decimalPlaces ( buyTokenDecimals , roundingStrategy ) : unitTakerAmount . dividedBy ( unitMakerAmount . minus ( affiliateFeeUnitMakerAmount ) ) . decimalPlaces ( sellTokenDecimals , roundingStrategy ) ; const guaranteedPrice = isSelling ? guaranteedUnitMakerAmount . minus ( affiliateFeeUnitMakerAmount ) . dividedBy ( guaranteedUnitTakerAmount ) . decimalPlaces ( buyTokenDecimals , roundingStrategy ) : guaranteedUnitTakerAmount . dividedBy ( guaranteedUnitMakerAmount . minus ( affiliateFeeUnitMakerAmount ) ) . decimalPlaces ( sellTokenDecimals , roundingStrategy ) ; return { price , guaranteedPrice , } ; } constructor ( orderbook , provider , contractAddresses , firmQuoteValidator ? , rfqDynamicBlacklist ? , private readonly _pairsManager ? ) { this . _provider = provider ; this . _firmQuoteValidator = firmQuoteValidator ; let axiosOpts = { } ; if ( RFQ_PROXY_ADDRESS !== undefined && RFQ_PROXY_PORT !== undefined ) { axiosOpts = { proxy : { host : RFQ_PROXY_ADDRESS , port : RFQ_PROXY_PORT , } , } ; } this . _swapQuoterOpts = { ... SWAP_QUOTER_OPTS , rfqt : { ... SWAP_QUOTER_OPTS . rfqt ! , warningLogger : logger . warn . bind ( logger ) , infoLogger : logger . info . bind ( logger ) , axiosInstanceOpts : axiosOpts , metricsProxy : METRICS_PROXY , } , contractAddresses , } ; if ( this . _swapQuoterOpts . rfqt !== undefined && rfqDynamicBlacklist !== undefined ) { this . _swapQuoterOpts . rfqt . txOriginBlacklist = rfqDynamicBlacklist ; } if ( CHAIN_ID === ChainId . Ganache ) { this . _swapQuoterOpts . samplerOverrides = { block : BlockParamLiteral . Latest , overrides : { } , to : contractAddresses . erc20BridgeSampler , ... ( this . _swapQuoterOpts . samplerOverrides || { } ) , } ; } this . _swapQuoter = new SwapQuoter ( this . _provider , orderbook , this . _swapQuoterOpts ) ; this . _renewSwapQuoter ( ) ; this . _pairsManager ?. on ( PairsManager . REFRESHED_EVENT , ( ) => { this . _renewSwapQuoter ( ) ; } ) ; this . _swapQuoteConsumer = new SwapQuoteConsumer ( this . _swapQuoterOpts ) ; this . _web3Wrapper = new Web3Wrapper ( this . _provider ) ; this . _contractAddresses = contractAddresses ; this . _wethContract = new WETH9Contract ( this . _contractAddresses . etherToken , this . _provider ) ; this . _fakeTaker = new FakeTakerContract ( NULL_ADDRESS , this . _provider ) ; } public async calculateSwapQuoteAsync ( params ) { const { takerAddress , sellAmount , buyAmount , buyToken , sellToken , slippagePercentage , gasPrice : providedGasPrice , isMetaTransaction , isETHSell , isETHBuy , excludedSources , includedSources , integrator , rfqt , affiliateAddress , affiliateFee , includePriceComparisons , skipValidation , shouldSellEntireBalance , } = params ; let _rfqt ; const shouldEnableRfqt = integrator !== undefined && ( isETHSell || takerAddress !== undefined || ( rfqt && rfqt . isIndicative ) ) ; if ( shouldEnableRfqt ) { const altRfqAssetOfferings = await this . _getAltMarketOfferingsAsync ( 1500 ) ; _rfqt = { ... rfqt , intentOnFilling : rfqt && rfqt . intentOnFilling ? true : false , integrator : integrator ! , makerEndpointMaxResponseTimeMs : RFQT_REQUEST_MAX_RESPONSE_MS , takerAddress : NULL_ADDRESS , txOrigin : takerAddress ! , firmQuoteValidator : this . _firmQuoteValidator , altRfqAssetOfferings , } ; } const shouldGenerateQuoteReport = rfqt && rfqt . intentOnFilling ; let swapQuoteRequestOpts ; if ( isMetaTransaction || shouldSellEntireBalance || affiliateFee . feeType === AffiliateFeeType . PercentageFee ) { swapQuoteRequestOpts = ASSET_SWAPPER_MARKET_ORDERS_OPTS_NO_VIP ; } else { swapQuoteRequestOpts = ASSET_SWAPPER_MARKET_ORDERS_OPTS ; } const assetSwapperOpts = { ... swapQuoteRequestOpts , bridgeSlippage : slippagePercentage , gasPrice : providedGasPrice , excludedSources : swapQuoteRequestOpts . excludedSources ?. concat ( ... ( excludedSources || [ ] ) ) , includedSources , rfqt : _rfqt , shouldGenerateQuoteReport , shouldIncludePriceComparisonsReport : ! ! includePriceComparisons , samplerMetrics : SAMPLER_METRICS , } ; const marketSide = sellAmount !== undefined ? MarketOperation . Sell : MarketOperation . Buy ; const amount = marketSide === MarketOperation . Sell ? sellAmount : buyAmount ! . times ( affiliateFee . buyTokenPercentageFee + 1 ) . integerValue ( BigNumber . ROUND_DOWN ) ; const swapQuote = await this . _swapQuoter . getSwapQuoteAsync ( buyToken , sellToken , amount ! , marketSide , assetSwapperOpts ) ; const { makerAmount , totalTakerAmount , protocolFeeInWeiAmount : bestCaseProtocolFee , } = swapQuote . bestCaseQuoteInfo ; const { protocolFeeInWeiAmount : protocolFee , gas : worstCaseGas } = swapQuote . worstCaseQuoteInfo ; const { gasPrice , sourceBreakdown , quoteReport , priceComparisonsReport , extendedQuoteReportSources } = swapQuote ; const { gasCost : affiliateFeeGasCost , buyTokenFeeAmount , sellTokenFeeAmount , } = serviceUtils . getAffiliateFeeAmounts ( swapQuote , affiliateFee ) ; const { to , value , data , decodedUniqueId , gasOverhead } = await this . _getSwapQuotePartialTransactionAsync ( swapQuote , isETHSell , isETHBuy , isMetaTransaction , shouldSellEntireBalance , affiliateAddress , { recipient : affiliateFee . recipient , feeType : affiliateFee . feeType , buyTokenFeeAmount , sellTokenFeeAmount , } ) ; let conservativeBestCaseGasEstimate = new BigNumber ( worstCaseGas ) . plus ( affiliateFeeGasCost ) . plus ( isETHSell ? WRAP_ETH_GAS : 0 ) . plus ( isETHBuy ? UNWRAP_WETH_GAS : 0 ) ; const isNativeIncluded = swapQuote . sourceBreakdown . Native !== undefined ; const isFirmQuote = ! _rfqt ?. isIndicative ; const canEstimateGas = isFirmQuote || ! isNativeIncluded ; if ( takerAddress && ! skipValidation && canEstimateGas ) { let estimateGasCallResult = await this . _estimateGasOrThrowRevertErrorAsync ( { to , data , from : takerAddress , value , gasPrice , } ) ; estimateGasCallResult = estimateGasCallResult . plus ( gasOverhead ) ; conservativeBestCaseGasEstimate = BigNumber . max ( estimateGasCallResult . times ( GAS_LIMIT_BUFFER_MULTIPLIER ) . integerValue ( ) , conservativeBestCaseGasEstimate ) ; } const hasUndeterministicFills = _ . flatten ( swapQuote . orders . map ( ( order ) => order . fills ) ) . some ( ( fill ) => [ ERC20BridgeSource . Native , ERC20BridgeSource . MultiBridge ] . includes ( fill . source ) ) ; const undeterministicMultiplier = hasUndeterministicFills ? GAS_LIMIT_BUFFER_MULTIPLIER : 1 ; const worstCaseGasEstimate = conservativeBestCaseGasEstimate . times ( undeterministicMultiplier ) . integerValue ( ) ; const { makerTokenDecimals , takerTokenDecimals } = swapQuote ; const { price , guaranteedPrice } = SwapService . _getSwapQuotePrice ( buyAmount , makerTokenDecimals , takerTokenDecimals , swapQuote , affiliateFee ) ; let adjustedValue = value ; adjustedValue = isETHSell ? protocolFee . plus ( swapQuote . worstCaseQuoteInfo . takerAmount ) : protocolFee ; const allowanceTarget = isETHSell ? NULL_ADDRESS : this . _contractAddresses . exchangeProxy ; const { takerAmountPerEth : takerTokenToEthRate , makerAmountPerEth : makerTokenToEthRate } = swapQuote ; const wethToken = getTokenMetadataIfExists ( 'WETH' , CHAIN_ID ) ! ; const sellTokenToEthRate = takerTokenToEthRate . times ( new BigNumber ( 10 ) . pow ( wethToken . decimals - takerTokenDecimals ) ) . decimalPlaces ( takerTokenDecimals ) ; const buyTokenToEthRate = makerTokenToEthRate . times ( new BigNumber ( 10 ) . pow ( wethToken . decimals - makerTokenDecimals ) ) . decimalPlaces ( makerTokenDecimals ) ; const apiSwapQuote = { chainId : CHAIN_ID , price , guaranteedPrice , to , data , value : adjustedValue , gas : worstCaseGasEstimate , estimatedGas : conservativeBestCaseGasEstimate , from : takerAddress , gasPrice , protocolFee , minimumProtocolFee : BigNumber . min ( protocolFee , bestCaseProtocolFee ) , buyTokenAddress : isETHBuy ? ETH_TOKEN_ADDRESS : buyToken , sellTokenAddress : isETHSell ? ETH_TOKEN_ADDRESS : sellToken , buyAmount : makerAmount . minus ( buyTokenFeeAmount ) , sellAmount : totalTakerAmount , sources : serviceUtils . convertSourceBreakdownToArray ( sourceBreakdown ) , orders : swapQuote . orders , allowanceTarget , decodedUniqueId , extendedQuoteReportSources , sellTokenToEthRate , buyTokenToEthRate , quoteReport , priceComparisonsReport , } ; return apiSwapQuote ; } public async getSwapQuoteForWrapAsync ( params ) { return this . _getSwapQuoteForNativeWrappedAsync ( params , false ) ; } public async getSwapQuoteForUnwrapAsync ( params ) { return this . _getSwapQuoteForNativeWrappedAsync ( params , true ) ; } public async getTokenPricesAsync ( sellToken , unitAmount , page , perPage ) { const takerToken = sellToken . tokenAddress ; const queryTokenData = TokenMetadatasForChains . filter ( ( m ) => m . symbol !== sellToken . symbol ) . filter ( ( m ) => m . tokenAddresses [ CHAIN_ID ] !== NULL_ADDRESS ) ; const paginatedTokens = paginationUtils . paginate ( queryTokenData , page , perPage ) ; const chunkSize = 20 ; const queryTokenChunks = _ . chunk ( paginatedTokens . records , chunkSize ) ; const allResults = ( await Promise . all ( queryTokenChunks . map ( async ( tokens ) => { const makerTokens = tokens . map ( ( t ) => t . tokenAddresses [ CHAIN_ID ] ) ; const amounts = tokens . map ( ( t ) => Web3Wrapper . toBaseUnitAmount ( unitAmount , t . decimals ) ) ; const quotes = await this . _swapQuoter . getBatchMarketBuySwapQuoteAsync ( makerTokens , takerToken , amounts , { ... ASSET_SWAPPER_MARKET_ORDERS_OPTS , bridgeSlippage : 0 , maxFallbackSlippage : 0 , numSamples : 1 , } ) ; return quotes ; } ) ) ) . filter ( ( x ) => x !== undefined ) . reduce ( ( acc , x ) => acc . concat ( x ) , [ ] ) ; const prices = allResults . map ( ( quote , i ) => { const buyTokenDecimals = new BigNumber ( quote . makerTokenDecimals ) . toNumber ( ) ; const sellTokenDecimals = new BigNumber ( quote . takerTokenDecimals ) . toNumber ( ) ; const symbol = queryTokenData . find ( ( data ) => data . tokenAddresses [ CHAIN_ID ] === quote . makerToken ) ?. symbol ; const { makerAmount , totalTakerAmount } = quote . bestCaseQuoteInfo ; const unitMakerAmount = Web3Wrapper . toUnitAmount ( makerAmount , buyTokenDecimals ) ; const unitTakerAmount = Web3Wrapper . toUnitAmount ( totalTakerAmount , sellTokenDecimals ) ; const price = unitTakerAmount . dividedBy ( unitMakerAmount ) . decimalPlaces ( sellTokenDecimals , BigNumber . ROUND_CEIL ) ; return { symbol , price , } ; } ) . filter ( ( p ) => p ) as Price [ ] ; const wethData = prices . find ( ( p ) => p . symbol === 'WETH' ) ; if ( wethData ) { prices . push ( { ... wethData , symbol : 'ETH' } ) ; } return { ... paginatedTokens , records : prices } ; } public async calculateMarketDepthAsync ( params ) { const { buyToken : buyToken , sellToken : sellToken , sellAmount , numSamples , sampleDistributionBase , excludedSources , includedSources , } = params ; const marketDepth = await this . _swapQuoter . getBidAskLiquidityForMakerTakerAssetPairAsync ( buyToken , sellToken , sellAmount , { numSamples , excludedSources : [ ... ( excludedSources || [ ] ) , ERC20BridgeSource . MultiBridge , ERC20BridgeSource . MultiHop , ] , includedSources , sampleDistributionBase , } ) ; const maxEndSlippagePercentage = 20 ; const scalePriceByDecimals = ( priceDepth ) => priceDepth . map ( ( b ) => ( { ... b , price : b . price . times ( new BigNumber ( 10 ) . pow ( marketDepth . takerTokenDecimals - marketDepth . makerTokenDecimals ) ) , } ) ) ; const askDepth = scalePriceByDecimals ( marketDepthUtils . calculateDepthForSide ( marketDepth . asks , MarketOperation . Sell , numSamples * 2 , sampleDistributionBase , maxEndSlippagePercentage ) ) ; const bidDepth = scalePriceByDecimals ( marketDepthUtils . calculateDepthForSide ( marketDepth . bids , MarketOperation . Buy , numSamples * 2 , sampleDistributionBase , maxEndSlippagePercentage ) ) ; return { asks : { depth : askDepth } , bids : { depth : bidDepth } , buyToken : { tokenAddress : buyToken , decimals : marketDepth . makerTokenDecimals , } , sellToken : { tokenAddress : sellToken , decimals : marketDepth . takerTokenDecimals , } , } ; } private async _getSwapQuoteForNativeWrappedAsync ( params , isUnwrap ) { const { takerAddress , buyToken , sellToken , buyAmount , sellAmount , affiliateAddress , gasPrice : providedGasPrice , } = params ; const amount = buyAmount || sellAmount ; if ( amount === undefined ) { throw new Error ( 'sellAmount or buyAmount required' ) ; } const data = ( isUnwrap ? this . _wethContract . withdraw ( amount ) : this . _wethContract . deposit ( ) ) . getABIEncodedTransactionData ( ) ; const value = isUnwrap ? ZERO : amount ; const attributedCalldata = serviceUtils . attributeCallData ( data , affiliateAddress ) ; const gasPrice = providedGasPrice || ( await this . _swapQuoter . getGasPriceEstimationOrThrowAsync ( ) ) ; const gasEstimate = isUnwrap ? UNWRAP_QUOTE_GAS : WRAP_QUOTE_GAS ; const apiSwapQuote = { chainId : CHAIN_ID , price : ONE , guaranteedPrice : ONE , to : NATIVE_FEE_TOKEN_BY_CHAIN_ID [ CHAIN_ID ] , data : attributedCalldata . affiliatedData , decodedUniqueId : attributedCalldata . decodedUniqueId , value , gas : gasEstimate , estimatedGas : gasEstimate , from : takerAddress , gasPrice , protocolFee : ZERO , minimumProtocolFee : ZERO , buyTokenAddress : buyToken , sellTokenAddress : sellToken , buyAmount : amount , sellAmount : amount , sources : [ ] , orders : [ ] , sellTokenToEthRate : new BigNumber ( 1 ) , buyTokenToEthRate : new BigNumber ( 1 ) , allowanceTarget : NULL_ADDRESS , } ; return apiSwapQuote ; } private async _estimateGasOrThrowRevertErrorAsync ( txData ) { let revertError ; let gasEstimate = ZERO ; let callResult = { success : false , resultData : NULL_BYTES , gasUsed : ZERO } ; let callResultGanacheRaw ; try { if ( CHAIN_ID === ChainId . Ganache ) { callResult . success = true ; const gas = await this . _web3Wrapper . estimateGasAsync ( txData ) . catch ( ( _e ) => { callResult . success = false ; return DEFAULT_VALIDATION_GAS_LIMIT ; } ) ; callResultGanacheRaw = await this . _web3Wrapper . callAsync ( { ... txData , gas , } ) ; callResult . resultData = callResultGanacheRaw ; callResult . gasUsed = new BigNumber ( gas ) ; gasEstimate = new BigNumber ( gas ) ; } else { const { data , to , ... rest } = txData ; callResult = await this . _fakeTaker . execute ( to ! , data ! ) . callAsync ( { ... rest , to : txData . from ! , gasPrice : 0 , overrides : { [ txData . from ! ] : { code : _ . get ( artifacts . FakeTaker , 'compilerOutput.evm.deployedBytecode.object' ) , } , } , } ) ; } } catch ( e ) { if ( e . message && /insufficient funds/ . test ( e . message ) ) { throw new InsufficientFundsError ( ) ; } if ( e . message && /execution reverted/ . test ( e . message ) && e . data ) { try { revertError = RevertError . decode ( e . data , false ) ; } catch ( e ) { logger . error ( `Could not decode revert error: ${ e }` ) ; throw new Error ( e . message ) ; } } else { try { revertError = decodeThrownErrorAsRevertError ( e ) ; } catch ( e ) { } } if ( revertError ) { throw revertError ; } } try { if ( callResultGanacheRaw ) { revertError = RevertError . decode ( callResultGanacheRaw , false ) ; } else if ( callResult ! && ! callResult . success ) { revertError = RevertError . decode ( callResult . resultData , false ) ; } } catch ( e ) { } if ( revertError ) { throw revertError ; } gasEstimate = callResult . gasUsed . plus ( utils . calculateCallDataGas ( txData . data ! ) ) ; if ( ! callResult . success ) { throw new GasEstimationError ( ) ; } return gasEstimate ; } private async _getSwapQuotePartialTransactionAsync ( swapQuote , isFromETH , isToETH , isMetaTransaction , shouldSellEntireBalance , affiliateAddress , affiliateFee ) { const opts = { extensionContractOpts : { isFromETH , isToETH , isMetaTransaction , shouldSellEntireBalance , affiliateFee } , } ; const { calldataHexString : data , ethAmount : value , toAddress : to , gasOverhead , } = await this . _swapQuoteConsumer . getCalldataOrThrowAsync ( swapQuote , opts ) ; const { affiliatedData , decodedUniqueId } = serviceUtils . attributeCallData ( data , affiliateAddress ) ; return { to , value , data : affiliatedData , decodedUniqueId , gasOverhead , } ; } private async _getAltMarketOfferingsAsync ( timeoutMs ) { if ( ! this . _altRfqMarketsCache ) { this . _altRfqMarketsCache = createResultCache < AltRfqMakerAssetOfferings > ( async ( ) => { if ( ALT_RFQ_MM_ENDPOINT === undefined || ALT_RFQ_MM_API_KEY === undefined ) { return { } ; } try { const response = await axios . get ( `${ ALT_RFQ_MM_ENDPOINT }/markets` , { headers : { Authorization : `Bearer ${ ALT_RFQ_MM_API_KEY }` } , timeout : timeoutMs , } ) ; return altMarketResponseToAltOfferings ( response . data , ALT_RFQ_MM_ENDPOINT ) ; } catch ( err ) { logger . warn ( `error fetching alt RFQ markets: ${ err }` ) ; return { } ; } } , ONE_MINUTE_MS * 360 ) ; } return ( await this . _altRfqMarketsCache . getResultAsync ( ) ) . result ; } /**
     * Update to a new SwapQuoter instance with the newest RFQt assets offerings
     */ private _renewSwapQuoter ( ) { if ( this . _pairsManager !== undefined && this . _swapQuoterOpts . rfqt !== undefined ) { this . _swapQuoterOpts . rfqt . makerAssetOfferings = this . _pairsManager . getRfqtMakerOfferingsForRfqOrder ( ) ; this . _swapQuoter = new SwapQuoter ( this . _swapQuoter . provider , this . _swapQuoter . orderbook , this . _swapQuoterOpts ) ; } } }