* `@0x/asset-swapper`: Add ERC20Bridge aggregator library. * `@0x/asset-swapper`: Finish off `aggregate.ts`. * `@0x/types`: Add `OrderWithoutDomain` type. * `@0x/asset-swapper`: Add testing infra for sampler/aggregator. * `@0x/types`: Add `SignedOrderWithoutDomain` type. * `@0x/asset-swapper`: Update aggregator to take and return orders with signatures. * `@0x/asset-swapper`: Fix broken aggregator tests. * `@0x/asset-swapper`: Pass the sampler contract into aggregator entry points. * `@0x/contract-artifacts`: Add `IERC20BridgeSampler` artifact. * `@0x/contract-wrappers`: Add `IERC20BridgeSampler` wrapper. * `@0x/asset-swapper`: Address review comments. * fixed testing * refactored aggregate.ts and embeded into asset-swapper * added adjusted rates for taker and maker fees * remove PrunedSignedOrders * updated contract-addresses and addressed some other todos * streamlined logic * patched in lawrences changes * renamed aggregator utils and removed market_utils.ts * added ack heartbeats * fixed bug * patches * added dummy order things * Dummy with valid sig * Tweak gas price calculation to wei * added test coverage and fixed bugs * fixed migrations * Fix CHANGELOGs and types export * Deploy latest ERC20BridgeSampler on Mainnet * `@0x/types` Revert CHANGELOG. * `@0x/asset-swapper`: Address review comments. `@0x/contract-addresses`: Make kyber lowercase. * made protocol fee multiplier async * `@0x/asset-swapper: Fix build errors and do some code cleanup. * use assetDataUtils where possible
278 lines
12 KiB
TypeScript
278 lines
12 KiB
TypeScript
import { Orderbook } from '@0x/orderbook';
|
|
import { Web3ProviderEngine } from '@0x/subproviders';
|
|
import { AssetPairsItem, SignedOrder } from '@0x/types';
|
|
import { BigNumber } from '@0x/utils';
|
|
import * as chai from 'chai';
|
|
import 'mocha';
|
|
import * as TypeMoq from 'typemoq';
|
|
|
|
import { SwapQuoter } from '../src';
|
|
import { constants } from '../src/constants';
|
|
import { LiquidityForTakerMakerAssetDataPair, SignedOrderWithFillableAmounts } from '../src/types';
|
|
|
|
import { chaiSetup } from './utils/chai_setup';
|
|
import { mockAvailableAssetDatas, mockedSwapQuoterWithFillableAmounts, orderbookMock } from './utils/mocks';
|
|
import { testOrderFactory } from './utils/test_order_factory';
|
|
import { baseUnitAmount } from './utils/utils';
|
|
|
|
chaiSetup.configure();
|
|
const expect = chai.expect;
|
|
|
|
const FAKE_SRA_URL = 'https://fakeurl.com';
|
|
const FAKE_TAKER_ASSET_DATA = '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48';
|
|
const FAKE_MAKER_ASSET_DATA = '0xf47261b00000000000000000000000009f5B0C7e1623793bF0620569b9749e79DF6D0bC5';
|
|
const TOKEN_DECIMALS = 18;
|
|
const DAI_ASSET_DATA = '0xf47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359"';
|
|
const WETH_ASSET_DATA = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
|
const WETH_DECIMALS = constants.ETHER_TOKEN_DECIMALS;
|
|
const ZERO = new BigNumber(0);
|
|
|
|
const assetsToAssetPairItems = (makerAssetData: string, takerAssetData: string): AssetPairsItem[] => {
|
|
const defaultAssetPairItem = {
|
|
minAmount: ZERO,
|
|
maxAmount: ZERO,
|
|
precision: TOKEN_DECIMALS,
|
|
};
|
|
return [
|
|
{
|
|
assetDataA: {
|
|
...defaultAssetPairItem,
|
|
assetData: makerAssetData,
|
|
},
|
|
assetDataB: {
|
|
...defaultAssetPairItem,
|
|
assetData: takerAssetData,
|
|
},
|
|
},
|
|
{
|
|
assetDataA: {
|
|
...defaultAssetPairItem,
|
|
assetData: takerAssetData,
|
|
},
|
|
assetDataB: {
|
|
...defaultAssetPairItem,
|
|
assetData: makerAssetData,
|
|
},
|
|
},
|
|
];
|
|
};
|
|
|
|
const expectLiquidityResult = async (
|
|
web3Provider: Web3ProviderEngine,
|
|
orderbook: Orderbook,
|
|
orders: SignedOrderWithFillableAmounts[],
|
|
expectedLiquidityResult: LiquidityForTakerMakerAssetDataPair,
|
|
) => {
|
|
const mockedSwapQuoter = mockedSwapQuoterWithFillableAmounts(
|
|
web3Provider,
|
|
orderbook,
|
|
FAKE_MAKER_ASSET_DATA,
|
|
WETH_ASSET_DATA,
|
|
orders,
|
|
);
|
|
const liquidityResult = await mockedSwapQuoter.object.getLiquidityForMakerTakerAssetDataPairAsync(
|
|
FAKE_MAKER_ASSET_DATA,
|
|
WETH_ASSET_DATA,
|
|
);
|
|
expect(liquidityResult).to.deep.equal(expectedLiquidityResult);
|
|
};
|
|
|
|
// tslint:disable:custom-no-magic-numbers
|
|
describe('SwapQuoter', () => {
|
|
describe('getLiquidityForMakerTakerAssetDataPairAsync', () => {
|
|
const mockWeb3Provider = TypeMoq.Mock.ofType(Web3ProviderEngine);
|
|
const mockOrderbook = orderbookMock();
|
|
|
|
beforeEach(() => {
|
|
mockWeb3Provider.reset();
|
|
mockOrderbook.reset();
|
|
});
|
|
|
|
afterEach(() => {
|
|
mockWeb3Provider.verifyAll();
|
|
mockOrderbook.verifyAll();
|
|
});
|
|
|
|
describe('validation', () => {
|
|
it('should ensure takerAssetData is a string', async () => {
|
|
const swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(
|
|
mockWeb3Provider.object,
|
|
FAKE_SRA_URL,
|
|
);
|
|
|
|
expect(
|
|
swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(FAKE_MAKER_ASSET_DATA, false as any),
|
|
).to.be.rejectedWith('Expected takerAssetData to be of type string, encountered: false');
|
|
});
|
|
it('should ensure makerAssetData is a string', async () => {
|
|
const swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(
|
|
mockWeb3Provider.object,
|
|
FAKE_SRA_URL,
|
|
);
|
|
|
|
expect(
|
|
swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(false as any, FAKE_TAKER_ASSET_DATA),
|
|
).to.be.rejectedWith('Expected makerAssetData to be of type string, encountered: false');
|
|
});
|
|
});
|
|
|
|
describe('asset pair not supported', () => {
|
|
it('should return 0s when no asset pair are supported', async () => {
|
|
mockAvailableAssetDatas(mockOrderbook, []);
|
|
|
|
const swapQuoter = new SwapQuoter(mockWeb3Provider.object, mockOrderbook.object);
|
|
const liquidityResult = await swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(
|
|
FAKE_MAKER_ASSET_DATA,
|
|
FAKE_TAKER_ASSET_DATA,
|
|
);
|
|
expect(liquidityResult).to.deep.equal({
|
|
makerAssetAvailableInBaseUnits: new BigNumber(0),
|
|
takerAssetAvailableInBaseUnits: new BigNumber(0),
|
|
});
|
|
});
|
|
|
|
it('should return 0s when only other asset pair supported', async () => {
|
|
mockAvailableAssetDatas(mockOrderbook, assetsToAssetPairItems(FAKE_MAKER_ASSET_DATA, DAI_ASSET_DATA));
|
|
|
|
const swapQuoter = new SwapQuoter(mockWeb3Provider.object, mockOrderbook.object);
|
|
const liquidityResult = await swapQuoter.getLiquidityForMakerTakerAssetDataPairAsync(
|
|
FAKE_MAKER_ASSET_DATA,
|
|
FAKE_TAKER_ASSET_DATA,
|
|
);
|
|
expect(liquidityResult).to.deep.equal({
|
|
makerAssetAvailableInBaseUnits: new BigNumber(0),
|
|
takerAssetAvailableInBaseUnits: new BigNumber(0),
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('assetData is supported', () => {
|
|
// orders
|
|
const sellTenTokensFor10Weth: SignedOrder = testOrderFactory.generateTestSignedOrder({
|
|
makerAssetAmount: baseUnitAmount(10),
|
|
takerAssetAmount: baseUnitAmount(10, WETH_DECIMALS),
|
|
chainId: 42,
|
|
});
|
|
|
|
beforeEach(() => {
|
|
mockAvailableAssetDatas(mockOrderbook, assetsToAssetPairItems(WETH_ASSET_DATA, FAKE_MAKER_ASSET_DATA));
|
|
});
|
|
|
|
it('should return 0s when no orders available', async () => {
|
|
const orders: SignedOrderWithFillableAmounts[] = [];
|
|
const expectedResult = {
|
|
makerAssetAvailableInBaseUnits: new BigNumber(0),
|
|
takerAssetAvailableInBaseUnits: new BigNumber(0),
|
|
};
|
|
await expectLiquidityResult(mockWeb3Provider.object, mockOrderbook.object, orders, expectedResult);
|
|
});
|
|
|
|
it('should return correct computed value when orders provided with full fillableAmounts', async () => {
|
|
const orders: SignedOrderWithFillableAmounts[] = [
|
|
{
|
|
...sellTenTokensFor10Weth,
|
|
...{
|
|
fillableMakerAssetAmount: sellTenTokensFor10Weth.makerAssetAmount,
|
|
fillableTakerAssetAmount: sellTenTokensFor10Weth.takerAssetAmount,
|
|
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
|
|
},
|
|
},
|
|
{
|
|
...sellTenTokensFor10Weth,
|
|
...{
|
|
fillableMakerAssetAmount: sellTenTokensFor10Weth.makerAssetAmount,
|
|
fillableTakerAssetAmount: sellTenTokensFor10Weth.takerAssetAmount,
|
|
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
|
|
},
|
|
},
|
|
];
|
|
const expectedMakerAssetAvailable = orders[0].makerAssetAmount.plus(orders[1].makerAssetAmount);
|
|
const expectedTakerAssetAvailable = orders[0].takerAssetAmount.plus(orders[1].takerAssetAmount);
|
|
|
|
const expectedResult = {
|
|
makerAssetAvailableInBaseUnits: expectedMakerAssetAvailable,
|
|
takerAssetAvailableInBaseUnits: expectedTakerAssetAvailable,
|
|
};
|
|
|
|
await expectLiquidityResult(mockWeb3Provider.object, mockOrderbook.object, orders, expectedResult);
|
|
});
|
|
|
|
it('should return correct computed value with one partial fillableAmounts', async () => {
|
|
const orders: SignedOrderWithFillableAmounts[] = [
|
|
{
|
|
...sellTenTokensFor10Weth,
|
|
...{
|
|
fillableMakerAssetAmount: baseUnitAmount(1),
|
|
fillableTakerAssetAmount: baseUnitAmount(0.5, WETH_DECIMALS),
|
|
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
|
|
},
|
|
},
|
|
];
|
|
|
|
const expectedResult = {
|
|
makerAssetAvailableInBaseUnits: baseUnitAmount(1),
|
|
takerAssetAvailableInBaseUnits: baseUnitAmount(0.5, WETH_DECIMALS),
|
|
};
|
|
|
|
await expectLiquidityResult(mockWeb3Provider.object, mockOrderbook.object, orders, expectedResult);
|
|
});
|
|
|
|
it('should return correct computed value with multiple orders and fillable amounts', async () => {
|
|
const orders: SignedOrderWithFillableAmounts[] = [
|
|
{
|
|
...sellTenTokensFor10Weth,
|
|
...{
|
|
fillableMakerAssetAmount: baseUnitAmount(1),
|
|
fillableTakerAssetAmount: baseUnitAmount(0.5, WETH_DECIMALS),
|
|
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
|
|
},
|
|
},
|
|
{
|
|
...sellTenTokensFor10Weth,
|
|
...{
|
|
fillableMakerAssetAmount: baseUnitAmount(3),
|
|
fillableTakerAssetAmount: baseUnitAmount(3, WETH_DECIMALS),
|
|
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
|
|
},
|
|
},
|
|
];
|
|
|
|
const expectedResult = {
|
|
makerAssetAvailableInBaseUnits: baseUnitAmount(4),
|
|
takerAssetAvailableInBaseUnits: baseUnitAmount(3.5, WETH_DECIMALS),
|
|
};
|
|
|
|
await expectLiquidityResult(mockWeb3Provider.object, mockOrderbook.object, orders, expectedResult);
|
|
});
|
|
|
|
it('should return 0s when no amounts fillable', async () => {
|
|
const orders: SignedOrderWithFillableAmounts[] = [
|
|
{
|
|
...sellTenTokensFor10Weth,
|
|
...{
|
|
fillableMakerAssetAmount: constants.ZERO_AMOUNT,
|
|
fillableTakerAssetAmount: constants.ZERO_AMOUNT,
|
|
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
|
|
},
|
|
},
|
|
{
|
|
...sellTenTokensFor10Weth,
|
|
...{
|
|
fillableMakerAssetAmount: constants.ZERO_AMOUNT,
|
|
fillableTakerAssetAmount: constants.ZERO_AMOUNT,
|
|
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
|
|
},
|
|
},
|
|
];
|
|
|
|
const expectedResult = {
|
|
makerAssetAvailableInBaseUnits: constants.ZERO_AMOUNT,
|
|
takerAssetAvailableInBaseUnits: constants.ZERO_AMOUNT,
|
|
};
|
|
|
|
await expectLiquidityResult(mockWeb3Provider.object, mockOrderbook.object, orders, expectedResult);
|
|
});
|
|
});
|
|
});
|
|
});
|