protocol/packages/asset-swapper/test/order_prune_utils_test.ts
Xianny d73982819b
Deprecate abi-gen-wrappers (#2370)
* generate wrappers in @0x/contract-wrappers and delete abi-gen-wrappers

* trim exports from contract-wrappers

* separate contract-wrappers tests to get rid of dependency cycle

* remove dummy token contracts

* temporarily skip coordinator test until we can upgrade coordinator server
2019-11-27 17:50:24 -08:00

336 lines
15 KiB
TypeScript

import { ContractAddresses } from '@0x/contract-addresses';
import { DevUtilsContract, ERC20TokenContract, ExchangeContract } from '@0x/contract-wrappers';
import { constants as devConstants, getLatestBlockTimestampAsync, 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 { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import { constants } from '../src/constants';
import { OrderPrunerPermittedFeeTypes } from '../src/types';
import { OrderPruner } from '../src/utils/order_prune_utils';
import { chaiSetup } from './utils/chai_setup';
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 = devConstants.TESTRPC_CHAIN_ID;
const GAS_PRICE = new BigNumber(devConstants.DEFAULT_GAS_PRICE);
const PROTOCOL_FEE_MULTIPLIER = 150000;
const PROTOCOL_FEE_PER_FILL = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER);
const UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1); // tslint:disable-line:custom-no-magic-numbers
// tslint:disable: no-unused-expression
// tslint:disable: custom-no-magic-numbers
describe('OrderPruner', () => {
let erc20MakerTokenContract: ERC20TokenContract;
let erc20TakerTokenContract: ERC20TokenContract;
let exchangeContract: ExchangeContract;
let devUtilsContract: DevUtilsContract;
let userAddresses: string[];
let coinbaseAddress: string;
let makerAddress: string;
let takerAddress: string;
let feeRecipient: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let orderFactory: OrderFactory;
let wethAssetData: string;
let contractAddresses: ContractAddresses;
let orderPruner: OrderPruner;
let nonOpenSignedOrder: SignedOrder;
let expiredOpenSignedOrder: SignedOrder;
let invalidSignatureOpenSignedOrder: SignedOrder;
let fullyFillableOpenSignedOrder: SignedOrder;
let partiallyFilledOpenSignedOrderFeeless: SignedOrder;
let partiallyFilledOpenSignedOrderFeeInTakerAsset: SignedOrder;
let partiallyFilledOpenSignedOrderFeeInMakerAsset: SignedOrder;
let filledOpenSignedOrder: SignedOrder;
const chainId = TESTRPC_CHAIN_ID;
const fillableAmount = new BigNumber(10).multipliedBy(ONE_ETH_IN_WEI);
const partialFillAmount = new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI);
const takerFeeAmount = new BigNumber(2).multipliedBy(ONE_ETH_IN_WEI);
before(async () => {
contractAddresses = await migrateOnceAsync(provider);
await blockchainLifecycle.startAsync();
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
[coinbaseAddress, takerAddress, makerAddress, feeRecipient] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
erc20MakerTokenContract = new ERC20TokenContract(makerTokenAddress, provider);
erc20TakerTokenContract = new ERC20TokenContract(takerTokenAddress, provider);
[makerAssetData, takerAssetData, wethAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
assetDataUtils.encodeERC20AssetData(contractAddresses.etherToken),
];
exchangeContract = new ExchangeContract(contractAddresses.exchange, provider);
devUtilsContract = new DevUtilsContract(contractAddresses.devUtils, provider);
// Configure order defaults
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: feeRecipient,
exchangeAddress: contractAddresses.exchange,
chainId,
};
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
nonOpenSignedOrder = await orderFactory.newSignedOrderAsync({
takerAddress,
});
expiredOpenSignedOrder = await orderFactory.newSignedOrderAsync({
expirationTimeSeconds: new BigNumber(await getLatestBlockTimestampAsync()).minus(10),
});
invalidSignatureOpenSignedOrder = await orderFactory.newSignedOrderAsync({
takerAddress,
});
invalidSignatureOpenSignedOrder.signature = constants.NULL_BYTES;
fullyFillableOpenSignedOrder = await orderFactory.newSignedOrderAsync({
takerAssetAmount: fillableAmount,
makerAssetAmount: fillableAmount,
});
// give double fillableAmount to maker and taker as buffer
await erc20MakerTokenContract
.transfer(makerAddress, fillableAmount.multipliedBy(4))
.sendTransactionAsync({ from: coinbaseAddress });
await erc20TakerTokenContract
.transfer(takerAddress, fillableAmount.multipliedBy(4))
.sendTransactionAsync({ from: coinbaseAddress });
await erc20MakerTokenContract
.approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE_IN_BASE_UNITS)
.sendTransactionAsync({ from: makerAddress });
await erc20MakerTokenContract
.approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE_IN_BASE_UNITS)
.sendTransactionAsync({ from: takerAddress });
await erc20TakerTokenContract
.approve(contractAddresses.erc20Proxy, UNLIMITED_ALLOWANCE_IN_BASE_UNITS)
.sendTransactionAsync({ from: takerAddress });
partiallyFilledOpenSignedOrderFeeless = await orderFactory.newSignedOrderAsync({
takerAssetAmount: fillableAmount,
makerAssetAmount: fillableAmount,
});
await exchangeContract
.fillOrKillOrder(
partiallyFilledOpenSignedOrderFeeless,
partialFillAmount,
partiallyFilledOpenSignedOrderFeeless.signature,
)
.sendTransactionAsync({
from: takerAddress,
gasPrice: GAS_PRICE,
gas: 4000000,
value: PROTOCOL_FEE_PER_FILL,
});
partiallyFilledOpenSignedOrderFeeInTakerAsset = await orderFactory.newSignedOrderAsync({
takerAssetAmount: fillableAmount,
makerAssetAmount: fillableAmount,
takerFee: takerFeeAmount,
takerFeeAssetData: takerAssetData,
});
await exchangeContract
.fillOrKillOrder(
partiallyFilledOpenSignedOrderFeeInTakerAsset,
partialFillAmount,
partiallyFilledOpenSignedOrderFeeInTakerAsset.signature,
)
.sendTransactionAsync({
from: takerAddress,
gasPrice: GAS_PRICE,
gas: 4000000,
value: PROTOCOL_FEE_PER_FILL,
});
partiallyFilledOpenSignedOrderFeeInMakerAsset = await orderFactory.newSignedOrderAsync({
takerAssetAmount: fillableAmount,
makerAssetAmount: fillableAmount,
takerFee: takerFeeAmount,
takerFeeAssetData: makerAssetData,
});
await exchangeContract
.fillOrKillOrder(
partiallyFilledOpenSignedOrderFeeInMakerAsset,
partialFillAmount,
partiallyFilledOpenSignedOrderFeeInMakerAsset.signature,
)
.sendTransactionAsync({
from: takerAddress,
gasPrice: GAS_PRICE,
gas: 4000000,
value: PROTOCOL_FEE_PER_FILL,
});
filledOpenSignedOrder = await orderFactory.newSignedOrderAsync({
takerAssetAmount: fillableAmount,
makerAssetAmount: fillableAmount,
});
await exchangeContract
.fillOrKillOrder(filledOpenSignedOrder, fillableAmount, filledOpenSignedOrder.signature)
.sendTransactionAsync({
from: takerAddress,
gasPrice: GAS_PRICE,
gas: 4000000,
value: PROTOCOL_FEE_PER_FILL,
});
orderPruner = new OrderPruner(devUtilsContract, {
permittedOrderFeeTypes: new Set<OrderPrunerPermittedFeeTypes>([
OrderPrunerPermittedFeeTypes.NoFees,
OrderPrunerPermittedFeeTypes.MakerDenominatedTakerFee,
OrderPrunerPermittedFeeTypes.TakerDenominatedTakerFee,
]),
});
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('constructor options', () => {
it('should filter for only feeless orders if options permit only feeless orders', async () => {
orderPruner = new OrderPruner(devUtilsContract, {
permittedOrderFeeTypes: new Set<OrderPrunerPermittedFeeTypes>([OrderPrunerPermittedFeeTypes.NoFees]),
});
const orders = [
partiallyFilledOpenSignedOrderFeeInMakerAsset,
partiallyFilledOpenSignedOrderFeeInTakerAsset,
partiallyFilledOpenSignedOrderFeeless,
];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
// checks for one order in results and check for signature of orders
expect(resultPrunedOrders.length).to.be.equal(1);
expect(resultPrunedOrders[0].signature).to.be.deep.equal(partiallyFilledOpenSignedOrderFeeless.signature);
});
it('should filter for only takerFee in takerAsset orders if options permit only takerFee in takerAsset orders', async () => {
orderPruner = new OrderPruner(devUtilsContract, {
permittedOrderFeeTypes: new Set<OrderPrunerPermittedFeeTypes>([
OrderPrunerPermittedFeeTypes.TakerDenominatedTakerFee,
]),
});
const orders = [
partiallyFilledOpenSignedOrderFeeInMakerAsset,
partiallyFilledOpenSignedOrderFeeInTakerAsset,
partiallyFilledOpenSignedOrderFeeless,
];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
// checks for one order in results and check for signature of orders
expect(resultPrunedOrders.length).to.be.equal(1);
expect(resultPrunedOrders[0].signature).to.be.deep.equal(
partiallyFilledOpenSignedOrderFeeInTakerAsset.signature,
);
});
it('should filter for only makerFee in takerAsset orders if options permit only makerFee orders', async () => {
orderPruner = new OrderPruner(devUtilsContract, {
permittedOrderFeeTypes: new Set<OrderPrunerPermittedFeeTypes>([
OrderPrunerPermittedFeeTypes.MakerDenominatedTakerFee,
]),
});
const orders = [
partiallyFilledOpenSignedOrderFeeInMakerAsset,
partiallyFilledOpenSignedOrderFeeInTakerAsset,
partiallyFilledOpenSignedOrderFeeless,
];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
// checks for one order in results and check for signature of orders
expect(resultPrunedOrders.length).to.be.equal(1);
expect(resultPrunedOrders[0].signature).to.be.deep.equal(
partiallyFilledOpenSignedOrderFeeInMakerAsset.signature,
);
});
});
describe('#pruneSignedOrdersAsync', () => {
it('should filter out non open orders', async () => {
const orders = [nonOpenSignedOrder];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
expect(resultPrunedOrders).to.be.empty;
});
it('should filter out expired orders', async () => {
const orders = [expiredOpenSignedOrder];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
expect(resultPrunedOrders).to.be.empty;
});
it('should filter out invalid signature orders', async () => {
const orders = [invalidSignatureOpenSignedOrder];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
expect(resultPrunedOrders).to.be.empty;
});
it('should filter out fully filled orders', async () => {
const orders = [filledOpenSignedOrder];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
expect(resultPrunedOrders).to.be.empty;
});
it('should provide correct pruned signed orders for fully fillable orders', async () => {
const orders = [fullyFillableOpenSignedOrder];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
const prunedOrder = resultPrunedOrders[0];
expect(prunedOrder.fillableMakerAssetAmount).to.bignumber.equal(fillableAmount);
expect(prunedOrder.fillableTakerAssetAmount).to.bignumber.equal(fillableAmount);
});
it('should provide correct pruned signed orders for partially fillable orders', async () => {
const orders = [
partiallyFilledOpenSignedOrderFeeless,
partiallyFilledOpenSignedOrderFeeInTakerAsset,
partiallyFilledOpenSignedOrderFeeInMakerAsset,
];
const resultPrunedOrders = await orderPruner.pruneSignedOrdersAsync(orders);
expect(resultPrunedOrders[0].fillableMakerAssetAmount).to.bignumber.equal(
fillableAmount.minus(partialFillAmount),
);
expect(resultPrunedOrders[0].fillableTakerAssetAmount).to.bignumber.equal(
fillableAmount.minus(partialFillAmount),
);
expect(resultPrunedOrders[1].fillableMakerAssetAmount).to.bignumber.equal(
fillableAmount.minus(partialFillAmount),
);
expect(resultPrunedOrders[1].fillableTakerAssetAmount).to.bignumber.equal(
fillableAmount.minus(partialFillAmount),
);
expect(resultPrunedOrders[1].fillableTakerFeeAmount).to.bignumber.equal(
new BigNumber(1.6).multipliedBy(ONE_ETH_IN_WEI),
);
expect(resultPrunedOrders[2].fillableMakerAssetAmount).to.bignumber.equal(
fillableAmount.minus(partialFillAmount),
);
expect(resultPrunedOrders[2].fillableTakerAssetAmount).to.bignumber.equal(
fillableAmount.minus(partialFillAmount),
);
expect(resultPrunedOrders[2].fillableTakerFeeAmount).to.bignumber.equal(
new BigNumber(1.6).multipliedBy(ONE_ETH_IN_WEI),
);
});
});
});