* Remove old JS router and add a FillAdjustor Clean up JS router and unused functions Remove more unused functions, add adjustment of fills Comment on why we use fill over sample update CODEOWNERS lint Clean up Fill removing unused properties Remove CollapsedFills, omit flags bigint Create GasSchedule vs FeeSchedule, return Fill and gas on OptimizedOrder Use Fill Adjustment in Phase2 of routing Fix Limit orders being treated as VIP * Fix case where dex liquidity is empty * Use best gas adjusted pricing for fee sources * CHANGELOG
159 lines
5.0 KiB
TypeScript
159 lines
5.0 KiB
TypeScript
// tslint:disable:custom-no-magic-numbers
|
|
import { BigNumber } from '@0x/utils';
|
|
import * as chai from 'chai';
|
|
import * as _ from 'lodash';
|
|
import 'mocha';
|
|
|
|
import { SOURCE_FLAGS } from '../src';
|
|
import { MarketOperation } from '../src/types';
|
|
import { getComparisonPrices } from '../src/utils/market_operation_utils/comparison_price';
|
|
import { SourceFilters } from '../src/utils/market_operation_utils/source_filters';
|
|
import { DexSample, ERC20BridgeSource, MarketSideLiquidity } from '../src/utils/market_operation_utils/types';
|
|
|
|
import { chaiSetup } from './utils/chai_setup';
|
|
|
|
chaiSetup.configure();
|
|
const expect = chai.expect;
|
|
|
|
const DAI_TOKEN = '0x6b175474e89094c44da98b954eedeac495271d0f';
|
|
const ETH_TOKEN = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
|
const GAS_PRICE = new BigNumber(50e9); // 50 gwei
|
|
const NATIVE_ORDER_GAS = 220e3; // 220K gas
|
|
|
|
// DEX samples to fill in MarketSideLiquidity
|
|
const curveSample: DexSample = {
|
|
source: ERC20BridgeSource.Curve,
|
|
input: new BigNumber(10000),
|
|
output: new BigNumber(10001),
|
|
fillData: {},
|
|
};
|
|
const uniswapSample1: DexSample = {
|
|
source: ERC20BridgeSource.UniswapV2,
|
|
input: new BigNumber(10003),
|
|
output: new BigNumber(10004),
|
|
fillData: {},
|
|
};
|
|
const dexQuotes: DexSample[] = [curveSample, uniswapSample1];
|
|
|
|
const feeSchedule = {
|
|
[ERC20BridgeSource.Native]: _.constant({
|
|
gas: NATIVE_ORDER_GAS,
|
|
fee: GAS_PRICE.times(NATIVE_ORDER_GAS),
|
|
}),
|
|
};
|
|
|
|
const exchangeProxyOverhead = (sourceFlags: bigint) => {
|
|
if ([SOURCE_FLAGS.RfqOrder].includes(sourceFlags)) {
|
|
return new BigNumber(20e3).times(GAS_PRICE);
|
|
} else {
|
|
return new BigNumber(200e3).times(GAS_PRICE);
|
|
}
|
|
};
|
|
|
|
const buyMarketSideLiquidity: MarketSideLiquidity = {
|
|
// needed params
|
|
outputAmountPerEth: new BigNumber(500),
|
|
inputAmountPerEth: new BigNumber(1),
|
|
side: MarketOperation.Buy,
|
|
makerTokenDecimals: 18,
|
|
takerTokenDecimals: 18,
|
|
// extra
|
|
inputAmount: new BigNumber(0),
|
|
inputToken: ETH_TOKEN,
|
|
outputToken: DAI_TOKEN,
|
|
quotes: {
|
|
twoHopQuotes: [],
|
|
rfqtIndicativeQuotes: [],
|
|
dexQuotes: [dexQuotes],
|
|
nativeOrders: [],
|
|
},
|
|
quoteSourceFilters: new SourceFilters(),
|
|
isRfqSupported: false,
|
|
blockNumber: 1337420,
|
|
};
|
|
|
|
const sellMarketSideLiquidity: MarketSideLiquidity = {
|
|
// needed params
|
|
outputAmountPerEth: new BigNumber(500),
|
|
inputAmountPerEth: new BigNumber(1),
|
|
side: MarketOperation.Sell,
|
|
makerTokenDecimals: 18,
|
|
takerTokenDecimals: 18,
|
|
// extra
|
|
inputAmount: new BigNumber(0),
|
|
inputToken: ETH_TOKEN,
|
|
outputToken: DAI_TOKEN,
|
|
quotes: {
|
|
dexQuotes: [dexQuotes],
|
|
nativeOrders: [],
|
|
twoHopQuotes: [],
|
|
rfqtIndicativeQuotes: [],
|
|
},
|
|
quoteSourceFilters: new SourceFilters(),
|
|
isRfqSupported: false,
|
|
blockNumber: 1337420,
|
|
};
|
|
|
|
describe('getComparisonPrices', async () => {
|
|
it('should create a proper comparison price for Sells', () => {
|
|
// test selling 10 ETH for DAI
|
|
// here, ETH is the input token
|
|
// and DAI is the output token
|
|
const AMOUNT = new BigNumber(10 * 1e18);
|
|
// raw maker over taker rate, let's say is 500 flat
|
|
const adjustedRate = new BigNumber(500);
|
|
|
|
const comparisonPrices = getComparisonPrices(
|
|
adjustedRate,
|
|
AMOUNT,
|
|
sellMarketSideLiquidity,
|
|
feeSchedule,
|
|
exchangeProxyOverhead,
|
|
);
|
|
|
|
// expected outcome
|
|
const EXPECTED_PRICE = new BigNumber('500.6');
|
|
|
|
expect(comparisonPrices.wholeOrder).to.deep.eq(EXPECTED_PRICE);
|
|
});
|
|
it('should create a proper comparison price for Buys', () => {
|
|
// test buying 10 ETH with DAI
|
|
// here, ETH is the input token
|
|
// and DAI is the output token (now from the maker's perspective)
|
|
const AMOUNT = new BigNumber(10 * 1e18);
|
|
|
|
// raw maker over taker rate, let's say is ETH/DAI rate is 500 flat
|
|
const adjustedRate = new BigNumber(1).dividedBy(new BigNumber(500));
|
|
|
|
const comparisonPrices = getComparisonPrices(
|
|
adjustedRate,
|
|
AMOUNT,
|
|
buyMarketSideLiquidity,
|
|
feeSchedule,
|
|
exchangeProxyOverhead,
|
|
);
|
|
|
|
// expected outcome
|
|
const EXPECTED_PRICE = new BigNumber('0.0020024029');
|
|
|
|
expect(comparisonPrices.wholeOrder).to.deep.eq(EXPECTED_PRICE);
|
|
});
|
|
it('should not return a price if takerAmount is < 0', () => {
|
|
// test selling 0.00001 ETH for DAI
|
|
// this will result in a negative comparison price, but here we should return undefined
|
|
const AMOUNT = new BigNumber(0.00001 * 1e18);
|
|
// raw maker over taker rate, let's say is 500 flat
|
|
const adjustedRate = new BigNumber(500);
|
|
|
|
const comparisonPrices = getComparisonPrices(
|
|
adjustedRate,
|
|
AMOUNT,
|
|
sellMarketSideLiquidity,
|
|
feeSchedule,
|
|
exchangeProxyOverhead,
|
|
);
|
|
|
|
expect(comparisonPrices.wholeOrder === undefined);
|
|
});
|
|
});
|