protocol/contracts/exchange-libs/test/lib_fill_results.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

1933 lines
91 KiB
TypeScript

import {
blockchainTests,
constants,
describe,
expect,
hexRandom,
testCombinatoriallyWithReferenceFunc,
uint256Values,
} from '@0x/contracts-test-utils';
import { SafeMathRevertErrors } from '@0x/contracts-utils';
import { FillResults, MatchedFillResults, Order } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import LibMathRevertErrors = require('../src/lib_math_revert_errors');
import { addFillResults, calculateFillResults, getPartialAmountFloor } from '../src/reference_functions';
import { artifacts } from './artifacts';
import { TestLibFillResultsContract } from './wrappers';
blockchainTests('LibFillResults', env => {
interface PartialMatchedFillResults {
left: Partial<FillResults>;
right: Partial<FillResults>;
profitInLeftMakerAsset?: BigNumber;
profitInRightMakerAsset?: BigNumber;
}
const { ONE_ETHER, MAX_UINT256 } = constants;
const EMPTY_ORDER: Order = {
senderAddress: constants.NULL_ADDRESS,
makerAddress: constants.NULL_ADDRESS,
takerAddress: constants.NULL_ADDRESS,
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
makerAssetAmount: constants.ZERO_AMOUNT,
takerAssetAmount: constants.ZERO_AMOUNT,
makerAssetData: constants.NULL_BYTES,
takerAssetData: constants.NULL_BYTES,
makerFeeAssetData: constants.NULL_BYTES,
takerFeeAssetData: constants.NULL_BYTES,
salt: constants.ZERO_AMOUNT,
feeRecipientAddress: constants.NULL_ADDRESS,
expirationTimeSeconds: constants.ZERO_AMOUNT,
chainId: 1,
exchangeAddress: constants.NULL_ADDRESS,
};
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
const randomAssetData = () => hexRandom(36);
const randomUint256 = () => new BigNumber(hexRandom(constants.WORD_LENGTH));
let libsContract: TestLibFillResultsContract;
let makerAddressLeft: string;
let makerAddressRight: string;
before(async () => {
const accounts = await env.getAccountAddressesAsync();
makerAddressLeft = accounts[0];
makerAddressRight = accounts[1];
libsContract = await TestLibFillResultsContract.deployFrom0xArtifactAsync(
artifacts.TestLibFillResults,
env.provider,
env.txDefaults,
{},
);
});
describe('calculateFillResults', () => {
describe.optional('combinatorial tests', () => {
function makeOrder(
makerAssetAmount: BigNumber,
takerAssetAmount: BigNumber,
makerFee: BigNumber,
takerFee: BigNumber,
): Order {
return {
...EMPTY_ORDER,
makerAssetAmount,
takerAssetAmount,
makerFee,
takerFee,
};
}
async function referenceCalculateFillResultsAsync(
orderTakerAssetAmount: BigNumber,
takerAssetFilledAmount: BigNumber,
otherAmount: BigNumber,
): Promise<FillResults> {
// Note(albrow): Here we are re-using the same value (otherAmount)
// for order.makerAssetAmount, order.makerFee, and order.takerFee.
// This should be safe because they are never used with each other
// in any mathematical operation in either the reference TypeScript
// implementation or the Solidity implementation of
// calculateFillResults.
return calculateFillResults(
makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount),
takerAssetFilledAmount,
takerAssetFilledAmount, // Using this so that the gas price is distinct from protocolFeeMultiplier
otherAmount,
);
}
async function testCalculateFillResultsAsync(
orderTakerAssetAmount: BigNumber,
takerAssetFilledAmount: BigNumber,
otherAmount: BigNumber,
): Promise<FillResults> {
const order = makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount);
return libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
takerAssetFilledAmount, // Using this so that the gas price is distinct from protocolFeeMultiplier
otherAmount,
)
.callAsync();
}
testCombinatoriallyWithReferenceFunc(
'calculateFillResults',
referenceCalculateFillResultsAsync,
testCalculateFillResultsAsync,
[uint256Values, uint256Values, uint256Values],
);
});
describe('explicit tests', () => {
const MAX_UINT256_ROOT = constants.MAX_UINT256_ROOT;
const DEFAULT_GAS_PRICE = new BigNumber(200000);
const DEFAULT_PROTOCOL_FEE_MULTIPLIER = new BigNumber(150000);
function makeOrder(details?: Partial<Order>): Order {
return _.assign({}, EMPTY_ORDER, details);
}
it('matches the output of the reference function', async () => {
const order = makeOrder({
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER.times(2),
makerFee: ONE_ETHER.times(0.0023),
takerFee: ONE_ETHER.times(0.0025),
});
const takerAssetFilledAmount = ONE_ETHER.dividedToIntegerBy(3);
const expected = calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
);
const actual = await libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync();
expect(actual).to.deep.eq(expected);
});
it('reverts if computing `fillResults.makerAssetFilledAmount` overflows', async () => {
// All values need to be large to ensure we don't trigger a RoundingError.
const order = makeOrder({
makerAssetAmount: MAX_UINT256_ROOT.times(2),
takerAssetAmount: MAX_UINT256_ROOT,
});
const takerAssetFilledAmount = MAX_UINT256_ROOT;
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
takerAssetFilledAmount,
order.makerAssetAmount,
);
return expect(
libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync(),
).to.revertWith(expectedError);
});
it('reverts if computing `fillResults.makerFeePaid` overflows', async () => {
// All values need to be large to ensure we don't trigger a RoundingError.
const order = makeOrder({
makerAssetAmount: MAX_UINT256_ROOT,
takerAssetAmount: MAX_UINT256_ROOT,
makerFee: MAX_UINT256_ROOT.times(11),
});
const takerAssetFilledAmount = MAX_UINT256_ROOT.dividedToIntegerBy(10);
const makerAssetFilledAmount = getPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
makerAssetFilledAmount,
order.makerFee,
);
return expect(
libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync(),
).to.revertWith(expectedError);
});
it('reverts if computing `fillResults.takerFeePaid` overflows', async () => {
// All values need to be large to ensure we don't trigger a RoundingError.
const order = makeOrder({
makerAssetAmount: MAX_UINT256_ROOT,
takerAssetAmount: MAX_UINT256_ROOT,
takerFee: MAX_UINT256_ROOT.times(11),
});
const takerAssetFilledAmount = MAX_UINT256_ROOT.dividedToIntegerBy(10);
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
takerAssetFilledAmount,
order.takerFee,
);
return expect(
libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync(),
).to.revertWith(expectedError);
});
it('reverts if `order.takerAssetAmount` is 0', async () => {
const order = makeOrder({
makerAssetAmount: ONE_ETHER,
takerAssetAmount: constants.ZERO_AMOUNT,
});
const takerAssetFilledAmount = ONE_ETHER;
const expectedError = new LibMathRevertErrors.DivisionByZeroError();
return expect(
libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync(),
).to.revertWith(expectedError);
});
it('reverts if there is a rounding error computing `makerAsssetFilledAmount`', async () => {
const order = makeOrder({
makerAssetAmount: new BigNumber(100),
takerAssetAmount: ONE_ETHER,
});
const takerAssetFilledAmount = order.takerAssetAmount.dividedToIntegerBy(3);
const expectedError = new LibMathRevertErrors.RoundingError(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
return expect(
libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync(),
).to.revertWith(expectedError);
});
it('reverts if there is a rounding error computing `makerFeePaid`', async () => {
const order = makeOrder({
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER,
makerFee: new BigNumber(100),
});
const takerAssetFilledAmount = order.takerAssetAmount.dividedToIntegerBy(3);
const makerAssetFilledAmount = getPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
const expectedError = new LibMathRevertErrors.RoundingError(
makerAssetFilledAmount,
order.makerAssetAmount,
order.makerFee,
);
return expect(
libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync(),
).to.revertWith(expectedError);
});
it('reverts if there is a rounding error computing `takerFeePaid`', async () => {
const order = makeOrder({
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER,
takerFee: new BigNumber(100),
});
const takerAssetFilledAmount = order.takerAssetAmount.dividedToIntegerBy(3);
const makerAssetFilledAmount = getPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
const expectedError = new LibMathRevertErrors.RoundingError(
makerAssetFilledAmount,
order.makerAssetAmount,
order.takerFee,
);
return expect(
libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync(),
).to.revertWith(expectedError);
});
it('reverts if computing `fillResults.protocolFeePaid` overflows', async () => {
const order = makeOrder({
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER.times(2),
makerFee: ONE_ETHER.times(0.0023),
takerFee: ONE_ETHER.times(0.0025),
});
const takerAssetFilledAmount = ONE_ETHER.dividedToIntegerBy(3);
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow,
DEFAULT_GAS_PRICE,
MAX_UINT256,
);
return expect(
libsContract
.calculateFillResults(order, takerAssetFilledAmount, MAX_UINT256, DEFAULT_GAS_PRICE)
.callAsync(),
).to.revertWith(expectedError);
});
it('reverts if there is a rounding error computing `makerFeePaid`', async () => {
const order = makeOrder({
makerAssetAmount: ONE_ETHER,
takerAssetAmount: ONE_ETHER,
makerFee: new BigNumber(100),
});
const takerAssetFilledAmount = order.takerAssetAmount.dividedToIntegerBy(3);
const makerAssetFilledAmount = getPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
const expectedError = new LibMathRevertErrors.RoundingError(
makerAssetFilledAmount,
order.makerAssetAmount,
order.makerFee,
);
return expect(
libsContract
.calculateFillResults(
order,
takerAssetFilledAmount,
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
DEFAULT_GAS_PRICE,
)
.callAsync(),
).to.revertWith(expectedError);
});
});
});
describe('addFillResults', () => {
describe('explicit tests', () => {
const DEFAULT_FILL_RESULTS = [
{
makerAssetFilledAmount: ONE_ETHER,
takerAssetFilledAmount: ONE_ETHER.times(2),
makerFeePaid: ONE_ETHER.times(0.001),
takerFeePaid: ONE_ETHER.times(0.002),
protocolFeePaid: ONE_ETHER.times(0.003),
},
{
makerAssetFilledAmount: ONE_ETHER.times(0.01),
takerAssetFilledAmount: ONE_ETHER.times(2).times(0.01),
makerFeePaid: ONE_ETHER.times(0.001).times(0.01),
takerFeePaid: ONE_ETHER.times(0.002).times(0.01),
protocolFeePaid: ONE_ETHER.times(0.003).times(0.01),
},
];
it('matches the output of the reference function', async () => {
const [a, b] = DEFAULT_FILL_RESULTS;
const expected = addFillResults(a, b);
const actual = await libsContract.addFillResults(a, b).callAsync();
expect(actual).to.deep.equal(expected);
});
it('reverts if computing `makerAssetFilledAmount` overflows', async () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.makerAssetFilledAmount = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.makerAssetFilledAmount,
b.makerAssetFilledAmount,
);
return expect(libsContract.addFillResults(a, b).callAsync()).to.revertWith(expectedError);
});
it('reverts if computing `takerAssetFilledAmount` overflows', async () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.takerAssetFilledAmount = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.takerAssetFilledAmount,
b.takerAssetFilledAmount,
);
return expect(libsContract.addFillResults(a, b).callAsync()).to.revertWith(expectedError);
});
it('reverts if computing `makerFeePaid` overflows', async () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.makerFeePaid = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.makerFeePaid,
b.makerFeePaid,
);
return expect(libsContract.addFillResults(a, b).callAsync()).to.revertWith(expectedError);
});
it('reverts if computing `takerFeePaid` overflows', async () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.takerFeePaid = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.takerFeePaid,
b.takerFeePaid,
);
return expect(libsContract.addFillResults(a, b).callAsync()).to.revertWith(expectedError);
});
it('reverts if computing `protocolFeePaid` overflows', async () => {
const [a, b] = _.cloneDeep(DEFAULT_FILL_RESULTS);
b.protocolFeePaid = MAX_UINT256;
const expectedError = new SafeMathRevertErrors.Uint256BinOpError(
SafeMathRevertErrors.BinOpErrorCodes.AdditionOverflow,
a.protocolFeePaid,
b.protocolFeePaid,
);
return expect(libsContract.addFillResults(a, b).callAsync()).to.revertWith(expectedError);
});
});
});
const EMPTY_FILL_RESULTS: FillResults = {
makerAssetFilledAmount: constants.ZERO_AMOUNT,
takerAssetFilledAmount: constants.ZERO_AMOUNT,
makerFeePaid: constants.ZERO_AMOUNT,
takerFeePaid: constants.ZERO_AMOUNT,
protocolFeePaid: constants.ZERO_AMOUNT,
};
const EMPTY_MATCHED_FILL_RESULTS: MatchedFillResults = {
left: EMPTY_FILL_RESULTS,
right: EMPTY_FILL_RESULTS,
profitInLeftMakerAsset: constants.ZERO_AMOUNT,
profitInRightMakerAsset: constants.ZERO_AMOUNT,
};
const COMMON_MATCHED_FILL_RESULTS = {
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 4),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 4),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(3, 18),
profitInRightMakerAsset: constants.ZERO_AMOUNT,
};
function createMatchedFillResults(partialMatchedFillResults: PartialMatchedFillResults): MatchedFillResults {
const matchedFillResults = EMPTY_MATCHED_FILL_RESULTS;
matchedFillResults.left = _.assign({}, EMPTY_FILL_RESULTS, partialMatchedFillResults.left);
matchedFillResults.right = _.assign({}, EMPTY_FILL_RESULTS, partialMatchedFillResults.right);
matchedFillResults.profitInLeftMakerAsset =
partialMatchedFillResults.profitInLeftMakerAsset || constants.ZERO_AMOUNT;
matchedFillResults.profitInRightMakerAsset =
partialMatchedFillResults.profitInRightMakerAsset || constants.ZERO_AMOUNT;
return matchedFillResults;
}
blockchainTests('calculateMatchedFillResults', async () => {
/**
* Asserts that the results of calling `calculateMatchedFillResults()` is consistent with the results that are expected.
*/
async function assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults: MatchedFillResults,
leftOrder: Order,
rightOrder: Order,
leftOrderTakerAssetFilledAmount: BigNumber,
rightOrderTakerAssetFilledAmount: BigNumber,
protocolFeeMultiplier: BigNumber,
gasPrice: BigNumber,
from?: string,
): Promise<void> {
const actualMatchedFillResults = await libsContract
.calculateMatchedFillResults(
leftOrder,
rightOrder,
leftOrderTakerAssetFilledAmount,
rightOrderTakerAssetFilledAmount,
protocolFeeMultiplier,
gasPrice,
false,
)
.callAsync({ from });
expect(actualMatchedFillResults).to.be.deep.eq(expectedMatchedFillResults);
}
const ORDER_DEFAULTS = {
...constants.STATIC_ORDER_PARAMS,
makerAddress: randomAddress(),
takerAddress: randomAddress(),
senderAddress: randomAddress(),
makerAssetData: randomAssetData(),
takerAssetData: randomAssetData(),
makerFeeAssetData: randomAssetData(),
takerFeeAssetData: randomAssetData(),
feeRecipientAddress: randomAddress(),
expirationTimeSeconds: randomUint256(),
salt: randomUint256(),
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 = libsContract.address;
});
it('should correctly calculate the results when only the right order is fully filled', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(98, 0),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.4705882352941176'), 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.5306122448979591'), 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should correctly calculate the results when only the left order is fully filled', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(14, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('92.7835051546391752'), 16), // 92.85%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('92.8571428571428571'), 16), // 92.85%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(2, 0),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should give right maker a better price when rounding', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(83, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(49, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('26.5060240963855421'), 16), // 26.506%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('26.5306122448979591'), 16), // 26.531%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(3, 0),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should give left maker a better sell price when rounding', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
makerAddress: makerAddressLeft,
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('91.6666666666666666'), 16), // 91.6%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('91.7525773195876288'), 16), // 91.75%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(10, 0),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('Should give right maker and right taker a favorable fee price when rounding', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(83, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(49, 0),
makerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
takerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(2650, 0),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(2653, 0),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(3, 0),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('Should give left maker and left taker a favorable fee price when rounding', async () => {
// Create orders to match
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
makerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
takerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(9166, 0),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(9175, 0),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(10, 0),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('Should transfer correct amounts when right order fill amount deviates from amount derived by `Exchange.fillOrder`', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1005, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2126, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1063, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1005, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1005, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(503, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('47.2718720602069614'), 16), // 47.27%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('47.3189087488240827'), 16), // 47.31%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(497, 0),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts when orders completely fill each other', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(3, 18),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts when left order is completely filled and right order is partially filled', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(20, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(4, 18),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(50, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(50, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(3, 18),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts when right order is completely filled and left order is partially filled', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(10, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(10, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(3, 18),
});
await assertCalculateMatchedFillResultsAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
const feeRecipientAddress = randomAddress();
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
feeRecipientAddress,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
feeRecipientAddress,
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
it('should transfer the correct amounts if taker == leftMaker', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
leftOrder.makerAddress,
);
});
it('should transfer the correct amounts if taker == leftMaker', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
rightOrder.makerAddress,
);
});
it('should transfer the correct amounts if taker == leftFeeRecipient', async () => {
const feeRecipientAddressLeft = randomAddress();
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
feeRecipientAddress: feeRecipientAddressLeft,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
feeRecipientAddressLeft,
);
});
it('should transfer the correct amounts if taker == rightFeeRecipient', async () => {
const feeRecipientAddressRight = randomAddress();
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
feeRecipientAddress: feeRecipientAddressRight,
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
feeRecipientAddressRight,
);
});
it('should transfer the correct amounts if leftMaker == leftFeeRecipient && rightMaker == rightFeeRecipient', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
feeRecipientAddress: makerAddressLeft,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
feeRecipientAddress: makerAddressRight,
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
it('should transfer the correct amounts if leftMaker == leftFeeRecipient && leftMakerFeeAsset == leftTakerAsset', async () => {
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeeAssetData: rightOrder.makerAssetData,
feeRecipientAddress: makerAddressLeft,
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
it('should transfer the correct amounts if rightMaker == rightFeeRecipient && rightMakerFeeAsset == rightTakerAsset', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeeAssetData: leftOrder.makerAssetData,
feeRecipientAddress: makerAddressRight,
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
it('should transfer the correct amounts if rightMaker == rightFeeRecipient && rightTakerAsset == rightMakerFeeAsset && leftMaker == leftFeeRecipient && leftTakerAsset == leftMakerFeeAsset', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
feeRecipientAddress: makerAddressLeft,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeeAssetData: leftOrder.makerAssetData,
feeRecipientAddress: makerAddressRight,
});
await assertCalculateMatchedFillResultsAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
});
blockchainTests('calculateMatchedFillResultsWithMaximalFill', async () => {
/**
* Asserts that the results of calling `calculateMatchedFillResults()` is consistent with the results that are expected.
*/
async function assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults: MatchedFillResults,
leftOrder: Order,
rightOrder: Order,
leftOrderTakerAssetFilledAmount: BigNumber,
rightOrderTakerAssetFilledAmount: BigNumber,
protocolFeeMultiplier: BigNumber,
gasPrice: BigNumber,
from?: string,
): Promise<void> {
const actualMatchedFillResults = await libsContract
.calculateMatchedFillResults(
leftOrder,
rightOrder,
leftOrderTakerAssetFilledAmount,
rightOrderTakerAssetFilledAmount,
protocolFeeMultiplier,
gasPrice,
true,
)
.callAsync({ from });
expect(actualMatchedFillResults).to.be.deep.eq(expectedMatchedFillResults);
}
const ORDER_DEFAULTS = {
...constants.STATIC_ORDER_PARAMS,
makerAddress: randomAddress(),
takerAddress: randomAddress(),
senderAddress: randomAddress(),
makerAssetData: randomAssetData(),
takerAssetData: randomAssetData(),
makerFeeAssetData: randomAssetData(),
takerFeeAssetData: randomAssetData(),
feeRecipientAddress: randomAddress(),
expirationTimeSeconds: randomUint256(),
salt: randomUint256(),
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 = libsContract.address;
});
it('should transfer correct amounts when right order is fully filled', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(17, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(98, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.4705882352941176'), 16), // 76.47%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.5306122448979591'), 16), // 76.53%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(75, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(13, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
new BigNumber(150000),
new BigNumber(100000),
);
});
it('Should transfer correct amounts when left order is fully filled', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(196, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(28, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(90, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(105, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(15, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('53.5714285714285714'), 16), // 53.57%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('53.5714285714285714'), 16), // 53.57%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: constants.ZERO_AMOUNT,
profitInRightMakerAsset: Web3Wrapper.toBaseUnitAmount(15, 0),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
new BigNumber(150000),
new BigNumber(100000),
);
});
it('Should transfer correct amounts when left order is fully filled', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(87, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(48, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(29, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('33.3333333333333333'), 16), // 33.33%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('33.3333333333333333'), 16), // 33.33%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: constants.ZERO_AMOUNT,
profitInRightMakerAsset: Web3Wrapper.toBaseUnitAmount(7, 0),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should fully fill both orders and pay out profit in both maker assets', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(7, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(4, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(8, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(6, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(7, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(4, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(8, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(6, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(1, 0),
profitInRightMakerAsset: Web3Wrapper.toBaseUnitAmount(4, 0),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('Should give left maker a better sell price when rounding', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('91.6666666666666666'), 16), // 91.6%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('91.7525773195876288'), 16), // 91.75%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(10, 0),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('Should give right maker and right taker a favorable fee price when rounding', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(87, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(48, 0),
makerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
takerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(22, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(29, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(16, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(3333, 0),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(3333, 0),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInRightMakerAsset: Web3Wrapper.toBaseUnitAmount(7, 0),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('Should give left maker and left taker a favorable fee price when rounding', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(12, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(97, 0),
makerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
takerFee: Web3Wrapper.toBaseUnitAmount(10000, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(11, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(9166, 0),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(9175, 0),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(89, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(10, 0),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
const expectedMatchedFillResults = {
...COMMON_MATCHED_FILL_RESULTS,
left: {
...COMMON_MATCHED_FILL_RESULTS.left,
makerFeePaid: Web3Wrapper.toBaseUnitAmount(10, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(10, 16),
},
};
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
const rightOrder2 = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
});
const expectedMatchedFillResults2 = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(45, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(90, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(90, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(90, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(90, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(45, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(90, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(90, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults2,
leftOrder,
rightOrder2,
Web3Wrapper.toBaseUnitAmount(10, 18),
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('Should transfer correct amounts when right order fill amount deviates from amount derived by `Exchange.fillOrder`', async () => {
const leftOrder = makeOrder({
makerAddress: makerAddressLeft,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1005, 0),
});
const rightOrder = makeOrder({
makerAddress: makerAddressRight,
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(2126, 0),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1063, 0),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1005, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(2000, 0),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(1000, 0),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('94.0733772342427093'), 16), // 94.07%
takerFeePaid: Web3Wrapper.toBaseUnitAmount(new BigNumber('94.0733772342427093'), 16), // 94.07%
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInRightMakerAsset: Web3Wrapper.toBaseUnitAmount(995, 0),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
});
const expectedMatchedFillResults = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(10, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(10, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
profitInRightMakerAsset: Web3Wrapper.toBaseUnitAmount(3, 18),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
// Create second left order
// Note: This order needs makerAssetAmount=96/takerAssetAmount=48 to fully fill the right order.
// However, we use 100/50 to ensure a partial fill as we want to go down the "right fill"
// branch in the contract twice for this test.
const leftOrder2 = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(100, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(50, 18),
});
const expectedMatchedFillResults2 = createMatchedFillResults({
left: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(90, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(45, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(90, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(90, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
right: {
makerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(45, 18),
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(90, 18),
makerFeePaid: Web3Wrapper.toBaseUnitAmount(90, 16),
takerFeePaid: Web3Wrapper.toBaseUnitAmount(90, 16),
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 9),
},
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
expectedMatchedFillResults2,
leftOrder2,
rightOrder,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(10, 18),
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 5),
);
});
it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => {
const feeRecipientAddress = randomAddress();
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
feeRecipientAddress,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
feeRecipientAddress,
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
it('should transfer the correct amounts if taker == leftMaker', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
leftOrder.makerAddress,
);
});
it('should transfer the correct amounts if taker == rightMaker', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
rightOrder.makerAddress,
);
});
it('should transfer the correct amounts if taker == leftFeeRecipient', async () => {
const feeRecipientAddress = randomAddress();
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
feeRecipientAddress,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
feeRecipientAddress,
);
});
it('should transfer the correct amounts if taker == rightFeeRecipient', async () => {
const feeRecipientAddress = randomAddress();
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
feeRecipientAddress,
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
feeRecipientAddress,
);
});
it('should transfer the correct amounts if leftMaker == leftFeeRecipient && rightMaker == rightFeeRecipient', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
feeRecipientAddress: makerAddressLeft,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
feeRecipientAddress: makerAddressRight,
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
it('should transfer the correct amounts if leftMaker == leftFeeRecipient && leftMakerFeeAsset == leftTakerAsset', async () => {
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
});
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeeAssetData: rightOrder.makerAssetData,
feeRecipientAddress: makerAddressLeft,
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
it('should transfer the correct amounts if rightMaker == rightFeeRecipient && rightMakerFeeAsset == rightTakerAsset', async () => {
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeeAssetData: leftOrder.makerAssetData,
feeRecipientAddress: makerAddressRight,
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
it('should transfer the correct amounts if rightMaker == rightFeeRecipient && rightTakerAsset == rightMakerFeeAsset && leftMaker == leftFeeRecipient && leftTakerAsset == leftMakerFeeAsset', async () => {
const makerFeeAssetData = randomAssetData();
const leftOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(5, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
makerFeeAssetData,
feeRecipientAddress: makerAddressLeft,
});
const rightOrder = makeOrder({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(2, 18),
makerFeeAssetData: leftOrder.makerAssetData,
feeRecipientAddress: makerAddressRight,
});
await assertCalculateMatchedFillResultsWithMaximalFillAsync(
COMMON_MATCHED_FILL_RESULTS,
leftOrder,
rightOrder,
constants.ZERO_AMOUNT,
constants.ZERO_AMOUNT,
Web3Wrapper.toBaseUnitAmount(15, 4),
Web3Wrapper.toBaseUnitAmount(1, 0),
);
});
});
});
// tslint:disable-line:max-file-line-count