protocol/packages/asset-swapper/test/swap_quoter_test.ts

307 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, PrunedSignedOrder } from '../src/types';
import { chaiSetup } from './utils/chai_setup';
import { mockAvailableAssetDatas, mockedSwapQuoterWithPrunedSignedOrders, 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,
prunedOrders: PrunedSignedOrder[],
expectedLiquidityResult: LiquidityForTakerMakerAssetDataPair,
) => {
const mockedSwapQuoter = mockedSwapQuoterWithPrunedSignedOrders(
web3Provider,
orderbook,
FAKE_MAKER_ASSET_DATA,
WETH_ASSET_DATA,
prunedOrders,
);
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 prunedOrders: PrunedSignedOrder[] = [];
const expectedResult = {
makerAssetAvailableInBaseUnits: new BigNumber(0),
takerAssetAvailableInBaseUnits: new BigNumber(0),
};
await expectLiquidityResult(
mockWeb3Provider.object,
mockOrderbook.object,
prunedOrders,
expectedResult,
);
});
it('should return correct computed value when orders provided with full fillableAmounts', async () => {
const prunedOrders: PrunedSignedOrder[] = [
{
...sellTenTokensFor10Weth,
...{
fillableMakerAssetAmount: sellTenTokensFor10Weth.makerAssetAmount,
fillableTakerAssetAmount: sellTenTokensFor10Weth.takerAssetAmount,
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
},
},
{
...sellTenTokensFor10Weth,
...{
fillableMakerAssetAmount: sellTenTokensFor10Weth.makerAssetAmount,
fillableTakerAssetAmount: sellTenTokensFor10Weth.takerAssetAmount,
fillableTakerFeeAmount: constants.ZERO_AMOUNT,
},
},
];
const expectedMakerAssetAvailable = prunedOrders[0].makerAssetAmount.plus(
prunedOrders[1].makerAssetAmount,
);
const expectedTakerAssetAvailable = prunedOrders[0].takerAssetAmount.plus(
prunedOrders[1].takerAssetAmount,
);
const expectedResult = {
makerAssetAvailableInBaseUnits: expectedMakerAssetAvailable,
takerAssetAvailableInBaseUnits: expectedTakerAssetAvailable,
};
await expectLiquidityResult(
mockWeb3Provider.object,
mockOrderbook.object,
prunedOrders,
expectedResult,
);
});
it('should return correct computed value with one partial fillableAmounts', async () => {
const prunedOrders: PrunedSignedOrder[] = [
{
...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,
prunedOrders,
expectedResult,
);
});
it('should return correct computed value with multiple orders and fillable amounts', async () => {
const prunedOrders: PrunedSignedOrder[] = [
{
...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,
prunedOrders,
expectedResult,
);
});
it('should return 0s when no amounts fillable', async () => {
const prunedOrders: PrunedSignedOrder[] = [
{
...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,
prunedOrders,
expectedResult,
);
});
});
});
});