protocol/packages/asset-swapper/test/swap_quote_consumer_utils_test.ts
Lawrence Forman 7f56091fbd
@0x/asset-swapper: Add exponential sampling, exposed by sampleDistributionBase.
`@0x/asset-swapper`: Disable ethgasstation polling in tests.
`@0x/asset-swapper`: Tweak default hyper parameters to be friendlier to big fills.
2020-01-20 13:30:57 +10:00

241 lines
10 KiB
TypeScript

import { ContractAddresses } from '@0x/contract-addresses';
import { WETH9Contract } from '@0x/contract-wrappers';
import { constants as devConstants, OrderFactory } from '@0x/contracts-test-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { migrateOnceAsync } from '@0x/migrations';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import { SwapQuote, SwapQuoteConsumer } from '../src';
import { constants } from '../src/constants';
import { ExtensionContractType, MarketOperation, SignedOrderWithFillableAmounts } from '../src/types';
import { ProtocolFeeUtils } from '../src/utils/protocol_fee_utils';
import { chaiSetup } from './utils/chai_setup';
import { getFullyFillableSwapQuoteWithNoFeesAsync } from './utils/swap_quote';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
const ONE_ETH_IN_WEI = new BigNumber(1000000000000000000);
const TESTRPC_CHAIN_ID = 1337;
const GAS_PRICE = new BigNumber(devConstants.DEFAULT_GAS_PRICE);
const PARTIAL_PRUNED_SIGNED_ORDERS: Array<Partial<SignedOrderWithFillableAmounts>> = [
{
takerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI),
},
{
takerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(3).multipliedBy(ONE_ETH_IN_WEI),
},
{
takerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(5).multipliedBy(ONE_ETH_IN_WEI),
},
];
const PARTIAL_LARGE_PRUNED_SIGNED_ORDERS: Array<Partial<SignedOrderWithFillableAmounts>> = [
{
takerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
},
{
takerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
},
{
takerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
makerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
fillableTakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
fillableMakerAssetAmount: new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI),
},
];
describe('swapQuoteConsumerUtils', () => {
let wethContract: WETH9Contract;
let protocolFeeUtils: ProtocolFeeUtils;
let userAddresses: string[];
let makerAddress: string;
let takerAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let wethAssetData: string;
let contractAddresses: ContractAddresses;
let swapQuoteConsumer: SwapQuoteConsumer;
let orderFactory: OrderFactory;
let forwarderOrderFactory: OrderFactory;
const chainId = TESTRPC_CHAIN_ID;
before(async () => {
contractAddresses = await migrateOnceAsync(provider);
await blockchainLifecycle.startAsync();
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
wethContract = new WETH9Contract(contractAddresses.etherToken, provider);
[takerAddress, makerAddress] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
[makerAssetData, takerAssetData, wethAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
assetDataUtils.encodeERC20AssetData(contractAddresses.etherToken),
];
const defaultOrderParams = {
...devConstants.STATIC_ORDER_PARAMS,
makerAddress,
takerAddress: constants.NULL_ADDRESS,
makerAssetData,
takerAssetData,
makerFeeAssetData: constants.NULL_ERC20_ASSET_DATA,
takerFeeAssetData: constants.NULL_ERC20_ASSET_DATA,
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
feeRecipientAddress: constants.NULL_ADDRESS,
exchangeAddress: contractAddresses.exchange,
chainId,
};
const defaultForwarderOrderParams = {
...defaultOrderParams,
...{
takerAssetData: wethAssetData,
},
};
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1));
forwarderOrderFactory = new OrderFactory(privateKey, defaultForwarderOrderParams);
swapQuoteConsumer = new SwapQuoteConsumer(provider, {
chainId,
});
});
after(async () => {
await blockchainLifecycle.revertAsync();
await protocolFeeUtils.destroyAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('getConsumerTypeForSwapQuoteAsync', () => {
let forwarderOrders: SignedOrderWithFillableAmounts[];
let exchangeOrders: SignedOrderWithFillableAmounts[];
let largeForwarderOrders: SignedOrderWithFillableAmounts[];
let forwarderSwapQuote: SwapQuote;
let exchangeSwapQuote: SwapQuote;
let largeForwarderSwapQuote: SwapQuote;
beforeEach(async () => {
exchangeOrders = [];
for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS) {
const order = await orderFactory.newSignedOrderAsync(partialOrder);
const prunedOrder = {
...order,
...partialOrder,
};
exchangeOrders.push(prunedOrder as SignedOrderWithFillableAmounts);
}
forwarderOrders = [];
for (const partialOrder of PARTIAL_PRUNED_SIGNED_ORDERS) {
const order = await forwarderOrderFactory.newSignedOrderAsync(partialOrder);
const prunedOrder = {
...order,
...partialOrder,
};
forwarderOrders.push(prunedOrder as SignedOrderWithFillableAmounts);
}
largeForwarderOrders = [];
for (const partialOrder of PARTIAL_LARGE_PRUNED_SIGNED_ORDERS) {
const order = await forwarderOrderFactory.newSignedOrderAsync(partialOrder);
const prunedOrder = {
...order,
...partialOrder,
};
largeForwarderOrders.push(prunedOrder as SignedOrderWithFillableAmounts);
}
forwarderSwapQuote = await getFullyFillableSwapQuoteWithNoFeesAsync(
makerAssetData,
wethAssetData,
forwarderOrders,
MarketOperation.Sell,
GAS_PRICE,
protocolFeeUtils,
);
largeForwarderSwapQuote = await getFullyFillableSwapQuoteWithNoFeesAsync(
makerAssetData,
wethAssetData,
largeForwarderOrders,
MarketOperation.Sell,
GAS_PRICE,
protocolFeeUtils,
);
exchangeSwapQuote = await getFullyFillableSwapQuoteWithNoFeesAsync(
makerAssetData,
takerAssetData,
exchangeOrders,
MarketOperation.Sell,
GAS_PRICE,
protocolFeeUtils,
);
});
it('should return exchange consumer if takerAsset is not wEth', async () => {
const extensionContractType = await swapQuoteConsumer.getOptimalExtensionContractTypeAsync(
exchangeSwapQuote,
{ takerAddress },
);
expect(extensionContractType).to.equal(ExtensionContractType.None);
});
it('should return forwarder consumer if takerAsset is wEth and have enough eth balance', async () => {
const extensionContractType = await swapQuoteConsumer.getOptimalExtensionContractTypeAsync(
forwarderSwapQuote,
{ takerAddress },
);
expect(extensionContractType).to.equal(ExtensionContractType.Forwarder);
});
it('should return exchange consumer if takerAsset is wEth and taker has enough weth', async () => {
const etherInWei = new BigNumber(20).multipliedBy(ONE_ETH_IN_WEI);
await wethContract.deposit().sendTransactionAsync({ value: etherInWei, from: takerAddress });
const extensionContractType = await swapQuoteConsumer.getOptimalExtensionContractTypeAsync(
forwarderSwapQuote,
{ takerAddress },
);
expect(extensionContractType).to.equal(ExtensionContractType.None);
});
it('should return forwarder consumer if takerAsset is wEth and takerAddress has no available balance in either weth or eth (defaulting behavior)', async () => {
const etherInWei = new BigNumber(50).multipliedBy(ONE_ETH_IN_WEI);
await wethContract.deposit().sendTransactionAsync({ value: etherInWei, from: takerAddress });
const extensionContractType = await swapQuoteConsumer.getOptimalExtensionContractTypeAsync(
largeForwarderSwapQuote,
{ takerAddress },
);
expect(extensionContractType).to.equal(ExtensionContractType.Forwarder);
});
});
});