Files
protocol/contracts/exchange/test/internal.ts
F. Eugene Aumson f11d8a5bd8 @0x/order-utils refactors for v3: orderParsingUtils, signatureUtils, orderHashUtils, RevertErrors, transactionHashUtils (#2321)
* move orderParsingUtils from order-utils to connect

* Remove many functions from signatureUtils

Removed from the exported object, that is.  All of them are used in
other existing code, so they were all moved to be as local to their
usage as possible.

* remove orderHashUtils.isValidOrderHash()

* Move all *RevertErrors from order-utils...

...into their respective @0x/contracts- packages.

* Refactor @0x/order-utils' orderHashUtils away

- Move existing routines into @0x/contracts-test-utils

- Migrate non-contract-test callers to a newly-exposed getOrderHash()
method in DevUtils.

* Move all *RevertErrors from @0x/utils...

...into their respective @0x/contracts- packages.

* rm transactionHashUtils.isValidTransactionHash()

* DevUtils.sol: Fail yarn test if too big to deploy

* Refactor @0x/order-utils transactionHashUtils away

- Move existing routines into @0x/contracts-test-utils

- Migrate non-contract-test callers to a newly-exposed
getTransactionHash() method in DevUtils.

* Consolidate `Removed export...` CHANGELOG entries

* Rm EthBalanceChecker from devutils wrapper exports

* Stop importing from '.' or '.../src'

* fix builds

* fix prettier; dangling promise

* increase max bundle size
2019-11-14 17:14:24 -05:00

641 lines
31 KiB
TypeScript

import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
import { blockchainTests, constants, expect, hexRandom, orderHashUtils } from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { Order } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import ExchangeRevertErrors = require('../src/revert_errors');
import { artifacts } from './artifacts';
import {
TestExchangeInternalsContract,
TestExchangeInternalsDispatchTransferFromCalledEventArgs,
TestExchangeInternalsFillEventArgs,
} from './wrappers';
blockchainTests('Exchange core internal functions', env => {
const CHAIN_ID = 1337;
const ONE_ETHER = constants.ONE_ETHER;
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomHash = () => hexRandom(constants.WORD_LENGTH);
const randomAssetData = () => hexRandom(36);
let testExchange: TestExchangeInternalsContract;
let senderAddress: string;
const DEFAULT_PROTOCOL_MULTIPLIER = new BigNumber(150000);
const DEFAULT_GAS_PRICE = new BigNumber(200000);
before(async () => {
const accounts = await env.getAccountAddressesAsync();
senderAddress = accounts[0];
testExchange = await TestExchangeInternalsContract.deployFrom0xArtifactAsync(
artifacts.TestExchangeInternals,
env.provider,
env.txDefaults,
{},
new BigNumber(CHAIN_ID),
);
});
blockchainTests('assertValidMatch', () => {
const ORDER_DEFAULTS = {
senderAddress: randomAddress(),
makerAddress: randomAddress(),
takerAddress: randomAddress(),
makerFee: ONE_ETHER.times(0.001),
takerFee: ONE_ETHER.times(0.003),
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER.times(0.5),
makerAssetData: randomAssetData(),
takerAssetData: randomAssetData(),
makerFeeAssetData: randomAssetData(),
takerFeeAssetData: randomAssetData(),
salt: new BigNumber(_.random(0, 1e8)),
feeRecipientAddress: randomAddress(),
expirationTimeSeconds: new BigNumber(_.random(0, 1e8)),
exchangeAddress: constants.NULL_ADDRESS,
chainId: 1337, // The chain id for the isolated exchange
};
function makeOrder(details?: Partial<Order>): Order {
return _.assign({}, ORDER_DEFAULTS, details);
}
before(async () => {
ORDER_DEFAULTS.exchangeAddress = testExchange.address;
});
it('should revert if the maker asset multiplication should overflow', async () => {
const leftOrder = makeOrder({
makerAssetAmount: constants.MAX_UINT256,
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: constants.MAX_UINT256_ROOT,
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
});
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
leftOrder.makerAssetAmount,
rightOrder.makerAssetAmount,
);
return expect(testExchange.assertValidMatch(leftOrder, rightOrder).callAsync()).to.revertWith(
expectedError,
);
});
it('should revert if the taker asset multiplication should overflow', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
takerAssetAmount: constants.MAX_UINT256,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
takerAssetAmount: constants.MAX_UINT256_ROOT,
});
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
leftOrder.takerAssetAmount,
rightOrder.takerAssetAmount,
);
return expect(testExchange.assertValidMatch(leftOrder, rightOrder).callAsync()).to.revertWith(
expectedError,
);
});
it('should revert if the prices of the left order is less than the price of the right order', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(49, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
});
const orderHashHexLeft = orderHashUtils.getOrderHashHex(leftOrder);
const orderHashHexRight = orderHashUtils.getOrderHashHex(rightOrder);
const expectedError = new ExchangeRevertErrors.NegativeSpreadError(orderHashHexLeft, orderHashHexRight);
return expect(testExchange.assertValidMatch(leftOrder, rightOrder).callAsync()).to.revertWith(
expectedError,
);
});
it('should succeed if the prices of the left and right orders are equal', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
});
return expect(testExchange.assertValidMatch(leftOrder, rightOrder).callAsync()).to.be.fulfilled('');
});
it('should succeed if the price of the left order is higher than the price of the right', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
});
return expect(testExchange.assertValidMatch(leftOrder, rightOrder).callAsync()).to.be.fulfilled('');
});
});
blockchainTests.resets('updateFilledState', async () => {
const ORDER_DEFAULTS = {
senderAddress: randomAddress(),
makerAddress: randomAddress(),
takerAddress: randomAddress(),
makerFee: ONE_ETHER.times(0.001),
takerFee: ONE_ETHER.times(0.003),
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER.times(0.5),
makerAssetData: randomAssetData(),
takerAssetData: randomAssetData(),
makerFeeAssetData: randomAssetData(),
takerFeeAssetData: randomAssetData(),
salt: new BigNumber(_.random(0, 1e8)),
feeRecipientAddress: randomAddress(),
expirationTimeSeconds: new BigNumber(_.random(0, 1e8)),
chainId: 1337,
exchangeAddress: constants.NULL_ADDRESS,
};
function makeOrder(details?: Partial<Order>): Order {
return _.assign({}, ORDER_DEFAULTS, details);
}
async function testUpdateFilledStateAsync(
order: Order,
orderTakerAssetFilledAmount: BigNumber,
takerAddress: string,
takerAssetFillAmount: BigNumber,
protocolFeeMultiplier: BigNumber,
gasPrice: BigNumber,
): Promise<void> {
const orderHash = randomHash();
const fillResults = LibReferenceFunctions.calculateFillResults(
order,
takerAssetFillAmount,
protocolFeeMultiplier,
gasPrice,
);
const expectedFilledState = orderTakerAssetFilledAmount.plus(takerAssetFillAmount);
// CAll `testUpdateFilledState()`, which will set the `filled`
// state for this order to `orderTakerAssetFilledAmount` before
// calling `_updateFilledState()`.
const receipt = await testExchange
.testUpdateFilledState(order, takerAddress, orderHash, orderTakerAssetFilledAmount, fillResults)
.awaitTransactionSuccessAsync();
// Grab the new `filled` state for this order.
const actualFilledState = await testExchange.filled(orderHash).callAsync();
// Assert the `filled` state for this order.
expect(actualFilledState).to.bignumber.eq(expectedFilledState);
// Assert the logs.
// tslint:disable-next-line: no-unnecessary-type-assertion
const fillEvent = receipt.logs[0] as LogWithDecodedArgs<TestExchangeInternalsFillEventArgs>;
expect(fillEvent.event).to.eq('Fill');
expect(fillEvent.args.makerAddress).to.eq(order.makerAddress);
expect(fillEvent.args.feeRecipientAddress).to.eq(order.feeRecipientAddress);
expect(fillEvent.args.orderHash).to.eq(orderHash);
expect(fillEvent.args.takerAddress).to.eq(takerAddress);
expect(fillEvent.args.senderAddress).to.eq(senderAddress);
expect(fillEvent.args.makerAssetFilledAmount).to.bignumber.eq(fillResults.makerAssetFilledAmount);
expect(fillEvent.args.takerAssetFilledAmount).to.bignumber.eq(fillResults.takerAssetFilledAmount);
expect(fillEvent.args.makerFeePaid).to.bignumber.eq(fillResults.makerFeePaid);
expect(fillEvent.args.takerFeePaid).to.bignumber.eq(fillResults.takerFeePaid);
expect(fillEvent.args.makerAssetData).to.eq(order.makerAssetData);
expect(fillEvent.args.takerAssetData).to.eq(order.takerAssetData);
expect(fillEvent.args.makerFeeAssetData).to.eq(order.makerFeeAssetData);
expect(fillEvent.args.takerFeeAssetData).to.eq(order.takerFeeAssetData);
expect(fillEvent.args.protocolFeePaid).to.bignumber.eq(fillResults.protocolFeePaid);
}
it('emits a `Fill` event and updates `filled` state correctly', async () => {
const order = makeOrder();
return testUpdateFilledStateAsync(
order,
order.takerAssetAmount.times(0.1),
randomAddress(),
order.takerAssetAmount.times(0.25),
DEFAULT_PROTOCOL_MULTIPLIER,
DEFAULT_GAS_PRICE,
);
});
it('reverts if `leftOrderTakerAssetFilledAmount + fillResults.takerAssetFilledAmount` overflows', async () => {
const order = makeOrder();
const orderTakerAssetFilledAmount = constants.MAX_UINT256.dividedToIntegerBy(2);
const takerAssetFillAmount = constants.MAX_UINT256.dividedToIntegerBy(2).plus(2);
const fillResults = {
makerAssetFilledAmount: constants.ZERO_AMOUNT,
takerAssetFilledAmount: takerAssetFillAmount,
makerFeePaid: constants.ZERO_AMOUNT,
takerFeePaid: constants.ZERO_AMOUNT,
protocolFeePaid: constants.ZERO_AMOUNT,
};
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
orderTakerAssetFilledAmount,
takerAssetFillAmount,
);
return expect(
testExchange
.testUpdateFilledState(
order,
randomAddress(),
randomHash(),
orderTakerAssetFilledAmount,
fillResults,
)
.awaitTransactionSuccessAsync(),
).to.revertWith(expectedError);
});
});
blockchainTests('settleOrder', () => {
const DEFAULT_ORDER = {
senderAddress: randomAddress(),
makerAddress: randomAddress(),
takerAddress: randomAddress(),
makerFee: ONE_ETHER.times(0.001),
takerFee: ONE_ETHER.times(0.003),
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER.times(0.5),
makerAssetData: randomAssetData(),
takerAssetData: randomAssetData(),
makerFeeAssetData: randomAssetData(),
takerFeeAssetData: randomAssetData(),
salt: new BigNumber(_.random(0, 1e8)),
feeRecipientAddress: randomAddress(),
expirationTimeSeconds: new BigNumber(_.random(0, 1e8)),
};
it('calls `_dispatchTransferFrom()` in the right order with the correct arguments', async () => {
const order = DEFAULT_ORDER;
const orderHash = randomHash();
const takerAddress = randomAddress();
const fillResults = {
makerAssetFilledAmount: ONE_ETHER.times(2),
takerAssetFilledAmount: ONE_ETHER.times(10),
makerFeePaid: ONE_ETHER.times(0.01),
takerFeePaid: ONE_ETHER.times(0.025),
protocolFeePaid: constants.ZERO_AMOUNT,
};
const receipt = await testExchange
.settleOrder(orderHash, order, takerAddress, fillResults)
.awaitTransactionSuccessAsync();
const logs = receipt.logs as Array<
LogWithDecodedArgs<TestExchangeInternalsDispatchTransferFromCalledEventArgs>
>;
expect(logs.length === 4);
expect(_.every(logs, log => log.event === 'DispatchTransferFromCalled')).to.be.true();
// taker -> maker
expect(logs[0].args.orderHash).to.eq(orderHash);
expect(logs[0].args.assetData).to.eq(order.takerAssetData);
expect(logs[0].args.from).to.eq(takerAddress);
expect(logs[0].args.to).to.eq(order.makerAddress);
expect(logs[0].args.amount).to.bignumber.eq(fillResults.takerAssetFilledAmount);
// maker -> taker
expect(logs[1].args.orderHash).to.eq(orderHash);
expect(logs[1].args.assetData).to.eq(order.makerAssetData);
expect(logs[1].args.from).to.eq(order.makerAddress);
expect(logs[1].args.to).to.eq(takerAddress);
expect(logs[1].args.amount).to.bignumber.eq(fillResults.makerAssetFilledAmount);
// taker fee -> feeRecipient
expect(logs[2].args.orderHash).to.eq(orderHash);
expect(logs[2].args.assetData).to.eq(order.takerFeeAssetData);
expect(logs[2].args.from).to.eq(takerAddress);
expect(logs[2].args.to).to.eq(order.feeRecipientAddress);
expect(logs[2].args.amount).to.bignumber.eq(fillResults.takerFeePaid);
// maker fee -> feeRecipient
expect(logs[3].args.orderHash).to.eq(orderHash);
expect(logs[3].args.assetData).to.eq(order.makerFeeAssetData);
expect(logs[3].args.from).to.eq(order.makerAddress);
expect(logs[3].args.to).to.eq(order.feeRecipientAddress);
expect(logs[3].args.amount).to.bignumber.eq(fillResults.makerFeePaid);
});
});
blockchainTests('settleMatchOrders', () => {
const getOrder = () => {
return {
senderAddress: randomAddress(),
makerAddress: randomAddress(),
takerAddress: randomAddress(),
makerFee: ONE_ETHER.times(0.001),
takerFee: ONE_ETHER.times(0.003),
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER.times(0.5),
makerAssetData: randomAssetData(),
takerAssetData: randomAssetData(),
makerFeeAssetData: randomAssetData(),
takerFeeAssetData: randomAssetData(),
salt: new BigNumber(_.random(0, 1e8)),
feeRecipientAddress: randomAddress(),
expirationTimeSeconds: new BigNumber(_.random(0, 1e8)),
};
};
it('should revert if the taker fee paid fields addition overflow and left.feeRecipient == right.feeRecipient && left.takerFeeAssetData == right.takerFeeAssetData', async () => {
// Get the arguments for the call to `settleMatchOrders()`.
const leftOrder = getOrder();
const rightOrder = getOrder();
const leftOrderHash = randomHash();
const rightOrderHash = randomHash();
const takerAddress = randomAddress();
const matchedFillResults = {
left: {
makerAssetFilledAmount: ONE_ETHER.times(2),
takerAssetFilledAmount: ONE_ETHER.times(10),
makerFeePaid: ONE_ETHER.times(0.01),
takerFeePaid: constants.MAX_UINT256,
protocolFeePaid: constants.ZERO_AMOUNT,
},
right: {
takerAssetFilledAmount: ONE_ETHER.times(20),
makerAssetFilledAmount: ONE_ETHER.times(4),
makerFeePaid: ONE_ETHER.times(0.02),
takerFeePaid: constants.MAX_UINT256_ROOT,
protocolFeePaid: constants.ZERO_AMOUNT,
},
profitInLeftMakerAsset: ONE_ETHER,
profitInRightMakerAsset: ONE_ETHER.times(2),
};
// Set the fee recipient addresses and the taker fee asset data fields to be the same
rightOrder.feeRecipientAddress = leftOrder.feeRecipientAddress;
rightOrder.takerFeeAssetData = leftOrder.takerFeeAssetData;
// The expected error that should be thrown by the function.
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
matchedFillResults.left.takerFeePaid,
matchedFillResults.right.takerFeePaid,
);
// Ensure that the call to `settleMatchOrders()` fails with the expected error.
const tx = testExchange
.settleMatchOrders(
leftOrderHash,
rightOrderHash,
leftOrder,
rightOrder,
takerAddress,
matchedFillResults,
)
.sendTransactionAsync();
return expect(tx).to.revertWith(expectedError);
});
it('should succeed if the taker fee paid fields addition overflow and left.feeRecipient != right.feeRecipient || left.takerFeeAssetData != right.takerFeeAssetData', async () => {
// Get the arguments for the call to `settleMatchOrders()`.
const leftOrder = getOrder();
const rightOrder = getOrder();
const leftOrderHash = randomHash();
const rightOrderHash = randomHash();
const takerAddress = randomAddress();
const matchedFillResults = {
left: {
makerAssetFilledAmount: ONE_ETHER.times(2),
takerAssetFilledAmount: ONE_ETHER.times(10),
makerFeePaid: ONE_ETHER.times(0.01),
takerFeePaid: constants.MAX_UINT256,
protocolFeePaid: constants.ZERO_AMOUNT,
},
right: {
takerAssetFilledAmount: ONE_ETHER.times(20),
makerAssetFilledAmount: ONE_ETHER.times(4),
makerFeePaid: ONE_ETHER.times(0.02),
takerFeePaid: constants.MAX_UINT256_ROOT,
protocolFeePaid: constants.ZERO_AMOUNT,
},
profitInLeftMakerAsset: ONE_ETHER,
profitInRightMakerAsset: ONE_ETHER.times(2),
};
// The call to `settleMatchOrders()` should be successful.
return expect(
testExchange
.settleMatchOrders(
leftOrderHash,
rightOrderHash,
leftOrder,
rightOrder,
takerAddress,
matchedFillResults,
)
.sendTransactionAsync(),
).to.be.fulfilled('');
});
it('calls `_dispatchTransferFrom()` to collect fees from the left order when left.feeRecipient == right.feeRecipient && left.takerFeeAssetData == right.takerFeeAssetData', async () => {
const leftOrder = getOrder();
const rightOrder = getOrder();
const leftOrderHash = randomHash();
const rightOrderHash = randomHash();
const takerAddress = randomAddress();
const matchedFillResults = {
left: {
makerAssetFilledAmount: ONE_ETHER.times(2),
takerAssetFilledAmount: ONE_ETHER.times(10),
makerFeePaid: ONE_ETHER.times(0.01),
takerFeePaid: ONE_ETHER.times(0.025),
protocolFeePaid: constants.ZERO_AMOUNT,
},
right: {
takerAssetFilledAmount: ONE_ETHER.times(20),
makerAssetFilledAmount: ONE_ETHER.times(4),
makerFeePaid: ONE_ETHER.times(0.02),
takerFeePaid: ONE_ETHER.times(0.05),
protocolFeePaid: constants.ZERO_AMOUNT,
},
profitInLeftMakerAsset: ONE_ETHER,
profitInRightMakerAsset: ONE_ETHER.times(2),
};
// Set the fee recipient addresses and the taker fee asset data fields to be the same
rightOrder.feeRecipientAddress = leftOrder.feeRecipientAddress;
rightOrder.takerFeeAssetData = leftOrder.takerFeeAssetData;
// Call settleMatchOrders and collect the logs
const receipt = await testExchange
.settleMatchOrders(
leftOrderHash,
rightOrderHash,
leftOrder,
rightOrder,
takerAddress,
matchedFillResults,
)
.awaitTransactionSuccessAsync();
const logs = receipt.logs as Array<
LogWithDecodedArgs<TestExchangeInternalsDispatchTransferFromCalledEventArgs>
>;
// Ensure that the logs have the correct lengths and names
expect(logs.length).to.be.eq(7);
expect(_.every(logs, log => log.event === 'DispatchTransferFromCalled')).to.be.true();
// Right maker asset -> left maker
expect(logs[0].args.orderHash).to.be.eq(rightOrderHash);
expect(logs[0].args.assetData).to.be.eq(rightOrder.makerAssetData);
expect(logs[0].args.from).to.be.eq(rightOrder.makerAddress);
expect(logs[0].args.to).to.be.eq(leftOrder.makerAddress);
expect(logs[0].args.amount).bignumber.to.be.eq(matchedFillResults.left.takerAssetFilledAmount);
// Left maker asset -> right maker
expect(logs[1].args.orderHash).to.be.eq(leftOrderHash);
expect(logs[1].args.assetData).to.be.eq(leftOrder.makerAssetData);
expect(logs[1].args.from).to.be.eq(leftOrder.makerAddress);
expect(logs[1].args.to).to.be.eq(rightOrder.makerAddress);
expect(logs[1].args.amount).bignumber.to.be.eq(matchedFillResults.right.takerAssetFilledAmount);
// Right maker fee -> right fee recipient
expect(logs[2].args.orderHash).to.be.eq(rightOrderHash);
expect(logs[2].args.assetData).to.be.eq(rightOrder.makerFeeAssetData);
expect(logs[2].args.from).to.be.eq(rightOrder.makerAddress);
expect(logs[2].args.to).to.be.eq(rightOrder.feeRecipientAddress);
expect(logs[2].args.amount).bignumber.to.be.eq(matchedFillResults.right.makerFeePaid);
// Left maker fee -> left fee recipient
expect(logs[3].args.orderHash).to.be.eq(leftOrderHash);
expect(logs[3].args.assetData).to.be.eq(leftOrder.makerFeeAssetData);
expect(logs[3].args.from).to.be.eq(leftOrder.makerAddress);
expect(logs[3].args.to).to.be.eq(leftOrder.feeRecipientAddress);
expect(logs[3].args.amount).bignumber.to.be.eq(matchedFillResults.left.makerFeePaid);
// Left maker -> taker profit
expect(logs[4].args.orderHash).to.be.eq(leftOrderHash);
expect(logs[4].args.assetData).to.be.eq(leftOrder.makerAssetData);
expect(logs[4].args.from).to.be.eq(leftOrder.makerAddress);
expect(logs[4].args.to).to.be.eq(takerAddress);
expect(logs[4].args.amount).bignumber.to.be.eq(matchedFillResults.profitInLeftMakerAsset);
// right maker -> taker profit
expect(logs[5].args.orderHash).to.be.eq(rightOrderHash);
expect(logs[5].args.assetData).to.be.eq(rightOrder.makerAssetData);
expect(logs[5].args.from).to.be.eq(rightOrder.makerAddress);
expect(logs[5].args.to).to.be.eq(takerAddress);
expect(logs[5].args.amount).bignumber.to.be.eq(matchedFillResults.profitInRightMakerAsset);
// taker fees -> fee recipient
expect(logs[6].args.orderHash).to.be.eq(leftOrderHash);
expect(logs[6].args.assetData).to.be.eq(leftOrder.takerFeeAssetData);
expect(logs[6].args.from).to.be.eq(takerAddress);
expect(logs[6].args.to).to.be.eq(leftOrder.feeRecipientAddress);
expect(logs[6].args.amount).bignumber.to.be.eq(ONE_ETHER.times(0.075));
});
it('calls `_dispatchTransferFrom()` from in the right order when the fee recipients and taker fee asset data are not the same', async () => {
const leftOrder = getOrder();
const rightOrder = getOrder();
const leftOrderHash = randomHash();
const rightOrderHash = randomHash();
const takerAddress = randomAddress();
const matchedFillResults = {
left: {
makerAssetFilledAmount: ONE_ETHER.times(2),
takerAssetFilledAmount: ONE_ETHER.times(10),
makerFeePaid: ONE_ETHER.times(0.01),
takerFeePaid: ONE_ETHER.times(0.025),
protocolFeePaid: constants.ZERO_AMOUNT,
},
right: {
takerAssetFilledAmount: ONE_ETHER.times(20),
makerAssetFilledAmount: ONE_ETHER.times(4),
makerFeePaid: ONE_ETHER.times(0.02),
takerFeePaid: ONE_ETHER.times(0.05),
protocolFeePaid: constants.ZERO_AMOUNT,
},
profitInLeftMakerAsset: ONE_ETHER,
profitInRightMakerAsset: ONE_ETHER.times(2),
};
// Call settleMatchOrders and collect the logs
const receipt = await testExchange
.settleMatchOrders(
leftOrderHash,
rightOrderHash,
leftOrder,
rightOrder,
takerAddress,
matchedFillResults,
)
.awaitTransactionSuccessAsync();
const logs = receipt.logs as Array<
LogWithDecodedArgs<TestExchangeInternalsDispatchTransferFromCalledEventArgs>
>;
// Ensure that the logs have the correct lengths and names
expect(logs.length).to.be.eq(8);
expect(_.every(logs, log => log.event === 'DispatchTransferFromCalled')).to.be.true();
// Right maker asset -> left maker
expect(logs[0].args.orderHash).to.be.eq(rightOrderHash);
expect(logs[0].args.assetData).to.be.eq(rightOrder.makerAssetData);
expect(logs[0].args.from).to.be.eq(rightOrder.makerAddress);
expect(logs[0].args.to).to.be.eq(leftOrder.makerAddress);
expect(logs[0].args.amount).bignumber.to.be.eq(matchedFillResults.left.takerAssetFilledAmount);
// Left maker asset -> right maker
expect(logs[1].args.orderHash).to.be.eq(leftOrderHash);
expect(logs[1].args.assetData).to.be.eq(leftOrder.makerAssetData);
expect(logs[1].args.from).to.be.eq(leftOrder.makerAddress);
expect(logs[1].args.to).to.be.eq(rightOrder.makerAddress);
expect(logs[1].args.amount).bignumber.to.be.eq(matchedFillResults.right.takerAssetFilledAmount);
// Right maker fee -> right fee recipient
expect(logs[2].args.orderHash).to.be.eq(rightOrderHash);
expect(logs[2].args.assetData).to.be.eq(rightOrder.makerFeeAssetData);
expect(logs[2].args.from).to.be.eq(rightOrder.makerAddress);
expect(logs[2].args.to).to.be.eq(rightOrder.feeRecipientAddress);
expect(logs[2].args.amount).bignumber.to.be.eq(matchedFillResults.right.makerFeePaid);
// Left maker fee -> left fee recipient
expect(logs[3].args.orderHash).to.be.eq(leftOrderHash);
expect(logs[3].args.assetData).to.be.eq(leftOrder.makerFeeAssetData);
expect(logs[3].args.from).to.be.eq(leftOrder.makerAddress);
expect(logs[3].args.to).to.be.eq(leftOrder.feeRecipientAddress);
expect(logs[3].args.amount).bignumber.to.be.eq(matchedFillResults.left.makerFeePaid);
// Left maker -> taker profit
expect(logs[4].args.orderHash).to.be.eq(leftOrderHash);
expect(logs[4].args.assetData).to.be.eq(leftOrder.makerAssetData);
expect(logs[4].args.from).to.be.eq(leftOrder.makerAddress);
expect(logs[4].args.to).to.be.eq(takerAddress);
expect(logs[4].args.amount).bignumber.to.be.eq(matchedFillResults.profitInLeftMakerAsset);
// right maker -> taker profit
expect(logs[5].args.orderHash).to.be.eq(rightOrderHash);
expect(logs[5].args.assetData).to.be.eq(rightOrder.makerAssetData);
expect(logs[5].args.from).to.be.eq(rightOrder.makerAddress);
expect(logs[5].args.to).to.be.eq(takerAddress);
expect(logs[5].args.amount).bignumber.to.be.eq(matchedFillResults.profitInRightMakerAsset);
// Right taker fee -> right fee recipient
expect(logs[6].args.orderHash).to.be.eq(rightOrderHash);
expect(logs[6].args.assetData).to.be.eq(rightOrder.takerFeeAssetData);
expect(logs[6].args.from).to.be.eq(takerAddress);
expect(logs[6].args.to).to.be.eq(rightOrder.feeRecipientAddress);
expect(logs[6].args.amount).bignumber.to.be.eq(matchedFillResults.right.takerFeePaid);
// Right taker fee -> right fee recipient
expect(logs[7].args.orderHash).to.be.eq(leftOrderHash);
expect(logs[7].args.assetData).to.be.eq(leftOrder.takerFeeAssetData);
expect(logs[7].args.from).to.be.eq(takerAddress);
expect(logs[7].args.to).to.be.eq(leftOrder.feeRecipientAddress);
expect(logs[7].args.amount).bignumber.to.be.eq(matchedFillResults.left.takerFeePaid);
});
});
});
// tslint:disable-line:max-file-line-count