@0x/asset-swapper: hack together basic sampler service integration
This commit is contained in:
@@ -80,6 +80,7 @@
|
|||||||
"@ethersproject/contracts": "^5.0.1",
|
"@ethersproject/contracts": "^5.0.1",
|
||||||
"@ethersproject/providers": "^5.0.4",
|
"@ethersproject/providers": "^5.0.4",
|
||||||
"@ethersproject/strings": "^5.0.10",
|
"@ethersproject/strings": "^5.0.10",
|
||||||
|
"@open-rpc/client-js": "^1.7.1",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"axios-mock-adapter": "^1.19.0",
|
"axios-mock-adapter": "^1.19.0",
|
||||||
"cream-sor": "^0.3.3",
|
"cream-sor": "^0.3.3",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { ChainId } from '@0x/contract-addresses';
|
|
||||||
import { SignatureType } from '@0x/protocol-utils';
|
import { SignatureType } from '@0x/protocol-utils';
|
||||||
import { BigNumber, logUtils } from '@0x/utils';
|
import { BigNumber, logUtils } from '@0x/utils';
|
||||||
|
|
||||||
@@ -11,12 +10,10 @@ import {
|
|||||||
RfqRequestOpts,
|
RfqRequestOpts,
|
||||||
SwapQuoteGetOutputOpts,
|
SwapQuoteGetOutputOpts,
|
||||||
SwapQuoteRequestOpts,
|
SwapQuoteRequestOpts,
|
||||||
SwapQuoterOpts,
|
|
||||||
} from './types';
|
} from './types';
|
||||||
import {
|
import {
|
||||||
DEFAULT_GET_MARKET_ORDERS_OPTS,
|
DEFAULT_GET_MARKET_ORDERS_OPTS,
|
||||||
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID,
|
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID,
|
||||||
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
|
||||||
} from './utils/market_operation_utils/constants';
|
} from './utils/market_operation_utils/constants';
|
||||||
|
|
||||||
const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json';
|
const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json';
|
||||||
@@ -43,20 +40,6 @@ const PROTOCOL_FEE_MULTIPLIER = new BigNumber(0);
|
|||||||
// default 50% buffer for selecting native orders to be aggregated with other sources
|
// default 50% buffer for selecting native orders to be aggregated with other sources
|
||||||
const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5;
|
const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5;
|
||||||
|
|
||||||
const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
|
||||||
chainId: ChainId.Mainnet,
|
|
||||||
orderRefreshIntervalMs: 10000, // 10 seconds
|
|
||||||
...DEFAULT_ORDER_PRUNER_OPTS,
|
|
||||||
samplerGasLimit: 500e6,
|
|
||||||
ethGasStationUrl: ETH_GAS_STATION_API_URL,
|
|
||||||
rfqt: {
|
|
||||||
integratorsWhitelist: [],
|
|
||||||
makerAssetOfferings: {},
|
|
||||||
txOriginBlacklist: new Set(),
|
|
||||||
},
|
|
||||||
tokenAdjacencyGraph: DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID[ChainId.Mainnet],
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts = {
|
const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts = {
|
||||||
isFromETH: false,
|
isFromETH: false,
|
||||||
isToETH: false,
|
isToETH: false,
|
||||||
@@ -111,7 +94,6 @@ export const constants = {
|
|||||||
ONE_AMOUNT: new BigNumber(1),
|
ONE_AMOUNT: new BigNumber(1),
|
||||||
ONE_SECOND_MS,
|
ONE_SECOND_MS,
|
||||||
ONE_MINUTE_MS,
|
ONE_MINUTE_MS,
|
||||||
DEFAULT_SWAP_QUOTER_OPTS,
|
|
||||||
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID,
|
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID,
|
||||||
DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
||||||
DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS,
|
DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS,
|
||||||
|
|||||||
@@ -95,9 +95,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
|
|
||||||
private readonly _exchangeProxy: IZeroExContract;
|
private readonly _exchangeProxy: IZeroExContract;
|
||||||
|
|
||||||
constructor(public readonly contractAddresses: ContractAddresses, options: Partial<SwapQuoteConsumerOpts> = {}) {
|
constructor(public readonly contractAddresses: ContractAddresses, options: SwapQuoteConsumerOpts) {
|
||||||
const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
|
const { chainId } = options;
|
||||||
assert.isNumber('chainId', chainId);
|
|
||||||
this.chainId = chainId;
|
this.chainId = chainId;
|
||||||
this.contractAddresses = contractAddresses;
|
this.contractAddresses = contractAddresses;
|
||||||
this._exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, FAKE_PROVIDER);
|
this._exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, FAKE_PROVIDER);
|
||||||
@@ -276,62 +275,62 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
// if (
|
||||||
this.chainId === ChainId.Mainnet &&
|
// this.chainId === ChainId.Mainnet &&
|
||||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve]) &&
|
// isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve]) &&
|
||||||
// Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT
|
// // Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT
|
||||||
// into WETH prior/post the trade.
|
// // into WETH prior/post the trade.
|
||||||
// ETH buy/sell is supported
|
// // ETH buy/sell is supported
|
||||||
![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
|
// ![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
|
||||||
) {
|
// ) {
|
||||||
const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
|
// const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
|
||||||
return {
|
// return {
|
||||||
calldataHexString: this._exchangeProxy
|
// calldataHexString: this._exchangeProxy
|
||||||
.sellToLiquidityProvider(
|
// .sellToLiquidityProvider(
|
||||||
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
// isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
||||||
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
// isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
||||||
CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
|
// CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
|
||||||
NULL_ADDRESS,
|
// NULL_ADDRESS,
|
||||||
sellAmount,
|
// sellAmount,
|
||||||
minBuyAmount,
|
// minBuyAmount,
|
||||||
encodeCurveLiquidityProviderData({
|
// encodeCurveLiquidityProviderData({
|
||||||
curveAddress: fillData.pool.poolAddress,
|
// curveAddress: fillData.pool.poolAddress,
|
||||||
exchangeFunctionSelector: fillData.pool.exchangeFunctionSelector,
|
// exchangeFunctionSelector: fillData.pool.exchangeFunctionSelector,
|
||||||
fromCoinIdx: new BigNumber(fillData.fromTokenIdx),
|
// fromCoinIdx: new BigNumber(fillData.fromTokenIdx),
|
||||||
toCoinIdx: new BigNumber(fillData.toTokenIdx),
|
// toCoinIdx: new BigNumber(fillData.toTokenIdx),
|
||||||
}),
|
// }),
|
||||||
)
|
// )
|
||||||
.getABIEncodedTransactionData(),
|
// .getABIEncodedTransactionData(),
|
||||||
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
|
// ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
|
||||||
toAddress: this._exchangeProxy.address,
|
// toAddress: this._exchangeProxy.address,
|
||||||
allowanceTarget: this._exchangeProxy.address,
|
// allowanceTarget: this._exchangeProxy.address,
|
||||||
gasOverhead: ZERO_AMOUNT,
|
// gasOverhead: ZERO_AMOUNT,
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (
|
// if (
|
||||||
this.chainId === ChainId.Mainnet &&
|
// this.chainId === ChainId.Mainnet &&
|
||||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
|
// isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
|
||||||
) {
|
// ) {
|
||||||
const fillData = slippedOrders[0].fills[0].fillData as MooniswapFillData;
|
// const fillData = slippedOrders[0].fills[0].fillData as MooniswapFillData;
|
||||||
return {
|
// return {
|
||||||
calldataHexString: this._exchangeProxy
|
// calldataHexString: this._exchangeProxy
|
||||||
.sellToLiquidityProvider(
|
// .sellToLiquidityProvider(
|
||||||
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
// isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
||||||
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
// isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
||||||
MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
|
// MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
|
||||||
NULL_ADDRESS,
|
// NULL_ADDRESS,
|
||||||
sellAmount,
|
// sellAmount,
|
||||||
minBuyAmount,
|
// minBuyAmount,
|
||||||
poolEncoder.encode([fillData.poolAddress]),
|
// poolEncoder.encode([fillData.poolAddress]),
|
||||||
)
|
// )
|
||||||
.getABIEncodedTransactionData(),
|
// .getABIEncodedTransactionData(),
|
||||||
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
|
// ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
|
||||||
toAddress: this._exchangeProxy.address,
|
// toAddress: this._exchangeProxy.address,
|
||||||
allowanceTarget: this.contractAddresses.exchangeProxy,
|
// allowanceTarget: this.contractAddresses.exchangeProxy,
|
||||||
gasOverhead: ZERO_AMOUNT,
|
// gasOverhead: ZERO_AMOUNT,
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
|
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -20,13 +20,12 @@ export class SwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
private readonly _contractAddresses: ContractAddresses;
|
private readonly _contractAddresses: ContractAddresses;
|
||||||
private readonly _exchangeProxyConsumer: ExchangeProxySwapQuoteConsumer;
|
private readonly _exchangeProxyConsumer: ExchangeProxySwapQuoteConsumer;
|
||||||
|
|
||||||
public static getSwapQuoteConsumer(options: Partial<SwapQuoteConsumerOpts> = {}): SwapQuoteConsumer {
|
public static getSwapQuoteConsumer(options: SwapQuoteConsumerOpts): SwapQuoteConsumer {
|
||||||
return new SwapQuoteConsumer(options);
|
return new SwapQuoteConsumer(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(options: Partial<SwapQuoteConsumerOpts> = {}) {
|
constructor(options: SwapQuoteConsumerOpts) {
|
||||||
const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
|
const { chainId } = options;
|
||||||
assert.isNumber('chainId', chainId);
|
|
||||||
|
|
||||||
this.chainId = chainId;
|
this.chainId = chainId;
|
||||||
this._contractAddresses = options.contractAddresses || getContractAddressesForChainOrThrow(chainId);
|
this._contractAddresses = options.contractAddresses || getContractAddressesForChainOrThrow(chainId);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||||
import { FillQuoteTransformerOrderType, LimitOrder } from '@0x/protocol-utils';
|
import { FillQuoteTransformerOrderType, LimitOrder } from '@0x/protocol-utils';
|
||||||
import { BigNumber, providerUtils } from '@0x/utils';
|
import { BigNumber, providerUtils } from '@0x/utils';
|
||||||
import Axios, { AxiosInstance } from 'axios';
|
import Axios, { AxiosInstance } from 'axios';
|
||||||
@@ -26,9 +26,8 @@ import {
|
|||||||
} from './types';
|
} from './types';
|
||||||
import { assert } from './utils/assert';
|
import { assert } from './utils/assert';
|
||||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||||
import { BancorService } from './utils/market_operation_utils/bancor_service';
|
|
||||||
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
|
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
|
||||||
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
import { SamplerClient } from './utils/market_operation_utils/sampler';
|
||||||
import { SourceFilters } from './utils/market_operation_utils/source_filters';
|
import { SourceFilters } from './utils/market_operation_utils/source_filters';
|
||||||
import {
|
import {
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
@@ -85,20 +84,15 @@ export class SwapQuoter {
|
|||||||
*
|
*
|
||||||
* @return An instance of SwapQuoter
|
* @return An instance of SwapQuoter
|
||||||
*/
|
*/
|
||||||
constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: Partial<SwapQuoterOpts> = {}) {
|
constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: SwapQuoterOpts) {
|
||||||
const {
|
const {
|
||||||
chainId,
|
chainId,
|
||||||
expiryBufferMs,
|
expiryBufferMs,
|
||||||
permittedOrderFeeTypes,
|
permittedOrderFeeTypes,
|
||||||
samplerGasLimit,
|
|
||||||
rfqt,
|
rfqt,
|
||||||
tokenAdjacencyGraph,
|
} = options;
|
||||||
liquidityProviderRegistry,
|
|
||||||
} = { ...constants.DEFAULT_SWAP_QUOTER_OPTS, ...options };
|
|
||||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||||
assert.isValidOrderbook('orderbook', orderbook);
|
assert.isValidOrderbook('orderbook', orderbook);
|
||||||
assert.isNumber('chainId', chainId);
|
|
||||||
assert.isNumber('expiryBufferMs', expiryBufferMs);
|
|
||||||
this.chainId = chainId;
|
this.chainId = chainId;
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.orderbook = orderbook;
|
this.orderbook = orderbook;
|
||||||
@@ -113,45 +107,11 @@ export class SwapQuoter {
|
|||||||
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
||||||
options.ethGasStationUrl,
|
options.ethGasStationUrl,
|
||||||
);
|
);
|
||||||
// Allow the sampler bytecode to be overwritten using geths override functionality
|
|
||||||
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
|
|
||||||
// Allow address of the Sampler to be overridden, i.e in Ganache where overrides do not work
|
|
||||||
const samplerAddress = (options.samplerOverrides && options.samplerOverrides.to) || SAMPLER_ADDRESS;
|
|
||||||
const defaultCodeOverrides = samplerBytecode
|
|
||||||
? {
|
|
||||||
[samplerAddress]: { code: samplerBytecode },
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
const samplerOverrides = _.assign(
|
|
||||||
{ block: BlockParamLiteral.Latest, overrides: defaultCodeOverrides },
|
|
||||||
options.samplerOverrides,
|
|
||||||
);
|
|
||||||
const fastAbi = new FastABI(ERC20BridgeSamplerContract.ABI() as MethodAbi[], { BigNumber });
|
|
||||||
const samplerContract = new ERC20BridgeSamplerContract(
|
|
||||||
samplerAddress,
|
|
||||||
this.provider,
|
|
||||||
{
|
|
||||||
gas: samplerGasLimit,
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
undefined,
|
|
||||||
{
|
|
||||||
encodeInput: (fnName: string, values: any) => fastAbi.encodeInput(fnName, values),
|
|
||||||
decodeOutput: (fnName: string, data: string) => fastAbi.decodeOutput(fnName, data),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
this._marketOperationUtils = new MarketOperationUtils(
|
this._marketOperationUtils = new MarketOperationUtils(
|
||||||
new DexOrderSampler(
|
SamplerClient.createFromChainIdAndEndpoint(
|
||||||
this.chainId,
|
this.chainId,
|
||||||
samplerContract,
|
options.samplerServiceUrl,
|
||||||
samplerOverrides,
|
|
||||||
undefined, // pools caches for balancer and cream
|
|
||||||
tokenAdjacencyGraph,
|
|
||||||
liquidityProviderRegistry,
|
|
||||||
this.chainId === ChainId.Mainnet // Enable Bancor only on Mainnet
|
|
||||||
? async () => BancorService.createAsync(provider)
|
|
||||||
: async () => undefined,
|
|
||||||
),
|
),
|
||||||
this._contractAddresses,
|
this._contractAddresses,
|
||||||
{
|
{
|
||||||
@@ -243,49 +203,50 @@ export class SwapQuoter {
|
|||||||
takerAssetAmount: BigNumber,
|
takerAssetAmount: BigNumber,
|
||||||
options: Partial<SwapQuoteRequestOpts> = {},
|
options: Partial<SwapQuoteRequestOpts> = {},
|
||||||
): Promise<MarketDepth> {
|
): Promise<MarketDepth> {
|
||||||
assert.isString('makerToken', makerToken);
|
throw new Error(`Not implemented`);
|
||||||
assert.isString('takerToken', takerToken);
|
// assert.isString('makerToken', makerToken);
|
||||||
const sourceFilters = new SourceFilters([], options.excludedSources, options.includedSources);
|
// assert.isString('takerToken', takerToken);
|
||||||
|
// const sourceFilters = new SourceFilters([], options.excludedSources, options.includedSources);
|
||||||
let [sellOrders, buyOrders] = !sourceFilters.isAllowed(ERC20BridgeSource.Native)
|
//
|
||||||
? [[], []]
|
// let [sellOrders, buyOrders] = !sourceFilters.isAllowed(ERC20BridgeSource.Native)
|
||||||
: await Promise.all([
|
// ? [[], []]
|
||||||
this.orderbook.getOrdersAsync(makerToken, takerToken),
|
// : await Promise.all([
|
||||||
this.orderbook.getOrdersAsync(takerToken, makerToken),
|
// this.orderbook.getOrdersAsync(makerToken, takerToken),
|
||||||
]);
|
// this.orderbook.getOrdersAsync(takerToken, makerToken),
|
||||||
if (!sellOrders || sellOrders.length === 0) {
|
// ]);
|
||||||
sellOrders = [createDummyOrder(makerToken, takerToken)];
|
// if (!sellOrders || sellOrders.length === 0) {
|
||||||
}
|
// sellOrders = [createDummyOrder(makerToken, takerToken)];
|
||||||
if (!buyOrders || buyOrders.length === 0) {
|
// }
|
||||||
buyOrders = [createDummyOrder(takerToken, makerToken)];
|
// if (!buyOrders || buyOrders.length === 0) {
|
||||||
}
|
// buyOrders = [createDummyOrder(takerToken, makerToken)];
|
||||||
|
// }
|
||||||
const getMarketDepthSide = (marketSideLiquidity: MarketSideLiquidity): MarketDepthSide => {
|
//
|
||||||
const { dexQuotes, nativeOrders } = marketSideLiquidity.quotes;
|
// const getMarketDepthSide = (marketSideLiquidity: MarketSideLiquidity): MarketDepthSide => {
|
||||||
const { side } = marketSideLiquidity;
|
// const { dexQuotes, nativeOrders } = marketSideLiquidity.quotes;
|
||||||
|
// const { side } = marketSideLiquidity;
|
||||||
return [
|
//
|
||||||
...dexQuotes,
|
// return [
|
||||||
nativeOrders.map(o => {
|
// ...dexQuotes,
|
||||||
return {
|
// nativeOrders.map(o => {
|
||||||
input: side === MarketOperation.Sell ? o.fillableTakerAmount : o.fillableMakerAmount,
|
// return {
|
||||||
output: side === MarketOperation.Sell ? o.fillableMakerAmount : o.fillableTakerAmount,
|
// input: side === MarketOperation.Sell ? o.fillableTakerAmount : o.fillableMakerAmount,
|
||||||
fillData: o,
|
// output: side === MarketOperation.Sell ? o.fillableMakerAmount : o.fillableTakerAmount,
|
||||||
source: ERC20BridgeSource.Native,
|
// fillData: o,
|
||||||
};
|
// source: ERC20BridgeSource.Native,
|
||||||
}),
|
// };
|
||||||
];
|
// }),
|
||||||
};
|
// ];
|
||||||
const [bids, asks] = await Promise.all([
|
// };
|
||||||
this._marketOperationUtils.getMarketBuyLiquidityAsync(buyOrders, takerAssetAmount, options),
|
// const [bids, asks] = await Promise.all([
|
||||||
this._marketOperationUtils.getMarketSellLiquidityAsync(sellOrders, takerAssetAmount, options),
|
// this._marketOperationUtils.getMarketBuyLiquidityAsync(buyOrders, takerAssetAmount, options),
|
||||||
]);
|
// this._marketOperationUtils.getMarketSellLiquidityAsync(sellOrders, takerAssetAmount, options),
|
||||||
return {
|
// ]);
|
||||||
bids: getMarketDepthSide(bids),
|
// return {
|
||||||
asks: getMarketDepthSide(asks),
|
// bids: getMarketDepthSide(bids),
|
||||||
makerTokenDecimals: asks.makerTokenDecimals,
|
// asks: getMarketDepthSide(asks),
|
||||||
takerTokenDecimals: asks.takerTokenDecimals,
|
// makerTokenDecimals: asks.makerTokenDecimals,
|
||||||
};
|
// takerTokenDecimals: asks.takerTokenDecimals,
|
||||||
|
// };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ import {
|
|||||||
import { PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator';
|
import { PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator';
|
||||||
import { MetricsProxy } from './utils/quote_requestor';
|
import { MetricsProxy } from './utils/quote_requestor';
|
||||||
|
|
||||||
|
export type Address = string;
|
||||||
|
export type Bytes = string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
|
* expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
|
||||||
* permittedOrderFeeTypes: A set of all the takerFee types that OrderPruner will filter for
|
* permittedOrderFeeTypes: A set of all the takerFee types that OrderPruner will filter for
|
||||||
@@ -326,15 +329,16 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
|
|||||||
chainId: ChainId;
|
chainId: ChainId;
|
||||||
orderRefreshIntervalMs: number;
|
orderRefreshIntervalMs: number;
|
||||||
expiryBufferMs: number;
|
expiryBufferMs: number;
|
||||||
ethereumRpcUrl?: string;
|
// ethereumRpcUrl?: string;
|
||||||
contractAddresses?: AssetSwapperContractAddresses;
|
contractAddresses?: AssetSwapperContractAddresses;
|
||||||
samplerGasLimit?: number;
|
samplerGasLimit?: number;
|
||||||
multiBridgeAddress?: string;
|
// multiBridgeAddress?: string;
|
||||||
ethGasStationUrl?: string;
|
ethGasStationUrl?: string;
|
||||||
rfqt?: SwapQuoterRfqOpts;
|
rfqt?: SwapQuoterRfqOpts;
|
||||||
samplerOverrides?: SamplerOverrides;
|
// samplerOverrides?: SamplerOverrides;
|
||||||
tokenAdjacencyGraph?: TokenAdjacencyGraph;
|
// tokenAdjacencyGraph?: TokenAdjacencyGraph;
|
||||||
liquidityProviderRegistry?: LiquidityProviderRegistry;
|
// liquidityProviderRegistry?: LiquidityProviderRegistry;
|
||||||
|
samplerServiceUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1722,13 +1722,14 @@ export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSo
|
|||||||
);
|
);
|
||||||
|
|
||||||
const uniswapV2CloneGasSchedule = (fillData?: FillData) => {
|
const uniswapV2CloneGasSchedule = (fillData?: FillData) => {
|
||||||
// TODO: Different base cost if to/from ETH.
|
return 90e3;
|
||||||
let gas = 90e3;
|
// // TODO: Different base cost if to/from ETH.
|
||||||
const path = (fillData as UniswapV2FillData).tokenAddressPath;
|
// let gas = 90e3;
|
||||||
if (path.length > 2) {
|
// const path = (fillData as UniswapV2FillData).tokenAddressPath;
|
||||||
gas += (path.length - 2) * 60e3; // +60k for each hop.
|
// if (path.length > 2) {
|
||||||
}
|
// gas += (path.length - 2) * 60e3; // +60k for each hop.
|
||||||
return gas;
|
// }
|
||||||
|
// return gas;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1777,21 +1778,23 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
|||||||
[ERC20BridgeSource.Cream]: () => 120e3,
|
[ERC20BridgeSource.Cream]: () => 120e3,
|
||||||
[ERC20BridgeSource.MStable]: () => 200e3,
|
[ERC20BridgeSource.MStable]: () => 200e3,
|
||||||
[ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => {
|
[ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => {
|
||||||
const psmFillData = fillData as MakerPsmFillData;
|
return 210e3;
|
||||||
return psmFillData.takerToken === psmFillData.gemTokenAddress ? 210e3 : 290e3;
|
// const psmFillData = fillData as MakerPsmFillData;
|
||||||
|
// return psmFillData.takerToken === psmFillData.gemTokenAddress ? 210e3 : 290e3;
|
||||||
},
|
},
|
||||||
[ERC20BridgeSource.Mooniswap]: () => 130e3,
|
[ERC20BridgeSource.Mooniswap]: () => 130e3,
|
||||||
[ERC20BridgeSource.Shell]: () => 170e3,
|
[ERC20BridgeSource.Shell]: () => 170e3,
|
||||||
[ERC20BridgeSource.Component]: () => 188e3,
|
[ERC20BridgeSource.Component]: () => 188e3,
|
||||||
[ERC20BridgeSource.MultiHop]: (fillData?: FillData) => {
|
[ERC20BridgeSource.MultiHop]: (fillData?: FillData) => {
|
||||||
const firstHop = (fillData as MultiHopFillData).firstHopSource;
|
return 0;
|
||||||
const secondHop = (fillData as MultiHopFillData).secondHopSource;
|
// const firstHop = (fillData as MultiHopFillData).firstHopSource;
|
||||||
const firstHopGas = DEFAULT_GAS_SCHEDULE[firstHop.source](firstHop.fillData);
|
// const secondHop = (fillData as MultiHopFillData).secondHopSource;
|
||||||
const secondHopGas = DEFAULT_GAS_SCHEDULE[secondHop.source](secondHop.fillData);
|
// const firstHopGas = DEFAULT_GAS_SCHEDULE[firstHop.source](firstHop.fillData);
|
||||||
return new BigNumber(firstHopGas)
|
// const secondHopGas = DEFAULT_GAS_SCHEDULE[secondHop.source](secondHop.fillData);
|
||||||
.plus(secondHopGas)
|
// return new BigNumber(firstHopGas)
|
||||||
.plus(30e3)
|
// .plus(secondHopGas)
|
||||||
.toNumber();
|
// .plus(30e3)
|
||||||
|
// .toNumber();
|
||||||
},
|
},
|
||||||
[ERC20BridgeSource.Dodo]: (fillData?: FillData) => {
|
[ERC20BridgeSource.Dodo]: (fillData?: FillData) => {
|
||||||
const isSellBase = (fillData as DODOFillData).isSellBase;
|
const isSellBase = (fillData as DODOFillData).isSellBase;
|
||||||
@@ -1801,29 +1804,32 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
|||||||
},
|
},
|
||||||
[ERC20BridgeSource.DodoV2]: (_fillData?: FillData) => 100e3,
|
[ERC20BridgeSource.DodoV2]: (_fillData?: FillData) => 100e3,
|
||||||
[ERC20BridgeSource.Bancor]: (fillData?: FillData) => {
|
[ERC20BridgeSource.Bancor]: (fillData?: FillData) => {
|
||||||
let gas = 200e3;
|
return 200e3;
|
||||||
const path = (fillData as BancorFillData).path;
|
// let gas = 200e3;
|
||||||
if (path.length > 2) {
|
// const path = (fillData as BancorFillData).path;
|
||||||
gas += (path.length - 2) * 60e3; // +60k for each hop.
|
// if (path.length > 2) {
|
||||||
}
|
// gas += (path.length - 2) * 60e3; // +60k for each hop.
|
||||||
return gas;
|
// }
|
||||||
|
// return gas;
|
||||||
},
|
},
|
||||||
[ERC20BridgeSource.KyberDmm]: (fillData?: FillData) => {
|
[ERC20BridgeSource.KyberDmm]: (fillData?: FillData) => {
|
||||||
|
return 95e3;
|
||||||
// TODO: Different base cost if to/from ETH.
|
// TODO: Different base cost if to/from ETH.
|
||||||
let gas = 95e3;
|
// let gas = 95e3;
|
||||||
const path = (fillData as UniswapV2FillData).tokenAddressPath;
|
// const path = (fillData as UniswapV2FillData).tokenAddressPath;
|
||||||
if (path.length > 2) {
|
// if (path.length > 2) {
|
||||||
gas += (path.length - 2) * 65e3; // +65k for each hop.
|
// gas += (path.length - 2) * 65e3; // +65k for each hop.
|
||||||
}
|
// }
|
||||||
return gas;
|
// return gas;
|
||||||
},
|
},
|
||||||
[ERC20BridgeSource.UniswapV3]: (fillData?: FillData) => {
|
[ERC20BridgeSource.UniswapV3]: (fillData?: FillData) => {
|
||||||
let gas = 100e3;
|
return 100e3;
|
||||||
const path = (fillData as UniswapV3FillData).tokenAddressPath;
|
// let gas = 100e3;
|
||||||
if (path.length > 2) {
|
// const path = (fillData as UniswapV3FillData).tokenAddressPath;
|
||||||
gas += (path.length - 2) * 32e3; // +32k for each hop.
|
// if (path.length > 2) {
|
||||||
}
|
// gas += (path.length - 2) * 32e3; // +32k for each hop.
|
||||||
return gas;
|
// }
|
||||||
|
// return gas;
|
||||||
},
|
},
|
||||||
[ERC20BridgeSource.Lido]: () => 226e3,
|
[ERC20BridgeSource.Lido]: () => 226e3,
|
||||||
|
|
||||||
|
|||||||
@@ -97,61 +97,62 @@ export function nativeOrdersToFills(
|
|||||||
inputAmountPerEth: BigNumber,
|
inputAmountPerEth: BigNumber,
|
||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
): Fill[] {
|
): Fill[] {
|
||||||
const sourcePathId = hexUtils.random();
|
throw new Error(`Not implemented`);
|
||||||
// Create a single path from all orders.
|
// const sourcePathId = hexUtils.random();
|
||||||
let fills: Array<Fill & { adjustedRate: BigNumber }> = [];
|
// // Create a single path from all orders.
|
||||||
for (const o of orders) {
|
// let fills: Array<Fill & { adjustedRate: BigNumber }> = [];
|
||||||
const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount, type } = o;
|
// for (const o of orders) {
|
||||||
const makerAmount = fillableMakerAmount;
|
// const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount, type } = o;
|
||||||
const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
|
// const makerAmount = fillableMakerAmount;
|
||||||
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
|
// const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
|
||||||
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
|
// const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
|
||||||
const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(o);
|
// const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
|
||||||
const outputPenalty = ethToOutputAmount({
|
// const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(o);
|
||||||
input,
|
// const outputPenalty = ethToOutputAmount({
|
||||||
output,
|
// input,
|
||||||
inputAmountPerEth,
|
// output,
|
||||||
outputAmountPerEth,
|
// inputAmountPerEth,
|
||||||
ethAmount: fee,
|
// outputAmountPerEth,
|
||||||
});
|
// ethAmount: fee,
|
||||||
// targetInput can be less than the order size
|
// });
|
||||||
// whilst the penalty is constant, it affects the adjusted output
|
// // targetInput can be less than the order size
|
||||||
// only up until the target has been exhausted.
|
// // whilst the penalty is constant, it affects the adjusted output
|
||||||
// A large order and an order at the exact target should be penalized
|
// // only up until the target has been exhausted.
|
||||||
// the same.
|
// // A large order and an order at the exact target should be penalized
|
||||||
const clippedInput = BigNumber.min(targetInput, input);
|
// // the same.
|
||||||
// scale the clipped output inline with the input
|
// const clippedInput = BigNumber.min(targetInput, input);
|
||||||
const clippedOutput = clippedInput.dividedBy(input).times(output);
|
// // scale the clipped output inline with the input
|
||||||
const adjustedOutput =
|
// const clippedOutput = clippedInput.dividedBy(input).times(output);
|
||||||
side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
|
// const adjustedOutput =
|
||||||
const adjustedRate =
|
// side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
|
||||||
side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
|
// const adjustedRate =
|
||||||
// Skip orders with rates that are <= 0.
|
// side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
|
||||||
if (adjustedRate.lte(0)) {
|
// // Skip orders with rates that are <= 0.
|
||||||
continue;
|
// if (adjustedRate.lte(0)) {
|
||||||
}
|
// continue;
|
||||||
fills.push({
|
// }
|
||||||
sourcePathId,
|
// fills.push({
|
||||||
adjustedRate,
|
// sourcePathId,
|
||||||
adjustedOutput,
|
// adjustedRate,
|
||||||
input: clippedInput,
|
// adjustedOutput,
|
||||||
output: clippedOutput,
|
// input: clippedInput,
|
||||||
flags: SOURCE_FLAGS[type === FillQuoteTransformerOrderType.Rfq ? 'RfqOrder' : 'LimitOrder'],
|
// output: clippedOutput,
|
||||||
index: 0, // TBD
|
// flags: SOURCE_FLAGS[type === FillQuoteTransformerOrderType.Rfq ? 'RfqOrder' : 'LimitOrder'],
|
||||||
parent: undefined, // TBD
|
// index: 0, // TBD
|
||||||
source: ERC20BridgeSource.Native,
|
// parent: undefined, // TBD
|
||||||
type,
|
// source: ERC20BridgeSource.Native,
|
||||||
fillData: { ...o },
|
// type,
|
||||||
});
|
// fillData: { ...o },
|
||||||
}
|
// });
|
||||||
// Sort by descending adjusted rate.
|
// }
|
||||||
fills = fills.sort((a, b) => b.adjustedRate.comparedTo(a.adjustedRate));
|
// // Sort by descending adjusted rate.
|
||||||
// Re-index fills.
|
// fills = fills.sort((a, b) => b.adjustedRate.comparedTo(a.adjustedRate));
|
||||||
for (let i = 0; i < fills.length; ++i) {
|
// // Re-index fills.
|
||||||
fills[i].parent = i === 0 ? undefined : fills[i - 1];
|
// for (let i = 0; i < fills.length; ++i) {
|
||||||
fills[i].index = i;
|
// fills[i].parent = i === 0 ? undefined : fills[i - 1];
|
||||||
}
|
// fills[i].index = i;
|
||||||
return fills;
|
// }
|
||||||
|
// return fills;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dexSamplesToFills(
|
export function dexSamplesToFills(
|
||||||
@@ -171,10 +172,10 @@ export function dexSamplesToFills(
|
|||||||
for (let i = 0; i < nonzeroSamples.length; i++) {
|
for (let i = 0; i < nonzeroSamples.length; i++) {
|
||||||
const sample = nonzeroSamples[i];
|
const sample = nonzeroSamples[i];
|
||||||
const prevSample = i === 0 ? undefined : nonzeroSamples[i - 1];
|
const prevSample = i === 0 ? undefined : nonzeroSamples[i - 1];
|
||||||
const { source, fillData } = sample;
|
const { source, encodedFillData } = sample;
|
||||||
const input = sample.input.minus(prevSample ? prevSample.input : 0);
|
const input = sample.input.minus(prevSample ? prevSample.input : 0);
|
||||||
const output = sample.output.minus(prevSample ? prevSample.output : 0);
|
const output = sample.output.minus(prevSample ? prevSample.output : 0);
|
||||||
const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData) || 0;
|
const fee = fees[source] === undefined ? 0 : fees[source]!(sample.encodedFillData) || 0;
|
||||||
let penalty = ZERO_AMOUNT;
|
let penalty = ZERO_AMOUNT;
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
// Only the first fill in a DEX path incurs a penalty.
|
// Only the first fill in a DEX path incurs a penalty.
|
||||||
@@ -194,7 +195,7 @@ export function dexSamplesToFills(
|
|||||||
output,
|
output,
|
||||||
adjustedOutput,
|
adjustedOutput,
|
||||||
source,
|
source,
|
||||||
fillData,
|
encodedFillData,
|
||||||
type: FillQuoteTransformerOrderType.Bridge,
|
type: FillQuoteTransformerOrderType.Bridge,
|
||||||
index: i,
|
index: i,
|
||||||
parent: i !== 0 ? fills[fills.length - 1] : undefined,
|
parent: i !== 0 ? fills[fills.length - 1] : undefined,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import { getBestTwoHopQuote } from './multihop_utils';
|
|||||||
import { createOrdersFromTwoHopSample } from './orders';
|
import { createOrdersFromTwoHopSample } from './orders';
|
||||||
import { Path, PathPenaltyOpts } from './path';
|
import { Path, PathPenaltyOpts } from './path';
|
||||||
import { fillsToSortedPaths, findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer';
|
import { fillsToSortedPaths, findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer';
|
||||||
import { DexOrderSampler, getSampleAmounts } from './sampler';
|
import { Sampler } from './sampler';
|
||||||
import { SourceFilters } from './source_filters';
|
import { SourceFilters } from './source_filters';
|
||||||
import {
|
import {
|
||||||
AggregationError,
|
AggregationError,
|
||||||
@@ -65,7 +65,6 @@ export class MarketOperationUtils {
|
|||||||
private readonly _buySources: SourceFilters;
|
private readonly _buySources: SourceFilters;
|
||||||
private readonly _feeSources: SourceFilters;
|
private readonly _feeSources: SourceFilters;
|
||||||
private readonly _nativeFeeToken: string;
|
private readonly _nativeFeeToken: string;
|
||||||
private readonly _nativeFeeTokenAmount: BigNumber;
|
|
||||||
|
|
||||||
private static _computeQuoteReport(
|
private static _computeQuoteReport(
|
||||||
quoteRequestor: QuoteRequestor | undefined,
|
quoteRequestor: QuoteRequestor | undefined,
|
||||||
@@ -73,9 +72,10 @@ export class MarketOperationUtils {
|
|||||||
optimizerResult: OptimizerResult,
|
optimizerResult: OptimizerResult,
|
||||||
comparisonPrice?: BigNumber | undefined,
|
comparisonPrice?: BigNumber | undefined,
|
||||||
): QuoteReport {
|
): QuoteReport {
|
||||||
const { side, quotes } = marketSideLiquidity;
|
throw new Error(`Not implemented`);
|
||||||
const { liquidityDelivered } = optimizerResult;
|
// const { side, quotes } = marketSideLiquidity;
|
||||||
return generateQuoteReport(side, quotes.nativeOrders, liquidityDelivered, comparisonPrice, quoteRequestor);
|
// const { liquidityDelivered } = optimizerResult;
|
||||||
|
// return generateQuoteReport(side, quotes.nativeOrders, liquidityDelivered, comparisonPrice, quoteRequestor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _computePriceComparisonsReport(
|
private static _computePriceComparisonsReport(
|
||||||
@@ -83,24 +83,25 @@ export class MarketOperationUtils {
|
|||||||
marketSideLiquidity: MarketSideLiquidity,
|
marketSideLiquidity: MarketSideLiquidity,
|
||||||
comparisonPrice?: BigNumber | undefined,
|
comparisonPrice?: BigNumber | undefined,
|
||||||
): PriceComparisonsReport {
|
): PriceComparisonsReport {
|
||||||
const { side, quotes } = marketSideLiquidity;
|
throw new Error(`Not implemented`);
|
||||||
const dexSources = _.flatten(quotes.dexQuotes).map(quote => dexSampleToReportSource(quote, side));
|
// const { side, quotes } = marketSideLiquidity;
|
||||||
const multiHopSources = quotes.twoHopQuotes.map(quote => multiHopSampleToReportSource(quote, side));
|
// const dexSources = _.flatten(quotes.dexQuotes).map(quote => dexSampleToReportSource(quote, side));
|
||||||
const nativeSources = quotes.nativeOrders.map(order =>
|
// const multiHopSources = quotes.twoHopQuotes.map(quote => multiHopSampleToReportSource(quote, side));
|
||||||
nativeOrderToReportEntry(
|
// const nativeSources = quotes.nativeOrders.map(order =>
|
||||||
order.type,
|
// nativeOrderToReportEntry(
|
||||||
order as any,
|
// order.type,
|
||||||
order.fillableTakerAmount,
|
// order as any,
|
||||||
comparisonPrice,
|
// order.fillableTakerAmount,
|
||||||
quoteRequestor,
|
// comparisonPrice,
|
||||||
),
|
// quoteRequestor,
|
||||||
);
|
// ),
|
||||||
|
// );
|
||||||
return { dexSources, multiHopSources, nativeSources };
|
//
|
||||||
|
// return { dexSources, multiHopSources, nativeSources };
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _sampler: DexOrderSampler,
|
private readonly _sampler: Sampler,
|
||||||
private readonly contractAddresses: AssetSwapperContractAddresses,
|
private readonly contractAddresses: AssetSwapperContractAddresses,
|
||||||
private readonly _orderDomain: OrderDomain,
|
private readonly _orderDomain: OrderDomain,
|
||||||
) {
|
) {
|
||||||
@@ -108,7 +109,6 @@ export class MarketOperationUtils {
|
|||||||
this._sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[_sampler.chainId];
|
this._sellSources = SELL_SOURCE_FILTER_BY_CHAIN_ID[_sampler.chainId];
|
||||||
this._feeSources = new SourceFilters(FEE_QUOTE_SOURCES_BY_CHAIN_ID[_sampler.chainId]);
|
this._feeSources = new SourceFilters(FEE_QUOTE_SOURCES_BY_CHAIN_ID[_sampler.chainId]);
|
||||||
this._nativeFeeToken = NATIVE_FEE_TOKEN_BY_CHAIN_ID[_sampler.chainId];
|
this._nativeFeeToken = NATIVE_FEE_TOKEN_BY_CHAIN_ID[_sampler.chainId];
|
||||||
this._nativeFeeTokenAmount = NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID[_sampler.chainId];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,87 +125,53 @@ export class MarketOperationUtils {
|
|||||||
): Promise<MarketSideLiquidity> {
|
): Promise<MarketSideLiquidity> {
|
||||||
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||||
const { makerToken, takerToken } = nativeOrders[0].order;
|
const { makerToken, takerToken } = nativeOrders[0].order;
|
||||||
const sampleAmounts = getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase);
|
|
||||||
|
|
||||||
const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources);
|
const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources);
|
||||||
const quoteSourceFilters = this._sellSources.merge(requestFilters);
|
const quoteSourceFilters = this._sellSources.merge(requestFilters);
|
||||||
const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
|
const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
|
||||||
|
|
||||||
// Used to determine whether the tx origin is an EOA or a contract
|
|
||||||
const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS;
|
|
||||||
|
|
||||||
// Call the sampler contract.
|
|
||||||
const samplerPromise = this._sampler.executeAsync(
|
|
||||||
this._sampler.getTokenDecimals([makerToken, takerToken]),
|
|
||||||
// Get native order fillable amounts.
|
|
||||||
this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
|
|
||||||
// Get ETH -> maker token price.
|
|
||||||
this._sampler.getMedianSellRate(
|
|
||||||
feeSourceFilters.sources,
|
|
||||||
makerToken,
|
|
||||||
this._nativeFeeToken,
|
|
||||||
this._nativeFeeTokenAmount,
|
|
||||||
),
|
|
||||||
// Get ETH -> taker token price.
|
|
||||||
this._sampler.getMedianSellRate(
|
|
||||||
feeSourceFilters.sources,
|
|
||||||
takerToken,
|
|
||||||
this._nativeFeeToken,
|
|
||||||
this._nativeFeeTokenAmount,
|
|
||||||
),
|
|
||||||
// Get sell quotes for taker -> maker.
|
|
||||||
this._sampler.getSellQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
|
|
||||||
this._sampler.getTwoHopSellQuotes(
|
|
||||||
quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
|
|
||||||
makerToken,
|
|
||||||
takerToken,
|
|
||||||
takerAmount,
|
|
||||||
),
|
|
||||||
this._sampler.isAddressContract(txOrigin),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Refresh the cached pools asynchronously if required
|
|
||||||
void this._refreshPoolCacheIfRequiredAsync(takerToken, makerToken);
|
|
||||||
|
|
||||||
const [
|
const [
|
||||||
[
|
tokenInfos,
|
||||||
tokenDecimals,
|
[makerTokenToEthPrice, takerTokenToEthPrice],
|
||||||
orderFillableTakerAmounts,
|
dexQuotes,
|
||||||
outputAmountPerEth,
|
] = await Promise.all([
|
||||||
inputAmountPerEth,
|
this._sampler.getTokenInfosAsync(
|
||||||
dexQuotes,
|
[makerToken, takerToken],
|
||||||
rawTwoHopQuotes,
|
),
|
||||||
isTxOriginContract,
|
this._sampler.getPricesAsync(
|
||||||
],
|
[
|
||||||
] = await Promise.all([samplerPromise]);
|
[makerToken, this._nativeFeeToken],
|
||||||
|
[takerToken, this._nativeFeeToken],
|
||||||
|
],
|
||||||
|
feeSourceFilters.sources,
|
||||||
|
),
|
||||||
|
this._sampler.getSellLiquidityAsync(
|
||||||
|
[makerToken, takerToken],
|
||||||
|
takerAmount,
|
||||||
|
quoteSourceFilters.sources,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
// Filter out any invalid two hop quotes where we couldn't find a route
|
const [makerTokenInfo, takerTokenInfo] = tokenInfos;
|
||||||
const twoHopQuotes = rawTwoHopQuotes.filter(
|
const makerTokenDecimals = makerTokenInfo.decimals;
|
||||||
q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
|
const takerTokenDecimals = takerTokenInfo.decimals;
|
||||||
);
|
|
||||||
|
|
||||||
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
|
const isRfqSupported = !!_opts.rfqt;
|
||||||
|
|
||||||
const isRfqSupported = !!(_opts.rfqt && !isTxOriginContract);
|
|
||||||
const limitOrdersWithFillableAmounts = nativeOrders.map((order, i) => ({
|
|
||||||
...order,
|
|
||||||
...getNativeAdjustedFillableAmountsFromTakerAmount(order, orderFillableTakerAmounts[i]),
|
|
||||||
}));
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
side: MarketOperation.Sell,
|
side: MarketOperation.Sell,
|
||||||
inputAmount: takerAmount,
|
inputAmount: takerAmount,
|
||||||
inputToken: takerToken,
|
inputToken: takerToken,
|
||||||
outputToken: makerToken,
|
outputToken: makerToken,
|
||||||
outputAmountPerEth,
|
outputAmountPerEth: makerTokenToEthPrice,
|
||||||
inputAmountPerEth,
|
inputAmountPerEth: takerTokenToEthPrice,
|
||||||
quoteSourceFilters,
|
quoteSourceFilters,
|
||||||
makerTokenDecimals: makerTokenDecimals.toNumber(),
|
makerTokenDecimals: makerTokenDecimals,
|
||||||
takerTokenDecimals: takerTokenDecimals.toNumber(),
|
takerTokenDecimals: takerTokenDecimals,
|
||||||
quotes: {
|
quotes: {
|
||||||
nativeOrders: limitOrdersWithFillableAmounts,
|
nativeOrders: [],
|
||||||
rfqtIndicativeQuotes: [],
|
rfqtIndicativeQuotes: [],
|
||||||
twoHopQuotes,
|
// twoHopQuotes: [],
|
||||||
dexQuotes,
|
dexQuotes,
|
||||||
},
|
},
|
||||||
isRfqSupported,
|
isRfqSupported,
|
||||||
@@ -224,93 +190,94 @@ export class MarketOperationUtils {
|
|||||||
makerAmount: BigNumber,
|
makerAmount: BigNumber,
|
||||||
opts?: Partial<GetMarketOrdersOpts>,
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<MarketSideLiquidity> {
|
): Promise<MarketSideLiquidity> {
|
||||||
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
throw new Error(`Not implemented`);
|
||||||
const { makerToken, takerToken } = nativeOrders[0].order;
|
// const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||||
const sampleAmounts = getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase);
|
// const { makerToken, takerToken } = nativeOrders[0].order;
|
||||||
|
// const sampleAmounts = getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase);
|
||||||
const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources);
|
//
|
||||||
const quoteSourceFilters = this._buySources.merge(requestFilters);
|
// const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources);
|
||||||
const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
|
// const quoteSourceFilters = this._buySources.merge(requestFilters);
|
||||||
|
// const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
|
||||||
// Used to determine whether the tx origin is an EOA or a contract
|
//
|
||||||
const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS;
|
// // Used to determine whether the tx origin is an EOA or a contract
|
||||||
|
// const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS;
|
||||||
// Call the sampler contract.
|
//
|
||||||
const samplerPromise = this._sampler.executeAsync(
|
// // Call the sampler contract.
|
||||||
this._sampler.getTokenDecimals([makerToken, takerToken]),
|
// const samplerPromise = this._sampler.executeAsync(
|
||||||
// Get native order fillable amounts.
|
// this._sampler.getTokenDecimals([makerToken, takerToken]),
|
||||||
this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
|
// // Get native order fillable amounts.
|
||||||
// Get ETH -> makerToken token price.
|
// this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
|
||||||
this._sampler.getMedianSellRate(
|
// // Get ETH -> makerToken token price.
|
||||||
feeSourceFilters.sources,
|
// this._sampler.getMedianSellRate(
|
||||||
makerToken,
|
// feeSourceFilters.sources,
|
||||||
this._nativeFeeToken,
|
// makerToken,
|
||||||
this._nativeFeeTokenAmount,
|
// this._nativeFeeToken,
|
||||||
),
|
// this._nativeFeeTokenAmount,
|
||||||
// Get ETH -> taker token price.
|
// ),
|
||||||
this._sampler.getMedianSellRate(
|
// // Get ETH -> taker token price.
|
||||||
feeSourceFilters.sources,
|
// this._sampler.getMedianSellRate(
|
||||||
takerToken,
|
// feeSourceFilters.sources,
|
||||||
this._nativeFeeToken,
|
// takerToken,
|
||||||
this._nativeFeeTokenAmount,
|
// this._nativeFeeToken,
|
||||||
),
|
// this._nativeFeeTokenAmount,
|
||||||
// Get buy quotes for taker -> maker.
|
// ),
|
||||||
this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
|
// // Get buy quotes for taker -> maker.
|
||||||
this._sampler.getTwoHopBuyQuotes(
|
// this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
|
||||||
quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
|
// this._sampler.getTwoHopBuyQuotes(
|
||||||
makerToken,
|
// quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
|
||||||
takerToken,
|
// makerToken,
|
||||||
makerAmount,
|
// takerToken,
|
||||||
),
|
// makerAmount,
|
||||||
this._sampler.isAddressContract(txOrigin),
|
// ),
|
||||||
);
|
// this._sampler.isAddressContract(txOrigin),
|
||||||
|
// );
|
||||||
// Refresh the cached pools asynchronously if required
|
//
|
||||||
void this._refreshPoolCacheIfRequiredAsync(takerToken, makerToken);
|
// // Refresh the cached pools asynchronously if required
|
||||||
|
// void this._refreshPoolCacheIfRequiredAsync(takerToken, makerToken);
|
||||||
const [
|
//
|
||||||
[
|
// const [
|
||||||
tokenDecimals,
|
// [
|
||||||
orderFillableMakerAmounts,
|
// tokenDecimals,
|
||||||
ethToMakerAssetRate,
|
// orderFillableMakerAmounts,
|
||||||
ethToTakerAssetRate,
|
// ethToMakerAssetRate,
|
||||||
dexQuotes,
|
// ethToTakerAssetRate,
|
||||||
rawTwoHopQuotes,
|
// dexQuotes,
|
||||||
isTxOriginContract,
|
// rawTwoHopQuotes,
|
||||||
],
|
// isTxOriginContract,
|
||||||
] = await Promise.all([samplerPromise]);
|
// ],
|
||||||
|
// ] = await Promise.all([samplerPromise]);
|
||||||
// Filter out any invalid two hop quotes where we couldn't find a route
|
//
|
||||||
const twoHopQuotes = rawTwoHopQuotes.filter(
|
// // Filter out any invalid two hop quotes where we couldn't find a route
|
||||||
q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
|
// const twoHopQuotes = rawTwoHopQuotes.filter(
|
||||||
);
|
// q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
|
||||||
|
// );
|
||||||
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
|
//
|
||||||
const isRfqSupported = !isTxOriginContract;
|
// const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
|
||||||
|
// const isRfqSupported = !isTxOriginContract;
|
||||||
const limitOrdersWithFillableAmounts = nativeOrders.map((order, i) => ({
|
//
|
||||||
...order,
|
// const limitOrdersWithFillableAmounts = nativeOrders.map((order, i) => ({
|
||||||
...getNativeAdjustedFillableAmountsFromMakerAmount(order, orderFillableMakerAmounts[i]),
|
// ...order,
|
||||||
}));
|
// ...getNativeAdjustedFillableAmountsFromMakerAmount(order, orderFillableMakerAmounts[i]),
|
||||||
|
// }));
|
||||||
return {
|
//
|
||||||
side: MarketOperation.Buy,
|
// return {
|
||||||
inputAmount: makerAmount,
|
// side: MarketOperation.Buy,
|
||||||
inputToken: makerToken,
|
// inputAmount: makerAmount,
|
||||||
outputToken: takerToken,
|
// inputToken: makerToken,
|
||||||
outputAmountPerEth: ethToTakerAssetRate,
|
// outputToken: takerToken,
|
||||||
inputAmountPerEth: ethToMakerAssetRate,
|
// outputAmountPerEth: ethToTakerAssetRate,
|
||||||
quoteSourceFilters,
|
// inputAmountPerEth: ethToMakerAssetRate,
|
||||||
makerTokenDecimals: makerTokenDecimals.toNumber(),
|
// quoteSourceFilters,
|
||||||
takerTokenDecimals: takerTokenDecimals.toNumber(),
|
// makerTokenDecimals: makerTokenDecimals.toNumber(),
|
||||||
quotes: {
|
// takerTokenDecimals: takerTokenDecimals.toNumber(),
|
||||||
nativeOrders: limitOrdersWithFillableAmounts,
|
// quotes: {
|
||||||
rfqtIndicativeQuotes: [],
|
// nativeOrders: limitOrdersWithFillableAmounts,
|
||||||
twoHopQuotes,
|
// rfqtIndicativeQuotes: [],
|
||||||
dexQuotes,
|
// twoHopQuotes,
|
||||||
},
|
// dexQuotes,
|
||||||
isRfqSupported,
|
// },
|
||||||
};
|
// isRfqSupported,
|
||||||
|
// };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -329,98 +296,99 @@ export class MarketOperationUtils {
|
|||||||
makerAmounts: BigNumber[],
|
makerAmounts: BigNumber[],
|
||||||
opts: Partial<GetMarketOrdersOpts> & { gasPrice: BigNumber },
|
opts: Partial<GetMarketOrdersOpts> & { gasPrice: BigNumber },
|
||||||
): Promise<Array<OptimizerResult | undefined>> {
|
): Promise<Array<OptimizerResult | undefined>> {
|
||||||
if (batchNativeOrders.length === 0) {
|
throw new Error(`Not implemented`);
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
// if (batchNativeOrders.length === 0) {
|
||||||
}
|
// throw new Error(AggregationError.EmptyOrders);
|
||||||
const _opts: GetMarketOrdersOpts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
// }
|
||||||
|
// const _opts: GetMarketOrdersOpts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
|
||||||
const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources);
|
//
|
||||||
const quoteSourceFilters = this._buySources.merge(requestFilters);
|
// const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources);
|
||||||
|
// const quoteSourceFilters = this._buySources.merge(requestFilters);
|
||||||
const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
|
//
|
||||||
|
// const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
|
||||||
const ops = [
|
//
|
||||||
...batchNativeOrders.map(orders =>
|
// const ops = [
|
||||||
this._sampler.getLimitOrderFillableMakerAmounts(orders, this.contractAddresses.exchangeProxy),
|
// ...batchNativeOrders.map(orders =>
|
||||||
),
|
// this._sampler.getLimitOrderFillableMakerAmounts(orders, this.contractAddresses.exchangeProxy),
|
||||||
...batchNativeOrders.map(orders =>
|
// ),
|
||||||
this._sampler.getMedianSellRate(
|
// ...batchNativeOrders.map(orders =>
|
||||||
feeSourceFilters.sources,
|
// this._sampler.getMedianSellRate(
|
||||||
orders[0].order.takerToken,
|
// feeSourceFilters.sources,
|
||||||
this._nativeFeeToken,
|
// orders[0].order.takerToken,
|
||||||
this._nativeFeeTokenAmount,
|
// this._nativeFeeToken,
|
||||||
),
|
// this._nativeFeeTokenAmount,
|
||||||
),
|
// ),
|
||||||
...batchNativeOrders.map((orders, i) =>
|
// ),
|
||||||
this._sampler.getBuyQuotes(
|
// ...batchNativeOrders.map((orders, i) =>
|
||||||
quoteSourceFilters.sources,
|
// this._sampler.getBuyQuotes(
|
||||||
orders[0].order.makerToken,
|
// quoteSourceFilters.sources,
|
||||||
orders[0].order.takerToken,
|
// orders[0].order.makerToken,
|
||||||
[makerAmounts[i]],
|
// orders[0].order.takerToken,
|
||||||
),
|
// [makerAmounts[i]],
|
||||||
),
|
// ),
|
||||||
...batchNativeOrders.map(orders =>
|
// ),
|
||||||
this._sampler.getTokenDecimals([orders[0].order.makerToken, orders[0].order.takerToken]),
|
// ...batchNativeOrders.map(orders =>
|
||||||
),
|
// this._sampler.getTokenDecimals([orders[0].order.makerToken, orders[0].order.takerToken]),
|
||||||
];
|
// ),
|
||||||
|
// ];
|
||||||
const executeResults = await this._sampler.executeBatchAsync(ops);
|
//
|
||||||
const batchOrderFillableMakerAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][];
|
// const executeResults = await this._sampler.executeBatchAsync(ops);
|
||||||
const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
|
// const batchOrderFillableMakerAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][];
|
||||||
const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
|
// const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
|
||||||
const batchTokenDecimals = executeResults.splice(0, batchNativeOrders.length) as number[][];
|
// const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
|
||||||
const inputAmountPerEth = ZERO_AMOUNT;
|
// const batchTokenDecimals = executeResults.splice(0, batchNativeOrders.length) as number[][];
|
||||||
|
// const inputAmountPerEth = ZERO_AMOUNT;
|
||||||
return Promise.all(
|
//
|
||||||
batchNativeOrders.map(async (nativeOrders, i) => {
|
// return Promise.all(
|
||||||
if (nativeOrders.length === 0) {
|
// batchNativeOrders.map(async (nativeOrders, i) => {
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
// if (nativeOrders.length === 0) {
|
||||||
}
|
// throw new Error(AggregationError.EmptyOrders);
|
||||||
const { makerToken, takerToken } = nativeOrders[0].order;
|
// }
|
||||||
const orderFillableMakerAmounts = batchOrderFillableMakerAmounts[i];
|
// const { makerToken, takerToken } = nativeOrders[0].order;
|
||||||
const outputAmountPerEth = batchEthToTakerAssetRate[i];
|
// const orderFillableMakerAmounts = batchOrderFillableMakerAmounts[i];
|
||||||
const dexQuotes = batchDexQuotes[i];
|
// const outputAmountPerEth = batchEthToTakerAssetRate[i];
|
||||||
const makerAmount = makerAmounts[i];
|
// const dexQuotes = batchDexQuotes[i];
|
||||||
try {
|
// const makerAmount = makerAmounts[i];
|
||||||
const optimizerResult = await this._generateOptimizedOrdersAsync(
|
// try {
|
||||||
{
|
// const optimizerResult = await this._generateOptimizedOrdersAsync(
|
||||||
side: MarketOperation.Buy,
|
// {
|
||||||
inputToken: makerToken,
|
// side: MarketOperation.Buy,
|
||||||
outputToken: takerToken,
|
// inputToken: makerToken,
|
||||||
inputAmount: makerAmount,
|
// outputToken: takerToken,
|
||||||
outputAmountPerEth,
|
// inputAmount: makerAmount,
|
||||||
inputAmountPerEth,
|
// outputAmountPerEth,
|
||||||
quoteSourceFilters,
|
// inputAmountPerEth,
|
||||||
makerTokenDecimals: batchTokenDecimals[i][0],
|
// quoteSourceFilters,
|
||||||
takerTokenDecimals: batchTokenDecimals[i][1],
|
// makerTokenDecimals: batchTokenDecimals[i][0],
|
||||||
quotes: {
|
// takerTokenDecimals: batchTokenDecimals[i][1],
|
||||||
nativeOrders: nativeOrders.map((o, k) => ({
|
// quotes: {
|
||||||
...o,
|
// nativeOrders: nativeOrders.map((o, k) => ({
|
||||||
...getNativeAdjustedFillableAmountsFromMakerAmount(o, orderFillableMakerAmounts[k]),
|
// ...o,
|
||||||
})),
|
// ...getNativeAdjustedFillableAmountsFromMakerAmount(o, orderFillableMakerAmounts[k]),
|
||||||
dexQuotes,
|
// })),
|
||||||
rfqtIndicativeQuotes: [],
|
// dexQuotes,
|
||||||
twoHopQuotes: [],
|
// rfqtIndicativeQuotes: [],
|
||||||
},
|
// twoHopQuotes: [],
|
||||||
isRfqSupported: false,
|
// },
|
||||||
},
|
// isRfqSupported: false,
|
||||||
{
|
// },
|
||||||
bridgeSlippage: _opts.bridgeSlippage,
|
// {
|
||||||
maxFallbackSlippage: _opts.maxFallbackSlippage,
|
// bridgeSlippage: _opts.bridgeSlippage,
|
||||||
excludedSources: _opts.excludedSources,
|
// maxFallbackSlippage: _opts.maxFallbackSlippage,
|
||||||
feeSchedule: _opts.feeSchedule,
|
// excludedSources: _opts.excludedSources,
|
||||||
allowFallback: _opts.allowFallback,
|
// feeSchedule: _opts.feeSchedule,
|
||||||
gasPrice: _opts.gasPrice,
|
// allowFallback: _opts.allowFallback,
|
||||||
},
|
// gasPrice: _opts.gasPrice,
|
||||||
);
|
// },
|
||||||
return optimizerResult;
|
// );
|
||||||
} catch (e) {
|
// return optimizerResult;
|
||||||
// It's possible for one of the pairs to have no path
|
// } catch (e) {
|
||||||
// rather than throw NO_OPTIMAL_PATH we return undefined
|
// // It's possible for one of the pairs to have no path
|
||||||
return undefined;
|
// // rather than throw NO_OPTIMAL_PATH we return undefined
|
||||||
}
|
// return undefined;
|
||||||
}),
|
// }
|
||||||
);
|
// }),
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
public async _generateOptimizedOrdersAsync(
|
public async _generateOptimizedOrdersAsync(
|
||||||
@@ -515,7 +483,7 @@ export class MarketOperationUtils {
|
|||||||
const twoHopOrders = createOrdersFromTwoHopSample(bestTwoHopQuote, orderOpts);
|
const twoHopOrders = createOrdersFromTwoHopSample(bestTwoHopQuote, orderOpts);
|
||||||
return {
|
return {
|
||||||
optimizedOrders: twoHopOrders,
|
optimizedOrders: twoHopOrders,
|
||||||
liquidityDelivered: bestTwoHopQuote,
|
// liquidityDelivered: bestTwoHopQuote,
|
||||||
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
|
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
|
||||||
marketSideLiquidity,
|
marketSideLiquidity,
|
||||||
adjustedRate: bestTwoHopRate,
|
adjustedRate: bestTwoHopRate,
|
||||||
@@ -536,7 +504,7 @@ export class MarketOperationUtils {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
optimizedOrders: collapsedPath.orders,
|
optimizedOrders: collapsedPath.orders,
|
||||||
liquidityDelivered: collapsedPath.collapsedFills as CollapsedFill[],
|
// liquidityDelivered: collapsedPath.collapsedFills as CollapsedFill[],
|
||||||
sourceFlags: collapsedPath.sourceFlags,
|
sourceFlags: collapsedPath.sourceFlags,
|
||||||
marketSideLiquidity,
|
marketSideLiquidity,
|
||||||
adjustedRate: optimalPathRate,
|
adjustedRate: optimalPathRate,
|
||||||
@@ -713,17 +681,6 @@ export class MarketOperationUtils {
|
|||||||
return { ...optimizerResult, quoteReport, priceComparisonsReport };
|
return { ...optimizerResult, quoteReport, priceComparisonsReport };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
|
|
||||||
void Promise.all(
|
|
||||||
Object.values(this._sampler.poolsCaches).map(async cache => {
|
|
||||||
if (cache.isFresh(takerToken, makerToken)) {
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
return cache.getFreshPoolsForPairAsync(takerToken, makerToken);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line: prefer-function-over-method
|
// tslint:disable-next-line: prefer-function-over-method
|
||||||
private async _addOptionalFallbackAsync(
|
private async _addOptionalFallbackAsync(
|
||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
|
|||||||
@@ -38,41 +38,42 @@ export function getBestTwoHopQuote(
|
|||||||
marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
|
marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
|
||||||
feeSchedule?: FeeSchedule,
|
feeSchedule?: FeeSchedule,
|
||||||
exchangeProxyOverhead?: ExchangeProxyOverhead,
|
exchangeProxyOverhead?: ExchangeProxyOverhead,
|
||||||
): { quote: DexSample<MultiHopFillData> | undefined; adjustedRate: BigNumber } {
|
): { quote: DexSample | undefined; adjustedRate: BigNumber } {
|
||||||
const { side, inputAmount, outputAmountPerEth, quotes } = marketSideLiquidity;
|
throw new Error(`No implementado`);
|
||||||
const { twoHopQuotes } = quotes;
|
// const { side, inputAmount, outputAmountPerEth, quotes } = marketSideLiquidity;
|
||||||
// Ensure the expected data we require exists. In the case where all hops reverted
|
// const { twoHopQuotes } = quotes;
|
||||||
// or there were no sources included that allowed for multi hop,
|
// // Ensure the expected data we require exists. In the case where all hops reverted
|
||||||
// we can end up with empty, but not undefined, fill data
|
// // or there were no sources included that allowed for multi hop,
|
||||||
const filteredQuotes = twoHopQuotes.filter(
|
// // we can end up with empty, but not undefined, fill data
|
||||||
quote =>
|
// const filteredQuotes = twoHopQuotes.filter(
|
||||||
quote &&
|
// quote =>
|
||||||
quote.fillData &&
|
// quote &&
|
||||||
quote.fillData.firstHopSource &&
|
// quote.fillData &&
|
||||||
quote.fillData.secondHopSource &&
|
// quote.fillData.firstHopSource &&
|
||||||
quote.output.isGreaterThan(ZERO_AMOUNT),
|
// quote.fillData.secondHopSource &&
|
||||||
);
|
// quote.output.isGreaterThan(ZERO_AMOUNT),
|
||||||
if (filteredQuotes.length === 0) {
|
// );
|
||||||
return { quote: undefined, adjustedRate: ZERO_AMOUNT };
|
// if (filteredQuotes.length === 0) {
|
||||||
}
|
// return { quote: undefined, adjustedRate: ZERO_AMOUNT };
|
||||||
const best = filteredQuotes
|
// }
|
||||||
.map(quote =>
|
// const best = filteredQuotes
|
||||||
getTwoHopAdjustedRate(side, quote, inputAmount, outputAmountPerEth, feeSchedule, exchangeProxyOverhead),
|
// .map(quote =>
|
||||||
)
|
// getTwoHopAdjustedRate(side, quote, inputAmount, outputAmountPerEth, feeSchedule, exchangeProxyOverhead),
|
||||||
.reduce(
|
// )
|
||||||
(prev, curr, i) =>
|
// .reduce(
|
||||||
curr.isGreaterThan(prev.adjustedRate) ? { adjustedRate: curr, quote: filteredQuotes[i] } : prev,
|
// (prev, curr, i) =>
|
||||||
{
|
// curr.isGreaterThan(prev.adjustedRate) ? { adjustedRate: curr, quote: filteredQuotes[i] } : prev,
|
||||||
adjustedRate: getTwoHopAdjustedRate(
|
// {
|
||||||
side,
|
// adjustedRate: getTwoHopAdjustedRate(
|
||||||
filteredQuotes[0],
|
// side,
|
||||||
inputAmount,
|
// filteredQuotes[0],
|
||||||
outputAmountPerEth,
|
// inputAmount,
|
||||||
feeSchedule,
|
// outputAmountPerEth,
|
||||||
exchangeProxyOverhead,
|
// feeSchedule,
|
||||||
),
|
// exchangeProxyOverhead,
|
||||||
quote: filteredQuotes[0],
|
// ),
|
||||||
},
|
// quote: filteredQuotes[0],
|
||||||
);
|
// },
|
||||||
return best;
|
// );
|
||||||
|
// return best;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,33 +48,34 @@ export interface CreateOrderFromPathOpts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createOrdersFromTwoHopSample(
|
export function createOrdersFromTwoHopSample(
|
||||||
sample: DexSample<MultiHopFillData>,
|
sample: DexSample,
|
||||||
opts: CreateOrderFromPathOpts,
|
opts: CreateOrderFromPathOpts,
|
||||||
): OptimizedMarketOrder[] {
|
): OptimizedMarketOrder[] {
|
||||||
const [makerToken, takerToken] = getMakerTakerTokens(opts);
|
throw new Error(`Not implemented`);
|
||||||
const { firstHopSource, secondHopSource, intermediateToken } = sample.fillData;
|
// const [makerToken, takerToken] = getMakerTakerTokens(opts);
|
||||||
const firstHopFill: CollapsedFill = {
|
// const { firstHopSource, secondHopSource, intermediateToken } = sample.fillData;
|
||||||
sourcePathId: '',
|
// const firstHopFill: CollapsedFill = {
|
||||||
source: firstHopSource.source,
|
// sourcePathId: '',
|
||||||
type: FillQuoteTransformerOrderType.Bridge,
|
// source: firstHopSource.source,
|
||||||
input: opts.side === MarketOperation.Sell ? sample.input : ZERO_AMOUNT,
|
// type: FillQuoteTransformerOrderType.Bridge,
|
||||||
output: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output,
|
// input: opts.side === MarketOperation.Sell ? sample.input : ZERO_AMOUNT,
|
||||||
subFills: [],
|
// output: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output,
|
||||||
fillData: firstHopSource.fillData,
|
// subFills: [],
|
||||||
};
|
// fillData: firstHopSource.fillData,
|
||||||
const secondHopFill: CollapsedFill = {
|
// };
|
||||||
sourcePathId: '',
|
// const secondHopFill: CollapsedFill = {
|
||||||
source: secondHopSource.source,
|
// sourcePathId: '',
|
||||||
type: FillQuoteTransformerOrderType.Bridge,
|
// source: secondHopSource.source,
|
||||||
input: opts.side === MarketOperation.Sell ? MAX_UINT256 : sample.input,
|
// type: FillQuoteTransformerOrderType.Bridge,
|
||||||
output: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256,
|
// input: opts.side === MarketOperation.Sell ? MAX_UINT256 : sample.input,
|
||||||
subFills: [],
|
// output: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256,
|
||||||
fillData: secondHopSource.fillData,
|
// subFills: [],
|
||||||
};
|
// fillData: secondHopSource.fillData,
|
||||||
return [
|
// };
|
||||||
createBridgeOrder(firstHopFill, intermediateToken, takerToken, opts.side),
|
// return [
|
||||||
createBridgeOrder(secondHopFill, makerToken, intermediateToken, opts.side),
|
// createBridgeOrder(firstHopFill, intermediateToken, takerToken, opts.side),
|
||||||
];
|
// createBridgeOrder(secondHopFill, makerToken, intermediateToken, opts.side),
|
||||||
|
// ];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): string {
|
export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): string {
|
||||||
@@ -348,7 +349,7 @@ export function createBridgeOrder(
|
|||||||
takerToken,
|
takerToken,
|
||||||
makerAmount,
|
makerAmount,
|
||||||
takerAmount,
|
takerAmount,
|
||||||
fillData: createFinalBridgeOrderFillDataFromCollapsedFill(fill),
|
fillData: fill.encodedFillData,
|
||||||
source: fill.source,
|
source: fill.source,
|
||||||
sourcePathId: fill.sourcePathId,
|
sourcePathId: fill.sourcePathId,
|
||||||
type: FillQuoteTransformerOrderType.Bridge,
|
type: FillQuoteTransformerOrderType.Bridge,
|
||||||
@@ -356,36 +357,6 @@ export function createBridgeOrder(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFinalBridgeOrderFillDataFromCollapsedFill(fill: CollapsedFill): FillData {
|
|
||||||
switch (fill.source) {
|
|
||||||
case ERC20BridgeSource.UniswapV3: {
|
|
||||||
const fd = fill.fillData as UniswapV3FillData;
|
|
||||||
return {
|
|
||||||
router: fd.router,
|
|
||||||
tokenAddressPath: fd.tokenAddressPath,
|
|
||||||
uniswapPath: getBestUniswapV3PathForInputAmount(fd, fill.input),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return fill.fillData;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBestUniswapV3PathForInputAmount(fillData: UniswapV3FillData, inputAmount: BigNumber): string {
|
|
||||||
if (fillData.pathAmounts.length === 0) {
|
|
||||||
throw new Error(`No Uniswap V3 paths`);
|
|
||||||
}
|
|
||||||
// Find the best path that can satisfy `inputAmount`.
|
|
||||||
// Assumes `fillData.pathAmounts` is sorted ascending.
|
|
||||||
for (const { inputAmount: pathInputAmount, uniswapPath } of fillData.pathAmounts) {
|
|
||||||
if (pathInputAmount.gte(inputAmount)) {
|
|
||||||
return uniswapPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fillData.pathAmounts[fillData.pathAmounts.length - 1].uniswapPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMakerTakerTokens(opts: CreateOrderFromPathOpts): [string, string] {
|
export function getMakerTakerTokens(opts: CreateOrderFromPathOpts): [string, string] {
|
||||||
const makerToken = opts.side === MarketOperation.Sell ? opts.outputToken : opts.inputToken;
|
const makerToken = opts.side === MarketOperation.Sell ? opts.outputToken : opts.inputToken;
|
||||||
const takerToken = opts.side === MarketOperation.Sell ? opts.inputToken : opts.outputToken;
|
const takerToken = opts.side === MarketOperation.Sell ? opts.inputToken : opts.outputToken;
|
||||||
@@ -506,19 +477,20 @@ export function createNativeOptimizedOrder(
|
|||||||
fill: NativeCollapsedFill,
|
fill: NativeCollapsedFill,
|
||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
): OptimizedMarketOrderBase<NativeLimitOrderFillData> | OptimizedMarketOrderBase<NativeRfqOrderFillData> {
|
): OptimizedMarketOrderBase<NativeLimitOrderFillData> | OptimizedMarketOrderBase<NativeRfqOrderFillData> {
|
||||||
const fillData = fill.fillData;
|
throw new Error(`No implementado`);
|
||||||
const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side);
|
// const fillData = fill.fillData;
|
||||||
const base = {
|
// const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side);
|
||||||
type: fill.type,
|
// const base = {
|
||||||
source: ERC20BridgeSource.Native,
|
// type: fill.type,
|
||||||
makerToken: fillData.order.makerToken,
|
// source: ERC20BridgeSource.Native,
|
||||||
takerToken: fillData.order.takerToken,
|
// makerToken: fillData.order.makerToken,
|
||||||
makerAmount,
|
// takerToken: fillData.order.takerToken,
|
||||||
takerAmount,
|
// makerAmount,
|
||||||
fills: [fill],
|
// takerAmount,
|
||||||
fillData,
|
// fills: [fill],
|
||||||
};
|
// fillData,
|
||||||
return fill.type === FillQuoteTransformerOrderType.Rfq
|
// };
|
||||||
? { ...base, type: FillQuoteTransformerOrderType.Rfq, fillData: fillData as NativeRfqOrderFillData }
|
// return fill.type === FillQuoteTransformerOrderType.Rfq
|
||||||
: { ...base, type: FillQuoteTransformerOrderType.Limit, fillData: fillData as NativeLimitOrderFillData };
|
// ? { ...base, type: FillQuoteTransformerOrderType.Rfq, fillData: fillData as NativeRfqOrderFillData }
|
||||||
|
// : { ...base, type: FillQuoteTransformerOrderType.Limit, fillData: fillData as NativeLimitOrderFillData };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ export class Path {
|
|||||||
if (prevFill.sourcePathId === fill.sourcePathId) {
|
if (prevFill.sourcePathId === fill.sourcePathId) {
|
||||||
prevFill.input = prevFill.input.plus(fill.input);
|
prevFill.input = prevFill.input.plus(fill.input);
|
||||||
prevFill.output = prevFill.output.plus(fill.output);
|
prevFill.output = prevFill.output.plus(fill.output);
|
||||||
prevFill.fillData = fill.fillData;
|
prevFill.encodedFillData = fill.encodedFillData;
|
||||||
prevFill.subFills.push(fill);
|
prevFill.subFills.push(fill);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -266,7 +266,7 @@ export class Path {
|
|||||||
sourcePathId: fill.sourcePathId,
|
sourcePathId: fill.sourcePathId,
|
||||||
source: fill.source,
|
source: fill.source,
|
||||||
type: fill.type,
|
type: fill.type,
|
||||||
fillData: fill.fillData,
|
encodedFillData: fill.encodedFillData,
|
||||||
input: fill.input,
|
input: fill.input,
|
||||||
output: fill.output,
|
output: fill.output,
|
||||||
subFills: [fill],
|
subFills: [fill],
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ function calculateOuputFee(
|
|||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
): BigNumber {
|
): BigNumber {
|
||||||
if (isDexSample(sampleOrNativeOrder)) {
|
if (isDexSample(sampleOrNativeOrder)) {
|
||||||
const { input, output, source, fillData } = sampleOrNativeOrder;
|
const { input, output, source, encodedFillData } = sampleOrNativeOrder;
|
||||||
const fee = fees[source]?.(fillData) || 0;
|
const fee = fees[source]?.(encodedFillData) || 0;
|
||||||
const outputFee = ethToOutputAmount({
|
const outputFee = ethToOutputAmount({
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
@@ -259,7 +259,7 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
|
|
||||||
// NOTE: For DexSamples only
|
// NOTE: For DexSamples only
|
||||||
let fill = createFill(current);
|
let fill = createFill(current);
|
||||||
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
|
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample>;
|
||||||
// Descend to approach a closer fill for fillData which may not be consistent
|
// Descend to approach a closer fill for fillData which may not be consistent
|
||||||
// throughout the path (UniswapV3) and for a closer guesstimate at
|
// throughout the path (UniswapV3) and for a closer guesstimate at
|
||||||
// gas used
|
// gas used
|
||||||
|
|||||||
@@ -13,25 +13,26 @@ import { DexSample, ERC20BridgeSource, ExchangeProxyOverhead, FeeSchedule, Multi
|
|||||||
*/
|
*/
|
||||||
export function getTwoHopAdjustedRate(
|
export function getTwoHopAdjustedRate(
|
||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
twoHopQuote: DexSample<MultiHopFillData>,
|
twoHopQuote: DexSample,
|
||||||
targetInput: BigNumber,
|
targetInput: BigNumber,
|
||||||
outputAmountPerEth: BigNumber,
|
outputAmountPerEth: BigNumber,
|
||||||
fees: FeeSchedule = {},
|
fees: FeeSchedule = {},
|
||||||
exchangeProxyOverhead: ExchangeProxyOverhead = () => ZERO_AMOUNT,
|
exchangeProxyOverhead: ExchangeProxyOverhead = () => ZERO_AMOUNT,
|
||||||
): BigNumber {
|
): BigNumber {
|
||||||
const { output, input, fillData } = twoHopQuote;
|
throw new Error(`Not implemented`);
|
||||||
if (input.isLessThan(targetInput) || output.isZero()) {
|
// const { output, input, fillData } = twoHopQuote;
|
||||||
return ZERO_AMOUNT;
|
// if (input.isLessThan(targetInput) || output.isZero()) {
|
||||||
}
|
// return ZERO_AMOUNT;
|
||||||
const penalty = outputAmountPerEth.times(
|
// }
|
||||||
exchangeProxyOverhead(
|
// const penalty = outputAmountPerEth.times(
|
||||||
SOURCE_FLAGS.MultiHop |
|
// exchangeProxyOverhead(
|
||||||
SOURCE_FLAGS[fillData.firstHopSource.source] |
|
// SOURCE_FLAGS.MultiHop |
|
||||||
SOURCE_FLAGS[fillData.secondHopSource.source],
|
// SOURCE_FLAGS[fillData.firstHopSource.source] |
|
||||||
).plus(fees[ERC20BridgeSource.MultiHop]!(fillData)),
|
// SOURCE_FLAGS[fillData.secondHopSource.source],
|
||||||
);
|
// ).plus(fees[ERC20BridgeSource.MultiHop]!(fillData)),
|
||||||
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
|
// );
|
||||||
return side === MarketOperation.Sell ? adjustedOutput.div(input) : input.div(adjustedOutput);
|
// const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
|
||||||
|
// return side === MarketOperation.Sell ? adjustedOutput.div(input) : input.div(adjustedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,167 +1,86 @@
|
|||||||
import { ChainId } from '@0x/contract-addresses';
|
import { ChainId } from '@0x/contract-addresses';
|
||||||
import { BigNumber, NULL_BYTES } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { SamplerOverrides } from '../../types';
|
import { Address } from '../../types';
|
||||||
import { ERC20BridgeSamplerContract } from '../../wrappers';
|
|
||||||
|
|
||||||
import { BancorService } from './bancor_service';
|
import { DexSample, ERC20BridgeSource, TokenAdjacencyGraph } from './types';
|
||||||
import { PoolsCache } from './pools_cache';
|
import { SamplerServiceRpcClient } from './sampler_service_rpc_client';
|
||||||
import { SamplerOperations } from './sampler_operations';
|
|
||||||
import { BatchedOperation, ERC20BridgeSource, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
|
|
||||||
|
|
||||||
/**
|
interface TokenInfo {
|
||||||
* Generate sample amounts up to `maxFillAmount`.
|
decimals: number;
|
||||||
*/
|
address: Address;
|
||||||
export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] {
|
gasCost: number;
|
||||||
const distribution = [...Array<BigNumber>(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i));
|
symbol: string;
|
||||||
const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution)));
|
|
||||||
const amounts = stepSizes.map((_s, i) => {
|
|
||||||
if (i === numSamples - 1) {
|
|
||||||
return maxFillAmount;
|
|
||||||
}
|
|
||||||
return maxFillAmount
|
|
||||||
.times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)]))
|
|
||||||
.integerValue(BigNumber.ROUND_UP);
|
|
||||||
});
|
|
||||||
return amounts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BatchedOperationResult<T> = T extends BatchedOperation<infer TResult> ? TResult : never;
|
export interface Sampler {
|
||||||
|
chainId: ChainId;
|
||||||
|
getTokenInfosAsync(tokens: Address[]): Promise<TokenInfo[]>;
|
||||||
|
getPricesAsync(paths: Address[][], sources: ERC20BridgeSource[]): Promise<BigNumber[]>;
|
||||||
|
getSellLiquidityAsync(path: Address[], takerAmount: BigNumber, sources: ERC20BridgeSource[]): Promise<DexSample[][]>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
export class SamplerClient implements Sampler {
|
||||||
* Encapsulates interactions with the `ERC20BridgeSampler` contract.
|
static createFromChainIdAndEndpoint(chainId: ChainId, endpoint: string): SamplerClient {
|
||||||
*/
|
return new SamplerClient(chainId, new SamplerServiceRpcClient(endpoint));
|
||||||
export class DexOrderSampler extends SamplerOperations {
|
|
||||||
constructor(
|
|
||||||
public readonly chainId: ChainId,
|
|
||||||
_samplerContract: ERC20BridgeSamplerContract,
|
|
||||||
private readonly _samplerOverrides?: SamplerOverrides,
|
|
||||||
poolsCaches?: { [key in ERC20BridgeSource]: PoolsCache },
|
|
||||||
tokenAdjacencyGraph?: TokenAdjacencyGraph,
|
|
||||||
liquidityProviderRegistry?: LiquidityProviderRegistry,
|
|
||||||
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
|
|
||||||
) {
|
|
||||||
super(chainId, _samplerContract, poolsCaches, tokenAdjacencyGraph, liquidityProviderRegistry, bancorServiceFn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Type overloads for `executeAsync()`. Could skip this if we would upgrade TS. */
|
static async createFromEndpointAsync(endpoint: string): Promise<SamplerClient> {
|
||||||
|
const service = new SamplerServiceRpcClient(endpoint);
|
||||||
// prettier-ignore
|
const chainId = await service.getChainIdAsync();
|
||||||
public async executeAsync<
|
return new SamplerClient(
|
||||||
T1
|
chainId,
|
||||||
>(...ops: [T1]): Promise<[
|
service,
|
||||||
BatchedOperationResult<T1>
|
);
|
||||||
]>;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
public async executeAsync<
|
|
||||||
T1, T2
|
|
||||||
>(...ops: [T1, T2]): Promise<[
|
|
||||||
BatchedOperationResult<T1>,
|
|
||||||
BatchedOperationResult<T2>
|
|
||||||
]>;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
public async executeAsync<
|
|
||||||
T1, T2, T3
|
|
||||||
>(...ops: [T1, T2, T3]): Promise<[
|
|
||||||
BatchedOperationResult<T1>,
|
|
||||||
BatchedOperationResult<T2>,
|
|
||||||
BatchedOperationResult<T3>
|
|
||||||
]>;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
public async executeAsync<
|
|
||||||
T1, T2, T3, T4
|
|
||||||
>(...ops: [T1, T2, T3, T4]): Promise<[
|
|
||||||
BatchedOperationResult<T1>,
|
|
||||||
BatchedOperationResult<T2>,
|
|
||||||
BatchedOperationResult<T3>,
|
|
||||||
BatchedOperationResult<T4>
|
|
||||||
]>;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
public async executeAsync<
|
|
||||||
T1, T2, T3, T4, T5
|
|
||||||
>(...ops: [T1, T2, T3, T4, T5]): Promise<[
|
|
||||||
BatchedOperationResult<T1>,
|
|
||||||
BatchedOperationResult<T2>,
|
|
||||||
BatchedOperationResult<T3>,
|
|
||||||
BatchedOperationResult<T4>,
|
|
||||||
BatchedOperationResult<T5>
|
|
||||||
]>;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
public async executeAsync<
|
|
||||||
T1, T2, T3, T4, T5, T6
|
|
||||||
>(...ops: [T1, T2, T3, T4, T5, T6]): Promise<[
|
|
||||||
BatchedOperationResult<T1>,
|
|
||||||
BatchedOperationResult<T2>,
|
|
||||||
BatchedOperationResult<T3>,
|
|
||||||
BatchedOperationResult<T4>,
|
|
||||||
BatchedOperationResult<T5>,
|
|
||||||
BatchedOperationResult<T6>
|
|
||||||
]>;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
public async executeAsync<
|
|
||||||
T1, T2, T3, T4, T5, T6, T7
|
|
||||||
>(...ops: [T1, T2, T3, T4, T5, T6, T7]): Promise<[
|
|
||||||
BatchedOperationResult<T1>,
|
|
||||||
BatchedOperationResult<T2>,
|
|
||||||
BatchedOperationResult<T3>,
|
|
||||||
BatchedOperationResult<T4>,
|
|
||||||
BatchedOperationResult<T5>,
|
|
||||||
BatchedOperationResult<T6>,
|
|
||||||
BatchedOperationResult<T7>
|
|
||||||
]>;
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
public async executeAsync<
|
|
||||||
T1, T2, T3, T4, T5, T6, T7, T8
|
|
||||||
>(...ops: [T1, T2, T3, T4, T5, T6, T7, T8]): Promise<[
|
|
||||||
BatchedOperationResult<T1>,
|
|
||||||
BatchedOperationResult<T2>,
|
|
||||||
BatchedOperationResult<T3>,
|
|
||||||
BatchedOperationResult<T4>,
|
|
||||||
BatchedOperationResult<T5>,
|
|
||||||
BatchedOperationResult<T6>,
|
|
||||||
BatchedOperationResult<T7>,
|
|
||||||
BatchedOperationResult<T8>
|
|
||||||
]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run a series of operations from `DexOrderSampler.ops` in a single transaction.
|
|
||||||
*/
|
|
||||||
public async executeAsync(...ops: any[]): Promise<any[]> {
|
|
||||||
return this.executeBatchAsync(ops);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private constructor(
|
||||||
* Run a series of operations from `DexOrderSampler.ops` in a single transaction.
|
private readonly _chainId: number,
|
||||||
* Takes an arbitrary length array, but is not typesafe.
|
private readonly _service: SamplerServiceRpcClient,
|
||||||
*/
|
) {}
|
||||||
public async executeBatchAsync<T extends Array<BatchedOperation<any>>>(ops: T): Promise<any[]> {
|
|
||||||
const callDatas = ops.map(o => o.encodeCall());
|
|
||||||
const { overrides, block } = this._samplerOverrides
|
|
||||||
? this._samplerOverrides
|
|
||||||
: { overrides: undefined, block: undefined };
|
|
||||||
|
|
||||||
// All operations are NOOPs
|
public get chainId(): ChainId {
|
||||||
if (callDatas.every(cd => cd === NULL_BYTES)) {
|
return this._chainId;
|
||||||
return callDatas.map((_callData, i) => ops[i].handleCallResults(NULL_BYTES));
|
}
|
||||||
}
|
|
||||||
// Execute all non-empty calldatas.
|
public async getPricesAsync(
|
||||||
const rawCallResults = await this._samplerContract
|
paths: Address[][],
|
||||||
.batchCall(callDatas.filter(cd => cd !== NULL_BYTES))
|
sources: ERC20BridgeSource[],
|
||||||
.callAsync({ overrides }, block);
|
): Promise<BigNumber[]> {
|
||||||
// Return the parsed results.
|
return this._service.getPricesAsync(paths.map(p => ({
|
||||||
let rawCallResultsIdx = 0;
|
tokenPath: p,
|
||||||
return callDatas.map((callData, i) => {
|
demand: true,
|
||||||
// tslint:disable-next-line:boolean-naming
|
sources,
|
||||||
const { data, success } =
|
})));
|
||||||
callData !== NULL_BYTES ? rawCallResults[rawCallResultsIdx++] : { success: true, data: NULL_BYTES };
|
}
|
||||||
return success ? ops[i].handleCallResults(data) : ops[i].handleRevert(data);
|
|
||||||
});
|
public async getTokenInfosAsync(tokens: Address[]): Promise<TokenInfo[]> {
|
||||||
|
return this._service.getTokensAsync(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSellLiquidityAsync(
|
||||||
|
path: Address[],
|
||||||
|
takerAmount: BigNumber,
|
||||||
|
sources: ERC20BridgeSource[],
|
||||||
|
): Promise<DexSample[][]> {
|
||||||
|
const liquidity = await this._service.getSellLiquidityAsync(
|
||||||
|
sources.map(s => ({
|
||||||
|
tokenPath: path,
|
||||||
|
inputAmount: takerAmount,
|
||||||
|
source: s,
|
||||||
|
demand: true,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
return liquidity.map(
|
||||||
|
liq => liq.liquidityCurves.map(
|
||||||
|
pts =>
|
||||||
|
pts.map(pt => ({
|
||||||
|
input: pt.sellAmount,
|
||||||
|
output: pt.buyAmount,
|
||||||
|
encodedFillData: pt.encodedFillData,
|
||||||
|
gasCost: pt.gasCost,
|
||||||
|
source: liq.source,
|
||||||
|
}) as DexSample),
|
||||||
|
)).flat(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
|||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { Client as OpenRpcClient, HTTPTransport, RequestManager } from '@open-rpc/client-js';
|
||||||
|
|
||||||
|
import { Address, Bytes } from '../../types';
|
||||||
|
|
||||||
|
type DecimalString = string;
|
||||||
|
|
||||||
|
export interface LiquidityCurvePoint {
|
||||||
|
sellAmount: BigNumber;
|
||||||
|
buyAmount: BigNumber;
|
||||||
|
encodedFillData: Bytes;
|
||||||
|
gasCost: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcLiquidityCurvePoint = Omit<Omit<LiquidityCurvePoint, 'sellAmount'>, 'buyAmount'> & {
|
||||||
|
sellAmount: DecimalString;
|
||||||
|
buyAmount: DecimalString;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LiquidityRequest {
|
||||||
|
tokenPath: Address[];
|
||||||
|
inputAmount: BigNumber;
|
||||||
|
source: string;
|
||||||
|
demand?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcLiquidityRequest = Omit<LiquidityRequest, 'inputAmount'> & {
|
||||||
|
inputAmount: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PriceRequest {
|
||||||
|
tokenPath: Address[];
|
||||||
|
sources?: string[];
|
||||||
|
demand?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcPriceRequest = PriceRequest;
|
||||||
|
|
||||||
|
export interface LiquidityResponse {
|
||||||
|
source: string;
|
||||||
|
liquidityCurves: LiquidityCurvePoint[][];
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcLiquidityResponse = & Omit<LiquidityResponse, 'liquidityCurves'> & {
|
||||||
|
source: string;
|
||||||
|
liquidityCurves: RpcLiquidityCurvePoint[][];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenResponse {
|
||||||
|
address: Address;
|
||||||
|
symbol: string;
|
||||||
|
decimals: number;
|
||||||
|
gasCost: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcTokenResponse = TokenResponse;
|
||||||
|
|
||||||
|
export class SamplerServiceRpcClient {
|
||||||
|
private _rpcClient: OpenRpcClient;
|
||||||
|
|
||||||
|
public constructor(url: string) {
|
||||||
|
const transport = new HTTPTransport(url);
|
||||||
|
this._rpcClient = new OpenRpcClient(new RequestManager([transport]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _requestAsync<TResult, TArgs = any>(method: string, params: TArgs[] = []): Promise<TResult> {
|
||||||
|
return this._rpcClient.request({ method, params }) as Promise<TResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getChainIdAsync(): Promise<number> {
|
||||||
|
return this._requestAsync<number>('get_chain_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSellLiquidityAsync(reqs: LiquidityRequest[]): Promise<LiquidityResponse[]> {
|
||||||
|
const resp = await this._requestAsync<RpcLiquidityResponse[], RpcLiquidityRequest[]>(
|
||||||
|
'get_sell_liquidity',
|
||||||
|
[
|
||||||
|
reqs.map(r => ({
|
||||||
|
...r,
|
||||||
|
inputAmount: r.inputAmount.toString(10),
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
return resp.map(r => ({
|
||||||
|
...r,
|
||||||
|
liquidityCurves: r.liquidityCurves.map(a => a.map(c => ({
|
||||||
|
...c,
|
||||||
|
buyAmount: new BigNumber(c.buyAmount),
|
||||||
|
sellAmount: new BigNumber(c.sellAmount),
|
||||||
|
}))),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getBuyLiquidityAsync(reqs: LiquidityRequest[]): Promise<LiquidityResponse[]> {
|
||||||
|
const resp = await this._requestAsync<RpcLiquidityResponse[], RpcLiquidityRequest[]>(
|
||||||
|
'get_buy_liquidity',
|
||||||
|
[
|
||||||
|
reqs.map(r => ({
|
||||||
|
...r,
|
||||||
|
inputAmount: r.inputAmount.toString(10),
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
return resp.map(r => ({
|
||||||
|
...r,
|
||||||
|
liquidityCurves: r.liquidityCurves.map(a => a.map(c => ({
|
||||||
|
...c,
|
||||||
|
buyAmount: new BigNumber(c.buyAmount),
|
||||||
|
sellAmount: new BigNumber(c.sellAmount),
|
||||||
|
}))),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getPricesAsync(reqs: PriceRequest[]): Promise<BigNumber[]> {
|
||||||
|
const resp = await this._requestAsync<DecimalString[], RpcPriceRequest[]>(
|
||||||
|
'get_prices',
|
||||||
|
[ reqs ],
|
||||||
|
);
|
||||||
|
return resp.map(r => new BigNumber(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getTokensAsync(addresses: Address[]): Promise<TokenResponse[]> {
|
||||||
|
return this._requestAsync<RpcTokenResponse[], Address[]>(
|
||||||
|
'get_tokens',
|
||||||
|
[ addresses ],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import { V4RFQIndicativeQuote } from '@0x/quote-server';
|
|||||||
import { MarketOperation } from '@0x/types';
|
import { MarketOperation } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types';
|
import { Bytes, NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types';
|
||||||
import { QuoteRequestor } from '../../utils/quote_requestor';
|
import { QuoteRequestor } from '../../utils/quote_requestor';
|
||||||
import { PriceComparisonsReport, QuoteReport } from '../quote_report_generator';
|
import { PriceComparisonsReport, QuoteReport } from '../quote_report_generator';
|
||||||
|
|
||||||
@@ -172,12 +172,14 @@ export type NativeLimitOrderFillData = FillQuoteTransformerLimitOrderInfo;
|
|||||||
export type NativeFillData = NativeRfqOrderFillData | NativeLimitOrderFillData;
|
export type NativeFillData = NativeRfqOrderFillData | NativeLimitOrderFillData;
|
||||||
|
|
||||||
// Represents an individual DEX sample from the sampler contract
|
// Represents an individual DEX sample from the sampler contract
|
||||||
export interface DexSample<TFillData extends FillData = FillData> {
|
export interface DexSample {
|
||||||
source: ERC20BridgeSource;
|
source: ERC20BridgeSource;
|
||||||
fillData: TFillData;
|
encodedFillData: Bytes;
|
||||||
input: BigNumber;
|
input: BigNumber;
|
||||||
output: BigNumber;
|
output: BigNumber;
|
||||||
|
gasCost: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CurveFillData extends FillData {
|
export interface CurveFillData extends FillData {
|
||||||
fromTokenIdx: number;
|
fromTokenIdx: number;
|
||||||
toTokenIdx: number;
|
toTokenIdx: number;
|
||||||
@@ -273,12 +275,12 @@ export interface LidoFillData extends FillData {
|
|||||||
/**
|
/**
|
||||||
* Represents a node on a fill path.
|
* Represents a node on a fill path.
|
||||||
*/
|
*/
|
||||||
export interface Fill<TFillData extends FillData = FillData> {
|
export interface Fill {
|
||||||
// basic data for every fill
|
// basic data for every fill
|
||||||
source: ERC20BridgeSource;
|
source: ERC20BridgeSource;
|
||||||
// TODO jacob people seem to agree that orderType here is more readable
|
// TODO jacob people seem to agree that orderType here is more readable
|
||||||
type: FillQuoteTransformerOrderType; // should correspond with TFillData
|
type: FillQuoteTransformerOrderType; // should correspond with TFillData
|
||||||
fillData: TFillData;
|
encodedFillData: Bytes;
|
||||||
// Unique ID of the original source path this fill belongs to.
|
// Unique ID of the original source path this fill belongs to.
|
||||||
// This is generated when the path is generated and is useful to distinguish
|
// This is generated when the path is generated and is useful to distinguish
|
||||||
// paths that have the same `source` IDs but are distinct (e.g., Curves).
|
// paths that have the same `source` IDs but are distinct (e.g., Curves).
|
||||||
@@ -300,10 +302,10 @@ export interface Fill<TFillData extends FillData = FillData> {
|
|||||||
/**
|
/**
|
||||||
* Represents continguous fills on a path that have been merged together.
|
* Represents continguous fills on a path that have been merged together.
|
||||||
*/
|
*/
|
||||||
export interface CollapsedFill<TFillData extends FillData = FillData> {
|
export interface CollapsedFill {
|
||||||
source: ERC20BridgeSource;
|
source: ERC20BridgeSource;
|
||||||
type: FillQuoteTransformerOrderType; // should correspond with TFillData
|
type: FillQuoteTransformerOrderType; // should correspond with TFillData
|
||||||
fillData: TFillData;
|
encodedFillData: Bytes;
|
||||||
// Unique ID of the original source path this fill belongs to.
|
// Unique ID of the original source path this fill belongs to.
|
||||||
// This is generated when the path is generated and is useful to distinguish
|
// This is generated when the path is generated and is useful to distinguish
|
||||||
// paths that have the same `source` IDs but are distinct (e.g., Curves).
|
// paths that have the same `source` IDs but are distinct (e.g., Curves).
|
||||||
@@ -328,7 +330,7 @@ export interface CollapsedFill<TFillData extends FillData = FillData> {
|
|||||||
/**
|
/**
|
||||||
* A `CollapsedFill` wrapping a native order.
|
* A `CollapsedFill` wrapping a native order.
|
||||||
*/
|
*/
|
||||||
export interface NativeCollapsedFill extends CollapsedFill<NativeFillData> {}
|
export interface NativeCollapsedFill extends CollapsedFill {}
|
||||||
|
|
||||||
export interface OptimizedMarketOrderBase<TFillData extends FillData = FillData> {
|
export interface OptimizedMarketOrderBase<TFillData extends FillData = FillData> {
|
||||||
source: ERC20BridgeSource;
|
source: ERC20BridgeSource;
|
||||||
@@ -481,7 +483,7 @@ export interface SourceQuoteOperation<TFillData extends FillData = FillData> ext
|
|||||||
export interface OptimizerResult {
|
export interface OptimizerResult {
|
||||||
optimizedOrders: OptimizedMarketOrder[];
|
optimizedOrders: OptimizedMarketOrder[];
|
||||||
sourceFlags: bigint;
|
sourceFlags: bigint;
|
||||||
liquidityDelivered: CollapsedFill[] | DexSample<MultiHopFillData>;
|
// liquidityDelivered: CollapsedFill[] | DexSample<MultiHopFillData>;
|
||||||
marketSideLiquidity: MarketSideLiquidity;
|
marketSideLiquidity: MarketSideLiquidity;
|
||||||
adjustedRate: BigNumber;
|
adjustedRate: BigNumber;
|
||||||
unoptimizedPath?: CollapsedPath;
|
unoptimizedPath?: CollapsedPath;
|
||||||
@@ -494,7 +496,7 @@ export interface OptimizerResultWithReport extends OptimizerResult {
|
|||||||
priceComparisonsReport?: PriceComparisonsReport;
|
priceComparisonsReport?: PriceComparisonsReport;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MarketDepthSide = Array<Array<DexSample<FillData>>>;
|
export type MarketDepthSide = Array<Array<DexSample>>;
|
||||||
|
|
||||||
export interface MarketDepth {
|
export interface MarketDepth {
|
||||||
bids: MarketDepthSide;
|
bids: MarketDepthSide;
|
||||||
@@ -520,8 +522,8 @@ export interface MarketSideLiquidity {
|
|||||||
export interface RawQuotes {
|
export interface RawQuotes {
|
||||||
nativeOrders: NativeOrderWithFillableAmounts[];
|
nativeOrders: NativeOrderWithFillableAmounts[];
|
||||||
rfqtIndicativeQuotes: V4RFQIndicativeQuote[];
|
rfqtIndicativeQuotes: V4RFQIndicativeQuote[];
|
||||||
twoHopQuotes: Array<DexSample<MultiHopFillData>>;
|
// twoHopQuotes: Array<DexSample<MultiHopFillData>>;
|
||||||
dexQuotes: Array<Array<DexSample<FillData>>>;
|
dexQuotes: Array<Array<DexSample>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenAdjacencyGraph {
|
export interface TokenAdjacencyGraph {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as heartbeats from 'heartbeats';
|
import * as heartbeats from 'heartbeats';
|
||||||
|
import fetch from 'axios';
|
||||||
|
|
||||||
import { constants } from '../constants';
|
import { constants } from '../constants';
|
||||||
import { SwapQuoterError } from '../types';
|
import { SwapQuoterError } from '../types';
|
||||||
@@ -61,7 +62,7 @@ export class ProtocolFeeUtils {
|
|||||||
private async _getGasPriceFromGasStationOrThrowAsync(): Promise<BigNumber> {
|
private async _getGasPriceFromGasStationOrThrowAsync(): Promise<BigNumber> {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(this._ethGasStationUrl);
|
const res = await fetch(this._ethGasStationUrl);
|
||||||
const gasInfo = await res.json();
|
const gasInfo = res.data;
|
||||||
// Eth Gas Station result is gwei * 10
|
// Eth Gas Station result is gwei * 10
|
||||||
// tslint:disable-next-line:custom-no-magic-numbers
|
// tslint:disable-next-line:custom-no-magic-numbers
|
||||||
const BASE_TEN = 10;
|
const BASE_TEN = 10;
|
||||||
|
|||||||
@@ -73,47 +73,48 @@ export interface PriceComparisonsReport {
|
|||||||
export function generateQuoteReport(
|
export function generateQuoteReport(
|
||||||
marketOperation: MarketOperation,
|
marketOperation: MarketOperation,
|
||||||
nativeOrders: NativeOrderWithFillableAmounts[],
|
nativeOrders: NativeOrderWithFillableAmounts[],
|
||||||
liquidityDelivered: ReadonlyArray<CollapsedFill> | DexSample<MultiHopFillData>,
|
// liquidityDelivered: ReadonlyArray<CollapsedFill> | DexSample<MultiHopFillData>,
|
||||||
comparisonPrice?: BigNumber | undefined,
|
comparisonPrice?: BigNumber | undefined,
|
||||||
quoteRequestor?: QuoteRequestor,
|
quoteRequestor?: QuoteRequestor,
|
||||||
): QuoteReport {
|
): QuoteReport {
|
||||||
const nativeOrderSourcesConsidered = nativeOrders.map(order =>
|
throw new Error(`Not implemented`);
|
||||||
nativeOrderToReportEntry(order.type, order as any, order.fillableTakerAmount, comparisonPrice, quoteRequestor),
|
// const nativeOrderSourcesConsidered = nativeOrders.map(order =>
|
||||||
);
|
// nativeOrderToReportEntry(order.type, order as any, order.fillableTakerAmount, comparisonPrice, quoteRequestor),
|
||||||
const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRfqt)];
|
// );
|
||||||
|
// const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRfqt)];
|
||||||
let sourcesDelivered;
|
//
|
||||||
if (Array.isArray(liquidityDelivered)) {
|
// let sourcesDelivered;
|
||||||
// create easy way to look up fillable amounts
|
// if (Array.isArray(liquidityDelivered)) {
|
||||||
const nativeOrderSignaturesToFillableAmounts = _.fromPairs(
|
// // create easy way to look up fillable amounts
|
||||||
nativeOrders.map(o => {
|
// const nativeOrderSignaturesToFillableAmounts = _.fromPairs(
|
||||||
return [_nativeDataToId(o), o.fillableTakerAmount];
|
// nativeOrders.map(o => {
|
||||||
}),
|
// return [_nativeDataToId(o), o.fillableTakerAmount];
|
||||||
);
|
// }),
|
||||||
// map sources delivered
|
// );
|
||||||
sourcesDelivered = liquidityDelivered.map(collapsedFill => {
|
// // map sources delivered
|
||||||
if (_isNativeOrderFromCollapsedFill(collapsedFill)) {
|
// sourcesDelivered = liquidityDelivered.map(collapsedFill => {
|
||||||
return nativeOrderToReportEntry(
|
// if (_isNativeOrderFromCollapsedFill(collapsedFill)) {
|
||||||
collapsedFill.type,
|
// return nativeOrderToReportEntry(
|
||||||
collapsedFill.fillData,
|
// collapsedFill.type,
|
||||||
nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)],
|
// collapsedFill.fillData,
|
||||||
comparisonPrice,
|
// nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)],
|
||||||
quoteRequestor,
|
// comparisonPrice,
|
||||||
);
|
// quoteRequestor,
|
||||||
} else {
|
// );
|
||||||
return dexSampleToReportSource(collapsedFill, marketOperation);
|
// } else {
|
||||||
}
|
// return dexSampleToReportSource(collapsedFill, marketOperation);
|
||||||
});
|
// }
|
||||||
} else {
|
// });
|
||||||
sourcesDelivered = [
|
// } else {
|
||||||
// tslint:disable-next-line: no-unnecessary-type-assertion
|
// sourcesDelivered = [
|
||||||
multiHopSampleToReportSource(liquidityDelivered as DexSample<MultiHopFillData>, marketOperation),
|
// // tslint:disable-next-line: no-unnecessary-type-assertion
|
||||||
];
|
// multiHopSampleToReportSource(liquidityDelivered as DexSample<MultiHopFillData>, marketOperation),
|
||||||
}
|
// ];
|
||||||
return {
|
// }
|
||||||
sourcesConsidered,
|
// return {
|
||||||
sourcesDelivered,
|
// sourcesConsidered,
|
||||||
};
|
// sourcesDelivered,
|
||||||
|
// };
|
||||||
}
|
}
|
||||||
|
|
||||||
function _nativeDataToId(data: { signature: Signature }): string {
|
function _nativeDataToId(data: { signature: Signature }): string {
|
||||||
@@ -126,31 +127,32 @@ function _nativeDataToId(data: { signature: Signature }): string {
|
|||||||
* NOTE: this is used for the QuoteReport and quote price comparison data
|
* NOTE: this is used for the QuoteReport and quote price comparison data
|
||||||
*/
|
*/
|
||||||
export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOperation): BridgeQuoteReportEntry {
|
export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOperation): BridgeQuoteReportEntry {
|
||||||
const liquiditySource = ds.source;
|
throw new Error(`Not implemented`);
|
||||||
|
// const liquiditySource = ds.source;
|
||||||
if (liquiditySource === ERC20BridgeSource.Native) {
|
//
|
||||||
throw new Error(`Unexpected liquidity source Native`);
|
// if (liquiditySource === ERC20BridgeSource.Native) {
|
||||||
}
|
// throw new Error(`Unexpected liquidity source Native`);
|
||||||
|
// }
|
||||||
// input and output map to different values
|
//
|
||||||
// based on the market operation
|
// // input and output map to different values
|
||||||
if (marketOperation === MarketOperation.Buy) {
|
// // based on the market operation
|
||||||
return {
|
// if (marketOperation === MarketOperation.Buy) {
|
||||||
makerAmount: ds.input,
|
// return {
|
||||||
takerAmount: ds.output,
|
// makerAmount: ds.input,
|
||||||
liquiditySource,
|
// takerAmount: ds.output,
|
||||||
fillData: ds.fillData,
|
// liquiditySource,
|
||||||
};
|
// fillData: ds.fillData,
|
||||||
} else if (marketOperation === MarketOperation.Sell) {
|
// };
|
||||||
return {
|
// } else if (marketOperation === MarketOperation.Sell) {
|
||||||
makerAmount: ds.output,
|
// return {
|
||||||
takerAmount: ds.input,
|
// makerAmount: ds.output,
|
||||||
liquiditySource,
|
// takerAmount: ds.input,
|
||||||
fillData: ds.fillData,
|
// liquiditySource,
|
||||||
};
|
// fillData: ds.fillData,
|
||||||
} else {
|
// };
|
||||||
throw new Error(`Unexpected marketOperation ${marketOperation}`);
|
// } else {
|
||||||
}
|
// throw new Error(`Unexpected marketOperation ${marketOperation}`);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,31 +160,32 @@ export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOp
|
|||||||
* NOTE: this is used for the QuoteReport and quote price comparison data
|
* NOTE: this is used for the QuoteReport and quote price comparison data
|
||||||
*/
|
*/
|
||||||
export function multiHopSampleToReportSource(
|
export function multiHopSampleToReportSource(
|
||||||
ds: DexSample<MultiHopFillData>,
|
ds: DexSample,
|
||||||
marketOperation: MarketOperation,
|
marketOperation: MarketOperation,
|
||||||
): MultiHopQuoteReportEntry {
|
): MultiHopQuoteReportEntry {
|
||||||
const { firstHopSource: firstHop, secondHopSource: secondHop } = ds.fillData;
|
throw new Error(`Not implemented`);
|
||||||
// input and output map to different values
|
// const { firstHopSource: firstHop, secondHopSource: secondHop } = ds.fillData;
|
||||||
// based on the market operation
|
// // input and output map to different values
|
||||||
if (marketOperation === MarketOperation.Buy) {
|
// // based on the market operation
|
||||||
return {
|
// if (marketOperation === MarketOperation.Buy) {
|
||||||
liquiditySource: ERC20BridgeSource.MultiHop,
|
// return {
|
||||||
makerAmount: ds.input,
|
// liquiditySource: ERC20BridgeSource.MultiHop,
|
||||||
takerAmount: ds.output,
|
// makerAmount: ds.input,
|
||||||
fillData: ds.fillData,
|
// takerAmount: ds.output,
|
||||||
hopSources: [firstHop.source, secondHop.source],
|
// fillData: ds.fillData,
|
||||||
};
|
// hopSources: [firstHop.source, secondHop.source],
|
||||||
} else if (marketOperation === MarketOperation.Sell) {
|
// };
|
||||||
return {
|
// } else if (marketOperation === MarketOperation.Sell) {
|
||||||
liquiditySource: ERC20BridgeSource.MultiHop,
|
// return {
|
||||||
makerAmount: ds.output,
|
// liquiditySource: ERC20BridgeSource.MultiHop,
|
||||||
takerAmount: ds.input,
|
// makerAmount: ds.output,
|
||||||
fillData: ds.fillData,
|
// takerAmount: ds.input,
|
||||||
hopSources: [firstHop.source, secondHop.source],
|
// fillData: ds.fillData,
|
||||||
};
|
// hopSources: [firstHop.source, secondHop.source],
|
||||||
} else {
|
// };
|
||||||
throw new Error(`Unexpected marketOperation ${marketOperation}`);
|
// } else {
|
||||||
}
|
// throw new Error(`Unexpected marketOperation ${marketOperation}`);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
function _isNativeOrderFromCollapsedFill(cf: CollapsedFill): cf is NativeCollapsedFill {
|
function _isNativeOrderFromCollapsedFill(cf: CollapsedFill): cf is NativeCollapsedFill {
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ export class QuoteRequestor {
|
|||||||
private readonly _altRfqCreds?: { altRfqApiKey: string; altRfqProfile: string },
|
private readonly _altRfqCreds?: { altRfqApiKey: string; altRfqProfile: string },
|
||||||
private readonly _warningLogger: LogFunction = constants.DEFAULT_WARNING_LOGGER,
|
private readonly _warningLogger: LogFunction = constants.DEFAULT_WARNING_LOGGER,
|
||||||
private readonly _infoLogger: LogFunction = constants.DEFAULT_INFO_LOGGER,
|
private readonly _infoLogger: LogFunction = constants.DEFAULT_INFO_LOGGER,
|
||||||
private readonly _expiryBufferMs: number = constants.DEFAULT_SWAP_QUOTER_OPTS.expiryBufferMs,
|
private readonly _expiryBufferMs: number = 120e3,
|
||||||
private readonly _metrics?: MetricsProxy,
|
private readonly _metrics?: MetricsProxy,
|
||||||
) {
|
) {
|
||||||
rfqMakerBlacklist.infoLogger = this._infoLogger;
|
rfqMakerBlacklist.infoLogger = this._infoLogger;
|
||||||
|
|||||||
@@ -155,8 +155,8 @@ export function fillQuoteOrders(
|
|||||||
if (remainingInput.lte(0)) {
|
if (remainingInput.lte(0)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const { source, fillData } = fill;
|
const { source, encodedFillData } = fill;
|
||||||
const gas = gasSchedule[source] === undefined ? 0 : gasSchedule[source]!(fillData);
|
const gas = gasSchedule[source] === undefined ? 0 : gasSchedule[source]!(encodedFillData);
|
||||||
result.gas += new BigNumber(gas).toNumber();
|
result.gas += new BigNumber(gas).toNumber();
|
||||||
result.inputBySource[source] = result.inputBySource[source] || ZERO_AMOUNT;
|
result.inputBySource[source] = result.inputBySource[source] || ZERO_AMOUNT;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig",
|
"extends": "../../tsconfig",
|
||||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true, "lib": ["es2019"] },
|
||||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
"include": ["./src/**/*", "./generated-wrappers/**/*"],
|
||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/BalanceChecker.json",
|
"generated-artifacts/BalanceChecker.json",
|
||||||
"generated-artifacts/ERC20BridgeSampler.json",
|
"generated-artifacts/ERC20BridgeSampler.json",
|
||||||
|
|||||||
25
yarn.lock
25
yarn.lock
@@ -2607,6 +2607,16 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" ">= 8"
|
"@types/node" ">= 8"
|
||||||
|
|
||||||
|
"@open-rpc/client-js@^1.7.1":
|
||||||
|
version "1.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@open-rpc/client-js/-/client-js-1.7.1.tgz#763d75c046a40f57428b861e16a9a69aaa630cb1"
|
||||||
|
integrity sha512-DycSYZUGSUwFl+k9T8wLBSGA8f2hYkvS5A9AB94tBOuU8QlP468NS5ZtAxy72dF4g2WW0genwNJdfeFnHnaxXQ==
|
||||||
|
dependencies:
|
||||||
|
isomorphic-fetch "^3.0.0"
|
||||||
|
isomorphic-ws "^4.0.1"
|
||||||
|
strict-event-emitter-types "^2.0.0"
|
||||||
|
ws "^7.0.0"
|
||||||
|
|
||||||
"@sindresorhus/is@^0.14.0":
|
"@sindresorhus/is@^0.14.0":
|
||||||
version "0.14.0"
|
version "0.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
||||||
@@ -7969,6 +7979,11 @@ isomorphic-fetch@^3.0.0:
|
|||||||
node-fetch "^2.6.1"
|
node-fetch "^2.6.1"
|
||||||
whatwg-fetch "^3.4.1"
|
whatwg-fetch "^3.4.1"
|
||||||
|
|
||||||
|
isomorphic-ws@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc"
|
||||||
|
integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==
|
||||||
|
|
||||||
isstream@0.1.x, isstream@~0.1.2:
|
isstream@0.1.x, isstream@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||||
@@ -11657,6 +11672,11 @@ stream-to-pull-stream@^1.7.1:
|
|||||||
looper "^3.0.0"
|
looper "^3.0.0"
|
||||||
pull-stream "^3.2.3"
|
pull-stream "^3.2.3"
|
||||||
|
|
||||||
|
strict-event-emitter-types@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz#05e15549cb4da1694478a53543e4e2f4abcf277f"
|
||||||
|
integrity sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==
|
||||||
|
|
||||||
strict-uri-encode@^1.0.0:
|
strict-uri-encode@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
@@ -13565,6 +13585,11 @@ ws@^5.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
async-limiter "~1.0.0"
|
||||||
|
|
||||||
|
ws@^7.0.0:
|
||||||
|
version "7.5.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881"
|
||||||
|
integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==
|
||||||
|
|
||||||
wsrun@^5.2.4:
|
wsrun@^5.2.4:
|
||||||
version "5.2.4"
|
version "5.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/wsrun/-/wsrun-5.2.4.tgz#6eb6c3ccd3327721a8df073a5e3578fb0dea494e"
|
resolved "https://registry.yarnpkg.com/wsrun/-/wsrun-5.2.4.tgz#6eb6c3ccd3327721a8df073a5e3578fb0dea494e"
|
||||||
|
|||||||
Reference in New Issue
Block a user