import { AffiliateFeeType , ERC20BridgeSource , SELL_SOURCE_FILTER_BY_CHAIN_ID , SwapQuote , SwapQuoteOrdersBreakdown , } from '@0x/asset-swapper' ; import { AbiEncoder , BigNumber } from '@0x/utils' ; import * as _ from 'lodash' ; import { CHAIN_ID , FEE_RECIPIENT_ADDRESS } from '../config' ; import { AFFILIATE_FEE_TRANSFORMER_GAS , HEX_BASE , NULL_ADDRESS , ONE_SECOND_MS , PERCENTAGE_SIG_DIGITS , POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS , ZERO , } from '../constants' ; import { AffiliateFee , AffiliateFeeAmounts , GetSwapQuoteResponseLiquiditySource } from '../types' ; import { numberUtils } from './number_utils' ; export const serviceUtils = { attributeCallData ( data , affiliateAddress ? ) { const affiliateAddressOrDefault = affiliateAddress ? affiliateAddress : FEE_RECIPIENT_ADDRESS ; const affiliateCallDataEncoder = new AbiEncoder . Method ( { constant : true , outputs : [ ] , name : 'ZeroExAPIAffiliate' , inputs : [ { name : 'affiliate' , type : 'address' } , { name : 'timestamp' , type : 'uint256' } , ] , payable : false , stateMutability : 'view' , type : 'function' , } ) ; const timestampInSeconds = new BigNumber ( Date . now ( ) / ONE_SECOND_MS ) . integerValue ( ) ; const hexTimestamp = timestampInSeconds . toString ( HEX_BASE ) ; const randomNumber = numberUtils . randomHexNumberOfLength ( 10 ) ; const uniqueIdentifier = new BigNumber ( `${ randomNumber }${ hexTimestamp }` , HEX_BASE ) ; const encodedAffiliateData = affiliateCallDataEncoder . encode ( [ affiliateAddressOrDefault , uniqueIdentifier ] ) ; const affiliatedData = `${ data }${ encodedAffiliateData . slice ( 2 ) }` ; return { affiliatedData , decodedUniqueId : `${ randomNumber }-${ timestampInSeconds }` } ; } , param currentExcludedSources param apiKey param allowedApiKeys returns determineExcludedSources ( currentExcludedSources , apiKey , allowedApiKeys ) { const isWildcardEnabled = allowedApiKeys . length === 1 && allowedApiKeys [ 0 ] === '*' ; const isAPIKeyEnabled = isWildcardEnabled || ( apiKey && allowedApiKeys . includes ( apiKey ) ) ; if ( ! isAPIKeyEnabled && ! currentExcludedSources . includes ( ERC20BridgeSource . LiquidityProvider ) ) { return currentExcludedSources . concat ( ERC20BridgeSource . LiquidityProvider ) ; } return currentExcludedSources ; } , convertSourceBreakdownToArray ( sourceBreakdown ) { const defaultSourceBreakdown = Object . assign ( { } , ... Object . values ( SELL_SOURCE_FILTER_BY_CHAIN_ID [ CHAIN_ID ] . sources ) . map ( ( s ) => ( { [ s ] : ZERO } ) ) ) ; return Object . entries ( { ... defaultSourceBreakdown , ... sourceBreakdown } ) . reduce < GetSwapQuoteResponseLiquiditySource [ ] > ( ( acc , [ source , breakdown ] ) => { let obj ; if ( source === ERC20BridgeSource . MultiHop && ! BigNumber . isBigNumber ( breakdown ) ) { obj = { ... breakdown ! , name : ERC20BridgeSource . MultiHop , proportion : new BigNumber ( breakdown ! . proportion . toPrecision ( PERCENTAGE_SIG_DIGITS ) ) , } ; } else { obj = { name : source === ERC20BridgeSource . Native ? '0x' : source , proportion : new BigNumber ( ( breakdown as BigNumber ) . toPrecision ( PERCENTAGE_SIG_DIGITS ) ) , } ; } return [ ... acc , obj ] ; } , [ ] ) ; } , getAffiliateFeeAmounts ( quote , fee ) { if ( fee . feeType === AffiliateFeeType . None || fee . recipient === NULL_ADDRESS || fee . recipient === '' ) { return { sellTokenFeeAmount : ZERO , buyTokenFeeAmount : ZERO , gasCost : ZERO , } ; } const minBuyAmount = quote . worstCaseQuoteInfo . makerAmount ; const buyTokenFeeAmount = minBuyAmount . times ( fee . buyTokenPercentageFee ) . dividedBy ( fee . buyTokenPercentageFee + 1 ) . integerValue ( BigNumber . ROUND_DOWN ) ; return { sellTokenFeeAmount : ZERO , buyTokenFeeAmount , gasCost : fee . feeType === AffiliateFeeType . PercentageFee ? AFFILIATE_FEE_TRANSFORMER_GAS : POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS , } ; } , } ;