* 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
641 lines
31 KiB
TypeScript
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
|