@0x/contracts-exchange
: Add a buttload of tests to
`isolated_fill_order.ts`.
This commit is contained in:
@@ -4,33 +4,39 @@ import {
|
||||
expect,
|
||||
hexRandom,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { FillResults } from '@0x/types';
|
||||
import { ExchangeRevertErrors, LibMathRevertErrors } from '@0x/order-utils';
|
||||
import { FillResults, OrderInfo, OrderStatus, SignatureType } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import {
|
||||
AssetBalances,
|
||||
createBadAssetData,
|
||||
createBadSignature,
|
||||
createGoodAssetData,
|
||||
createGoodSignature,
|
||||
IsolatedExchangeWrapper,
|
||||
Orderish,
|
||||
Order,
|
||||
} from './utils/isolated_exchange_wrapper';
|
||||
import { calculateFillResults } from './utils/reference_functions';
|
||||
|
||||
blockchainTests.resets.only('Isolated fillOrder() tests', env => {
|
||||
const { ZERO_AMOUNT } = constants;
|
||||
const TOMORROW = Math.floor(_.now() / 1000) + 60 * 60 * 24;
|
||||
const ONE_ETHER = new BigNumber(10).pow(18);
|
||||
blockchainTests.only('Isolated fillOrder() tests', env => {
|
||||
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
||||
const DEFAULT_ORDER: Orderish = {
|
||||
const getCurrentTime = () => Math.floor(_.now() / 1000);
|
||||
const { ZERO_AMOUNT } = constants;
|
||||
const ONE_ETHER = new BigNumber(10).pow(18);
|
||||
const ONE_DAY = 60 * 60 * 24;
|
||||
const TOMORROW = getCurrentTime() + ONE_DAY;
|
||||
const DEFAULT_ORDER: Order = {
|
||||
senderAddress: constants.NULL_ADDRESS,
|
||||
makerAddress: randomAddress(),
|
||||
takerAddress: constants.NULL_ADDRESS,
|
||||
makerFee: ZERO_AMOUNT,
|
||||
takerFee: ZERO_AMOUNT,
|
||||
feeRecipientAddress: randomAddress(),
|
||||
makerAssetAmount: ONE_ETHER,
|
||||
takerAssetAmount: ONE_ETHER.times(2),
|
||||
makerFee: ONE_ETHER.times(0.015),
|
||||
takerFee: ONE_ETHER.times(0.025),
|
||||
salt: ZERO_AMOUNT,
|
||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||
expirationTimeSeconds: new BigNumber(TOMORROW),
|
||||
makerAssetData: createGoodAssetData(),
|
||||
takerAssetData: createGoodAssetData(),
|
||||
@@ -38,28 +44,37 @@ blockchainTests.resets.only('Isolated fillOrder() tests', env => {
|
||||
takerFeeAssetData: createGoodAssetData(),
|
||||
};
|
||||
let takerAddress: string;
|
||||
let notTakerAddress: string;
|
||||
let exchange: IsolatedExchangeWrapper;
|
||||
let nextSaltValue = 1;
|
||||
|
||||
before(async () => {
|
||||
[ takerAddress ] = await env.getAccountAddressesAsync();
|
||||
[ takerAddress, notTakerAddress ] = await env.getAccountAddressesAsync();
|
||||
exchange = await IsolatedExchangeWrapper.deployAsync(
|
||||
env.web3Wrapper,
|
||||
_.assign(env.txDefaults, { from: takerAddress }),
|
||||
);
|
||||
});
|
||||
|
||||
function createOrder(details: Partial<Orderish> = {}): Orderish {
|
||||
function createOrder(details: Partial<Order> = {}): Order {
|
||||
return _.assign({}, DEFAULT_ORDER, { salt: new BigNumber(nextSaltValue++) }, details);
|
||||
}
|
||||
|
||||
interface FillOrderAndAssertResultsResults {
|
||||
fillResults: FillResults;
|
||||
orderInfo: OrderInfo;
|
||||
}
|
||||
|
||||
async function fillOrderAndAssertResultsAsync(
|
||||
order: Orderish,
|
||||
order: Order,
|
||||
takerAssetFillAmount: BigNumber,
|
||||
): Promise<FillResults> {
|
||||
const efr = await calculateExpectedFillResultsAsync(order, takerAssetFillAmount);
|
||||
): Promise<FillOrderAndAssertResultsResults> {
|
||||
const orderInfo = await exchange.getOrderInfoAsync(order);
|
||||
const efr = calculateExpectedFillResults(order, orderInfo, takerAssetFillAmount);
|
||||
const eoi = calculateExpectedOrderInfo(order, orderInfo, efr);
|
||||
const efb = calculateExpectedFillBalances(order, efr);
|
||||
const fillResults = await exchange.fillOrderAsync(order, takerAssetFillAmount);
|
||||
const newOrderInfo = await exchange.getOrderInfoAsync(order);
|
||||
// Check returned fillResults.
|
||||
expect(fillResults.makerAssetFilledAmount)
|
||||
.to.bignumber.eq(efr.makerAssetFilledAmount);
|
||||
@@ -72,27 +87,59 @@ blockchainTests.resets.only('Isolated fillOrder() tests', env => {
|
||||
// Check balances.
|
||||
for (const assetData of Object.keys(efb)) {
|
||||
for (const address of Object.keys(efb[assetData])) {
|
||||
expect(exchange.getBalanceChange(assetData, address))
|
||||
.to.bignumber.eq(efb[assetData][address], `assetData: ${assetData}, address: ${address}`);
|
||||
expect(
|
||||
exchange.getBalanceChange(assetData, address),
|
||||
`checking balance of assetData: ${assetData}, address: ${address}`,
|
||||
).to.bignumber.eq(efb[assetData][address]);
|
||||
}
|
||||
}
|
||||
return fillResults;
|
||||
// Check order info.
|
||||
expect(newOrderInfo.orderStatus).to.eq(eoi.orderStatus);
|
||||
expect(newOrderInfo.orderTakerAssetFilledAmount)
|
||||
.to.bignumber.eq(eoi.orderTakerAssetFilledAmount);
|
||||
// Check that there wasn't an overfill.
|
||||
expect(
|
||||
newOrderInfo.orderTakerAssetFilledAmount.lte(order.takerAssetAmount),
|
||||
'checking for overfill',
|
||||
).to.be.ok('');
|
||||
return {
|
||||
fillResults,
|
||||
orderInfo: newOrderInfo,
|
||||
};
|
||||
}
|
||||
|
||||
async function calculateExpectedFillResultsAsync(
|
||||
order: Orderish,
|
||||
function calculateExpectedFillResults(
|
||||
order: Order,
|
||||
orderInfo: OrderInfo,
|
||||
takerAssetFillAmount: BigNumber,
|
||||
): Promise<FillResults> {
|
||||
const takerAssetFilledAmount = await exchange.getTakerAssetFilledAmountAsync(order);
|
||||
const remainingTakerAssetAmount = order.takerAssetAmount.minus(takerAssetFilledAmount);
|
||||
): FillResults {
|
||||
const remainingTakerAssetAmount = order.takerAssetAmount.minus(
|
||||
orderInfo.orderTakerAssetFilledAmount,
|
||||
);
|
||||
return calculateFillResults(
|
||||
order,
|
||||
BigNumber.min(takerAssetFillAmount, remainingTakerAssetAmount),
|
||||
);
|
||||
}
|
||||
|
||||
function calculateExpectedOrderInfo(
|
||||
order: Order,
|
||||
orderInfo: OrderInfo,
|
||||
fillResults: FillResults,
|
||||
): OrderInfo {
|
||||
const orderTakerAssetFilledAmount =
|
||||
orderInfo.orderTakerAssetFilledAmount.plus(fillResults.takerAssetFilledAmount);
|
||||
const orderStatus = orderTakerAssetFilledAmount.gte(order.takerAssetAmount) ?
|
||||
OrderStatus.FullyFilled : OrderStatus.Fillable;
|
||||
return {
|
||||
orderHash: exchange.getOrderHash(order),
|
||||
orderStatus,
|
||||
orderTakerAssetFilledAmount,
|
||||
};
|
||||
}
|
||||
|
||||
function calculateExpectedFillBalances(
|
||||
order: Orderish,
|
||||
order: Order,
|
||||
fillResults: FillResults,
|
||||
): AssetBalances {
|
||||
const balances: AssetBalances = {};
|
||||
@@ -112,23 +159,49 @@ blockchainTests.resets.only('Isolated fillOrder() tests', env => {
|
||||
return balances;
|
||||
}
|
||||
|
||||
function splitAmount(total: BigNumber, n: number = 2): BigNumber[] {
|
||||
const splitSize = total.dividedToIntegerBy(n);
|
||||
const splits = _.times(n - 1, () => splitSize);
|
||||
splits.push(total.minus(splitSize.times(n - 1)));
|
||||
return splits;
|
||||
}
|
||||
|
||||
describe('full fills', () => {
|
||||
it('can fully fill an order', async () => {
|
||||
const order = createOrder();
|
||||
return fillOrderAndAssertResultsAsync(order, order.takerAssetAmount);
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, order.takerAssetAmount);
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
|
||||
it('can\'t overfill an order', async () => {
|
||||
const order = createOrder();
|
||||
return fillOrderAndAssertResultsAsync(order, order.takerAssetAmount.times(1.01));
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, order.takerAssetAmount.times(1.01));
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
|
||||
it('pays maker and taker fees', async () => {
|
||||
it('no fees', async () => {
|
||||
const order = createOrder({
|
||||
makerFee: ONE_ETHER.times(0.025),
|
||||
takerFee: ONE_ETHER.times(0.035),
|
||||
makerFee: ZERO_AMOUNT,
|
||||
takerFee: ZERO_AMOUNT,
|
||||
});
|
||||
return fillOrderAndAssertResultsAsync(order, order.takerAssetAmount);
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, order.takerAssetAmount);
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
|
||||
it('only maker fees', async () => {
|
||||
const order = createOrder({
|
||||
takerFee: ZERO_AMOUNT,
|
||||
});
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, order.takerAssetAmount);
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
|
||||
it('only taker fees', async () => {
|
||||
const order = createOrder({
|
||||
makerFee: ZERO_AMOUNT,
|
||||
});
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, order.takerAssetAmount);
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -137,15 +210,344 @@ blockchainTests.resets.only('Isolated fillOrder() tests', env => {
|
||||
|
||||
it('can partially fill an order', async () => {
|
||||
const order = createOrder();
|
||||
return fillOrderAndAssertResultsAsync(order, takerAssetFillAmount);
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, takerAssetFillAmount);
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.Fillable);
|
||||
});
|
||||
|
||||
it('pays maker and taker fees', async () => {
|
||||
it('no fees', async () => {
|
||||
const order = createOrder({
|
||||
makerFee: ONE_ETHER.times(0.025),
|
||||
takerFee: ONE_ETHER.times(0.035),
|
||||
makerFee: ZERO_AMOUNT,
|
||||
takerFee: ZERO_AMOUNT,
|
||||
});
|
||||
return fillOrderAndAssertResultsAsync(order, takerAssetFillAmount);
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, takerAssetFillAmount);
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.Fillable);
|
||||
});
|
||||
|
||||
it('only maker fees', async () => {
|
||||
const order = createOrder({
|
||||
takerFee: ZERO_AMOUNT,
|
||||
});
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, takerAssetFillAmount);
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.Fillable);
|
||||
});
|
||||
|
||||
it('only taker fees', async () => {
|
||||
const order = createOrder({
|
||||
makerFee: ZERO_AMOUNT,
|
||||
});
|
||||
const { orderInfo } = await fillOrderAndAssertResultsAsync(order, takerAssetFillAmount);
|
||||
expect(orderInfo.orderStatus).to.eq(OrderStatus.Fillable);
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiple fills', () => {
|
||||
it('can fully fill an order in two fills', async () => {
|
||||
const order = createOrder();
|
||||
const fillAmounts = splitAmount(order.takerAssetAmount);
|
||||
const orderInfos = [
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[0])).orderInfo,
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[1])).orderInfo,
|
||||
];
|
||||
expect(orderInfos[0].orderStatus).to.eq(OrderStatus.Fillable);
|
||||
expect(orderInfos[1].orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
|
||||
it('can partially fill an order in two fills', async () => {
|
||||
const order = createOrder();
|
||||
const fillAmounts = splitAmount(order.takerAssetAmount.dividedToIntegerBy(2));
|
||||
const orderInfos = [
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[0])).orderInfo,
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[1])).orderInfo,
|
||||
];
|
||||
expect(orderInfos[0].orderStatus).to.eq(OrderStatus.Fillable);
|
||||
expect(orderInfos[1].orderStatus).to.eq(OrderStatus.Fillable);
|
||||
});
|
||||
|
||||
it('can\'t overfill an order in two fills', async () => {
|
||||
const order = createOrder();
|
||||
const fillAmounts = splitAmount(order.takerAssetAmount);
|
||||
fillAmounts[0] = fillAmounts[0].times(1.01);
|
||||
const orderInfos = [
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[0])).orderInfo,
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[1])).orderInfo,
|
||||
];
|
||||
expect(orderInfos[0].orderStatus).to.eq(OrderStatus.Fillable);
|
||||
expect(orderInfos[1].orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
|
||||
it('can fully fill an order with no fees in two fills', async () => {
|
||||
const order = createOrder({
|
||||
makerFee: ZERO_AMOUNT,
|
||||
takerFee: ZERO_AMOUNT,
|
||||
});
|
||||
const fillAmounts = splitAmount(order.takerAssetAmount);
|
||||
const orderInfos = [
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[0])).orderInfo,
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[1])).orderInfo,
|
||||
];
|
||||
expect(orderInfos[0].orderStatus).to.eq(OrderStatus.Fillable);
|
||||
expect(orderInfos[1].orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
|
||||
it('can fully fill an order with only maker fees in two fills', async () => {
|
||||
const order = createOrder({
|
||||
takerFee: ZERO_AMOUNT,
|
||||
});
|
||||
const fillAmounts = splitAmount(order.takerAssetAmount);
|
||||
const orderInfos = [
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[0])).orderInfo,
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[1])).orderInfo,
|
||||
];
|
||||
expect(orderInfos[0].orderStatus).to.eq(OrderStatus.Fillable);
|
||||
expect(orderInfos[1].orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
|
||||
it('can fully fill an order with only taker fees in two fills', async () => {
|
||||
const order = createOrder({
|
||||
makerFee: ZERO_AMOUNT,
|
||||
});
|
||||
const fillAmounts = splitAmount(order.takerAssetAmount);
|
||||
const orderInfos = [
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[0])).orderInfo,
|
||||
(await fillOrderAndAssertResultsAsync(order, fillAmounts[1])).orderInfo,
|
||||
];
|
||||
expect(orderInfos[0].orderStatus).to.eq(OrderStatus.Fillable);
|
||||
expect(orderInfos[1].orderStatus).to.eq(OrderStatus.FullyFilled);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bad fills', () => {
|
||||
it('can\'t fill an order with zero takerAssetAmount', async () => {
|
||||
const order = createOrder({
|
||||
takerAssetAmount: ZERO_AMOUNT,
|
||||
});
|
||||
const expectedError = new ExchangeRevertErrors.OrderStatusError(
|
||||
exchange.getOrderHash(order),
|
||||
OrderStatus.InvalidTakerAssetAmount,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, ONE_ETHER))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order with zero makerAssetAmount', async () => {
|
||||
const order = createOrder({
|
||||
makerAssetAmount: ZERO_AMOUNT,
|
||||
});
|
||||
const expectedError = new ExchangeRevertErrors.OrderStatusError(
|
||||
exchange.getOrderHash(order),
|
||||
OrderStatus.InvalidMakerAssetAmount,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, ONE_ETHER))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order that is fully filled', async () => {
|
||||
const order = createOrder();
|
||||
const expectedError = new ExchangeRevertErrors.OrderStatusError(
|
||||
exchange.getOrderHash(order),
|
||||
OrderStatus.FullyFilled,
|
||||
);
|
||||
await exchange.fillOrderAsync(order, order.takerAssetAmount);
|
||||
return expect(exchange.fillOrderAsync(order, 1))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order that is expired', async () => {
|
||||
const order = createOrder({
|
||||
expirationTimeSeconds: new BigNumber(getCurrentTime() - 60),
|
||||
});
|
||||
const expectedError = new ExchangeRevertErrors.OrderStatusError(
|
||||
exchange.getOrderHash(order),
|
||||
OrderStatus.Expired,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order that is cancelled by `cancelOrder()`', async () => {
|
||||
const order = createOrder({
|
||||
makerAddress: notTakerAddress,
|
||||
});
|
||||
const expectedError = new ExchangeRevertErrors.OrderStatusError(
|
||||
exchange.getOrderHash(order),
|
||||
OrderStatus.Cancelled,
|
||||
);
|
||||
await exchange.cancelOrderAsync(order, { from: notTakerAddress });
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order that is cancelled by `cancelOrdersUpTo()`', async () => {
|
||||
const order = createOrder({
|
||||
makerAddress: notTakerAddress,
|
||||
});
|
||||
const expectedError = new ExchangeRevertErrors.OrderStatusError(
|
||||
exchange.getOrderHash(order),
|
||||
OrderStatus.Cancelled,
|
||||
);
|
||||
await exchange.cancelOrdersUpToAsync(order.salt, { from: notTakerAddress });
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order if taker is not `takerAddress`', async () => {
|
||||
const order = createOrder({
|
||||
takerAddress: randomAddress(),
|
||||
});
|
||||
const expectedError = new ExchangeRevertErrors.InvalidTakerError(
|
||||
exchange.getOrderHash(order),
|
||||
takerAddress,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order if sender is not `senderAddress`', async () => {
|
||||
const order = createOrder({
|
||||
senderAddress: randomAddress(),
|
||||
});
|
||||
const expectedError = new ExchangeRevertErrors.InvalidSenderError(
|
||||
exchange.getOrderHash(order),
|
||||
takerAddress,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order with a taker amount that results in a maker asset rounding error', async () => {
|
||||
const order = createOrder({
|
||||
makerAssetAmount: new BigNumber(100),
|
||||
takerAssetAmount: ONE_ETHER,
|
||||
});
|
||||
const takerAssetFillAmount = order.takerAssetAmount.dividedToIntegerBy(3);
|
||||
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||
takerAssetFillAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, takerAssetFillAmount))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order with a taker amount that results in a maker fee rounding error', async () => {
|
||||
const order = createOrder({
|
||||
makerAssetAmount: ONE_ETHER.times(2),
|
||||
takerAssetAmount: ONE_ETHER,
|
||||
makerFee: new BigNumber(100),
|
||||
});
|
||||
const takerAssetFillAmount = order.takerAssetAmount.dividedToIntegerBy(3);
|
||||
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||
takerAssetFillAmount.times(2),
|
||||
order.makerAssetAmount,
|
||||
order.makerFee,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, takerAssetFillAmount))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order with a taker amount that results in a taker fee rounding error', async () => {
|
||||
const order = createOrder({
|
||||
makerAssetAmount: ONE_ETHER.times(2),
|
||||
takerAssetAmount: ONE_ETHER,
|
||||
takerFee: new BigNumber(100),
|
||||
});
|
||||
const takerAssetFillAmount = order.takerAssetAmount.dividedToIntegerBy(3);
|
||||
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||
takerAssetFillAmount,
|
||||
order.takerAssetAmount,
|
||||
order.takerFee,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, takerAssetFillAmount))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t fill an order with a bad signature', async () => {
|
||||
const order = createOrder();
|
||||
const signature = createBadSignature();
|
||||
const expectedError = new ExchangeRevertErrors.SignatureError(
|
||||
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
|
||||
exchange.getOrderHash(order),
|
||||
order.makerAddress,
|
||||
signature,
|
||||
);
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount, signature))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('can\'t complementary fill an order with a bad signature that is always checked', async () => {
|
||||
const order = createOrder();
|
||||
const takerAssetFillAmounts = splitAmount(order.takerAssetAmount);
|
||||
const goodSignature = createGoodSignature(SignatureType.Wallet);
|
||||
const badSignature = createBadSignature(SignatureType.Wallet);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureError(
|
||||
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
|
||||
exchange.getOrderHash(order),
|
||||
order.makerAddress,
|
||||
badSignature,
|
||||
);
|
||||
await exchange.fillOrderAsync(order, takerAssetFillAmounts[0], goodSignature);
|
||||
return expect(exchange.fillOrderAsync(order, takerAssetFillAmounts[1], badSignature))
|
||||
.to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
const TRANSFER_ERROR = 'TRANSFER_FAILED';
|
||||
|
||||
it('can\'t fill an order with a maker asset that fails to transfer', async () => {
|
||||
const order = createOrder({
|
||||
makerAssetData: createBadAssetData(),
|
||||
});
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(TRANSFER_ERROR);
|
||||
});
|
||||
|
||||
it('can\'t fill an order with a taker asset that fails to transfer', async () => {
|
||||
const order = createOrder({
|
||||
takerAssetData: createBadAssetData(),
|
||||
});
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(TRANSFER_ERROR);
|
||||
});
|
||||
|
||||
it('can\'t fill an order with a maker fee asset that fails to transfer', async () => {
|
||||
const order = createOrder({
|
||||
makerFeeAssetData: createBadAssetData(),
|
||||
});
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(TRANSFER_ERROR);
|
||||
});
|
||||
|
||||
it('can\'t fill an order with a taker fee asset that fails to transfer', async () => {
|
||||
const order = createOrder({
|
||||
takerFeeAssetData: createBadAssetData(),
|
||||
});
|
||||
return expect(exchange.fillOrderAsync(order, order.takerAssetAmount))
|
||||
.to.revertWith(TRANSFER_ERROR);
|
||||
});
|
||||
});
|
||||
|
||||
describe('permitted fills', () => {
|
||||
it('can fill an order if taker is `takerAddress`', async () => {
|
||||
const order = createOrder({
|
||||
takerAddress,
|
||||
});
|
||||
return fillOrderAndAssertResultsAsync(order, order.takerAssetAmount);
|
||||
});
|
||||
|
||||
it('can fill an order if sender is `senderAddress`', async () => {
|
||||
const order = createOrder({
|
||||
senderAddress: takerAddress,
|
||||
});
|
||||
return fillOrderAndAssertResultsAsync(order, order.takerAssetAmount);
|
||||
});
|
||||
|
||||
it('can complementary fill an order with a bad signature that is checked only once', async () => {
|
||||
const order = createOrder();
|
||||
const takerAssetFillAmounts = splitAmount(order.takerAssetAmount);
|
||||
const goodSignature = createGoodSignature(SignatureType.EthSign);
|
||||
const badSignature = createBadSignature(SignatureType.EthSign);
|
||||
await exchange.fillOrderAsync(order, takerAssetFillAmounts[0], goodSignature);
|
||||
await exchange.fillOrderAsync(order, takerAssetFillAmounts[1], badSignature);
|
||||
});
|
||||
});
|
||||
});
|
||||
// tslint:disable: max-file-line-count
|
||||
|
@@ -4,7 +4,6 @@ import { artifacts as erc721Artifacts } from '@0x/contracts-erc721';
|
||||
import {
|
||||
BatchMatchOrder,
|
||||
LogDecoder,
|
||||
OrderInfo,
|
||||
orderUtils,
|
||||
Web3ProviderEngine,
|
||||
} from '@0x/contracts-test-utils';
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
BatchMatchedFillResults,
|
||||
FillResults,
|
||||
MatchedFillResults,
|
||||
OrderInfo,
|
||||
SignedOrder,
|
||||
SignedZeroExTransaction,
|
||||
} from '@0x/types';
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { constants, filterLogsToArguments, LogDecoder, txDefaults as testTxDefaults } from '@0x/contracts-test-utils';
|
||||
import { orderHashUtils } from '@0x/order-utils';
|
||||
import { FillResults, OrderWithoutDomain, SignatureType } from '@0x/types';
|
||||
import { FillResults, OrderInfo, OrderWithoutDomain, SignatureType } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { TxData, Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as crypto from 'crypto';
|
||||
@@ -23,7 +23,7 @@ export interface IsolatedExchangeEvents {
|
||||
transferFromCalls: DispatchTransferFromCallArgs[];
|
||||
}
|
||||
|
||||
export type Orderish = OrderWithoutDomain;
|
||||
export type Order = OrderWithoutDomain;
|
||||
export type Numberish = string | number | BigNumber;
|
||||
|
||||
export const DEFAULT_GOOD_SIGNATURE = createGoodSignature();
|
||||
@@ -67,12 +67,20 @@ export class IsolatedExchangeWrapper {
|
||||
this.logDecoder = new LogDecoder(web3Wrapper, artifacts);
|
||||
}
|
||||
|
||||
public async getTakerAssetFilledAmountAsync(order: Orderish): Promise<BigNumber> {
|
||||
public async getTakerAssetFilledAmountAsync(order: Order): Promise<BigNumber> {
|
||||
return this.instance.filled.callAsync(this.getOrderHash(order));
|
||||
}
|
||||
|
||||
public async cancelOrderAsync(order: Order, txOpts?: TxData): Promise<void> {
|
||||
await this.instance.cancelOrder.awaitTransactionSuccessAsync(order, txOpts);
|
||||
}
|
||||
|
||||
public async cancelOrdersUpToAsync(epoch: BigNumber, txOpts?: TxData): Promise<void> {
|
||||
await this.instance.cancelOrdersUpTo.awaitTransactionSuccessAsync(epoch, txOpts);
|
||||
}
|
||||
|
||||
public async fillOrderAsync(
|
||||
order: Orderish,
|
||||
order: Order,
|
||||
takerAssetFillAmount: Numberish,
|
||||
signature: string = DEFAULT_GOOD_SIGNATURE,
|
||||
txOpts?: TxData,
|
||||
@@ -86,7 +94,7 @@ export class IsolatedExchangeWrapper {
|
||||
);
|
||||
}
|
||||
|
||||
public getOrderHash(order: Orderish): string {
|
||||
public getOrderHash(order: Order): string {
|
||||
const domain = {
|
||||
verifyingContractAddress: this.instance.address,
|
||||
chainId: IsolatedExchangeWrapper.CHAIN_ID,
|
||||
@@ -94,6 +102,10 @@ export class IsolatedExchangeWrapper {
|
||||
return orderHashUtils.getOrderHashHex(_.assign(order, { domain }));
|
||||
}
|
||||
|
||||
public async getOrderInfoAsync(order: Order): Promise<OrderInfo> {
|
||||
return this.instance.getOrderInfo.callAsync(order);
|
||||
}
|
||||
|
||||
public getBalanceChange(assetData: string, address: string): BigNumber {
|
||||
if (assetData in this.lastTxBalanceChanges) {
|
||||
const balances = this.lastTxBalanceChanges[assetData];
|
||||
|
Reference in New Issue
Block a user