@0x/contracts-exchange
: Remove _assertValidFill()
.
`@0x/contracts-exchange`: Add `_settleOrder()` unit tests. `@0x/contracts-exchange`: Add explicit tests for `_calculateFillResults()`. `@0x/contracts-exchange`: Add overflow tests to `isolated_fill_order` tests. `@0x/contracts-exchange`: Add explicit `takerAssetFillAmount = 0` test to `isolated_fill_order` tests.
This commit is contained in:
parent
293510c087
commit
4711ce5532
@ -133,6 +133,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Remove functions from `TestExchangeInternals.sol` that are no longer tested in this package.",
|
"note": "Remove functions from `TestExchangeInternals.sol` that are no longer tested in this package.",
|
||||||
"pr": "TODO"
|
"pr": "TODO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove `_assertValidFill()`",
|
||||||
|
"pr": "TODO"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -210,15 +210,6 @@ contract MixinExchangeCore is
|
|||||||
uint256 remainingTakerAssetAmount = _safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
|
uint256 remainingTakerAssetAmount = _safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
|
||||||
uint256 takerAssetFilledAmount = _min256(takerAssetFillAmount, remainingTakerAssetAmount);
|
uint256 takerAssetFilledAmount = _min256(takerAssetFillAmount, remainingTakerAssetAmount);
|
||||||
|
|
||||||
// Validate context
|
|
||||||
_assertValidFill(
|
|
||||||
order,
|
|
||||||
orderInfo,
|
|
||||||
takerAssetFillAmount,
|
|
||||||
takerAssetFilledAmount,
|
|
||||||
fillResults.makerAssetFilledAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compute proportional fill amounts
|
// Compute proportional fill amounts
|
||||||
fillResults = _calculateFillResults(order, takerAssetFilledAmount);
|
fillResults = _calculateFillResults(order, takerAssetFilledAmount);
|
||||||
|
|
||||||
@ -389,78 +380,6 @@ contract MixinExchangeCore is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Validates context for fillOrder. Succeeds or throws.
|
|
||||||
/// @param order to be filled.
|
|
||||||
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
|
|
||||||
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
|
|
||||||
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
|
|
||||||
/// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.
|
|
||||||
function _assertValidFill(
|
|
||||||
Order memory order,
|
|
||||||
OrderInfo memory orderInfo,
|
|
||||||
uint256 takerAssetFillAmount, // TODO: use FillResults
|
|
||||||
uint256 takerAssetFilledAmount,
|
|
||||||
uint256 makerAssetFilledAmount
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
pure
|
|
||||||
{
|
|
||||||
// Revert if fill amount is invalid
|
|
||||||
// TODO: reconsider necessity for v2.1
|
|
||||||
if (takerAssetFillAmount == 0) {
|
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.FillError(
|
|
||||||
FillErrorCodes.INVALID_TAKER_AMOUNT,
|
|
||||||
orderInfo.orderHash
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure taker does not pay more than desired amount
|
|
||||||
// NOTE: This assertion should never fail, it is here
|
|
||||||
// as an extra defence against potential bugs.
|
|
||||||
if (takerAssetFilledAmount > takerAssetFillAmount) {
|
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.FillError(
|
|
||||||
FillErrorCodes.TAKER_OVERPAY,
|
|
||||||
orderInfo.orderHash
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure order is not overfilled
|
|
||||||
// NOTE: This assertion should never fail, it is here
|
|
||||||
// as an extra defence against potential bugs.
|
|
||||||
if (_safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount)
|
|
||||||
> order.takerAssetAmount) {
|
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.FillError(
|
|
||||||
FillErrorCodes.OVERFILL,
|
|
||||||
orderInfo.orderHash
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure order is filled at acceptable price.
|
|
||||||
// The order has an implied price from the makers perspective:
|
|
||||||
// order price = order.makerAssetAmount / order.takerAssetAmount
|
|
||||||
// i.e. the number of makerAsset maker is paying per takerAsset. The
|
|
||||||
// maker is guaranteed to get this price or a better (lower) one. The
|
|
||||||
// actual price maker is getting in this fill is:
|
|
||||||
// fill price = makerAssetFilledAmount / takerAssetFilledAmount
|
|
||||||
// We need `fill price <= order price` for the fill to be fair to maker.
|
|
||||||
// This amounts to:
|
|
||||||
// makerAssetFilledAmount order.makerAssetAmount
|
|
||||||
// ------------------------ <= -----------------------
|
|
||||||
// takerAssetFilledAmount order.takerAssetAmount
|
|
||||||
// or, equivalently:
|
|
||||||
// makerAssetFilledAmount * order.takerAssetAmount <=
|
|
||||||
// order.makerAssetAmount * takerAssetFilledAmount
|
|
||||||
// NOTE: This assertion should never fail, it is here
|
|
||||||
// as an extra defence against potential bugs.
|
|
||||||
if (_safeMul(makerAssetFilledAmount, order.takerAssetAmount)
|
|
||||||
> _safeMul(order.makerAssetAmount, takerAssetFilledAmount)) {
|
|
||||||
LibRichErrors._rrevert(LibExchangeRichErrors.FillError(
|
|
||||||
FillErrorCodes.INVALID_FILL_PRICE,
|
|
||||||
orderInfo.orderHash
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Validates context for cancelOrder. Succeeds or throws.
|
/// @dev Validates context for cancelOrder. Succeeds or throws.
|
||||||
/// @param order to be cancelled.
|
/// @param order to be cancelled.
|
||||||
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
|
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.
|
||||||
|
@ -680,22 +680,6 @@ contract MixinMatchOrders is
|
|||||||
shouldMaximallyFillOrders
|
shouldMaximallyFillOrders
|
||||||
);
|
);
|
||||||
|
|
||||||
// Validate fill contexts
|
|
||||||
_assertValidFill(
|
|
||||||
leftOrder,
|
|
||||||
leftOrderInfo,
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount,
|
|
||||||
matchedFillResults.left.takerAssetFilledAmount,
|
|
||||||
matchedFillResults.left.makerAssetFilledAmount
|
|
||||||
);
|
|
||||||
_assertValidFill(
|
|
||||||
rightOrder,
|
|
||||||
rightOrderInfo,
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount,
|
|
||||||
matchedFillResults.right.takerAssetFilledAmount,
|
|
||||||
matchedFillResults.right.makerAssetFilledAmount
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update exchange state
|
// Update exchange state
|
||||||
_updateFilledState(
|
_updateFilledState(
|
||||||
leftOrder,
|
leftOrder,
|
||||||
|
@ -26,6 +26,14 @@ import "../src/Exchange.sol";
|
|||||||
contract TestExchangeInternals is
|
contract TestExchangeInternals is
|
||||||
Exchange
|
Exchange
|
||||||
{
|
{
|
||||||
|
event DispatchTransferFromCalled(
|
||||||
|
bytes32 orderHash,
|
||||||
|
bytes assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
constructor (uint256 chainId)
|
constructor (uint256 chainId)
|
||||||
public
|
public
|
||||||
Exchange(chainId)
|
Exchange(chainId)
|
||||||
@ -62,4 +70,34 @@ contract TestExchangeInternals is
|
|||||||
fillResults
|
fillResults
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function settleOrder(
|
||||||
|
bytes32 orderHash,
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
address takerAddress,
|
||||||
|
LibFillResults.FillResults memory fillResults
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
_settleOrder(orderHash, order, takerAddress, fillResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Overidden to only log arguments so we can test `_settleOrder()`.
|
||||||
|
function _dispatchTransferFrom(
|
||||||
|
bytes32 orderHash,
|
||||||
|
bytes memory assetData,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
emit DispatchTransferFromCalled(
|
||||||
|
orderHash,
|
||||||
|
assetData,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,15 +189,7 @@ blockchainTests.resets('FillOrder Tests', ({ web3Wrapper, txDefaults }) => {
|
|||||||
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
|
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should revert if takerAssetFillAmount is 0', async () => {
|
it('should throw if an order is expired', async () => {
|
||||||
const fillScenario = {
|
|
||||||
...defaultFillScenario,
|
|
||||||
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.Zero,
|
|
||||||
};
|
|
||||||
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should revert if an order is expired', async () => {
|
|
||||||
const fillScenario = {
|
const fillScenario = {
|
||||||
...defaultFillScenario,
|
...defaultFillScenario,
|
||||||
orderScenario: {
|
orderScenario: {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
|
||||||
import {
|
import {
|
||||||
blockchainTests,
|
blockchainTests,
|
||||||
constants,
|
constants,
|
||||||
@ -8,6 +9,7 @@ import {
|
|||||||
testCombinatoriallyWithReferenceFunc,
|
testCombinatoriallyWithReferenceFunc,
|
||||||
uint256Values,
|
uint256Values,
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { LibMathRevertErrors } from '@0x/order-utils';
|
||||||
import { FillResults, OrderWithoutDomain as Order } from '@0x/types';
|
import { FillResults, OrderWithoutDomain as Order } from '@0x/types';
|
||||||
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
||||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
import { LogWithDecodedArgs } from 'ethereum-types';
|
||||||
@ -18,11 +20,12 @@ import {
|
|||||||
ReferenceFunctions,
|
ReferenceFunctions,
|
||||||
TestExchangeInternalsContract,
|
TestExchangeInternalsContract,
|
||||||
TestExchangeInternalsFillEventArgs,
|
TestExchangeInternalsFillEventArgs,
|
||||||
|
TestExchangeInternalsDispatchTransferFromCalledEventArgs,
|
||||||
} from '../src';
|
} from '../src';
|
||||||
|
|
||||||
// TODO(dorothy-zbornak): Add _settleOrder
|
blockchainTests('Exchange core internal functions', env => {
|
||||||
blockchainTests.only('Exchange core internal functions', env => {
|
|
||||||
const CHAIN_ID = 1337;
|
const CHAIN_ID = 1337;
|
||||||
|
const ONE_ETHER = constants.ONE_ETHER;
|
||||||
const EMPTY_ORDER: Order = {
|
const EMPTY_ORDER: Order = {
|
||||||
senderAddress: constants.NULL_ADDRESS,
|
senderAddress: constants.NULL_ADDRESS,
|
||||||
makerAddress: constants.NULL_ADDRESS,
|
makerAddress: constants.NULL_ADDRESS,
|
||||||
@ -39,6 +42,9 @@ blockchainTests.only('Exchange core internal functions', env => {
|
|||||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||||
expirationTimeSeconds: constants.ZERO_AMOUNT,
|
expirationTimeSeconds: constants.ZERO_AMOUNT,
|
||||||
};
|
};
|
||||||
|
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
||||||
|
const randomHash = () => hexRandom(constants.WORD_LENGTH);
|
||||||
|
const randomAssetData = () => hexRandom(36);
|
||||||
let testExchange: TestExchangeInternalsContract;
|
let testExchange: TestExchangeInternalsContract;
|
||||||
let logDecoder: LogDecoder;
|
let logDecoder: LogDecoder;
|
||||||
let senderAddress: string;
|
let senderAddress: string;
|
||||||
@ -54,49 +60,49 @@ blockchainTests.only('Exchange core internal functions', env => {
|
|||||||
logDecoder = new LogDecoder(env.web3Wrapper, artifacts);
|
logDecoder = new LogDecoder(env.web3Wrapper, artifacts);
|
||||||
});
|
});
|
||||||
|
|
||||||
blockchainTests('calculateFillResults', async () => {
|
blockchainTests('calculateFillResults', () => {
|
||||||
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 ReferenceFunctions.calculateFillResults(
|
|
||||||
makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount),
|
|
||||||
takerAssetFilledAmount,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testCalculateFillResultsAsync(
|
|
||||||
orderTakerAssetAmount: BigNumber,
|
|
||||||
takerAssetFilledAmount: BigNumber,
|
|
||||||
otherAmount: BigNumber,
|
|
||||||
): Promise<FillResults> {
|
|
||||||
const order = makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount);
|
|
||||||
return testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe.optional('combinatorial tests', () => {
|
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 ReferenceFunctions.calculateFillResults(
|
||||||
|
makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount),
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testCalculateFillResultsAsync(
|
||||||
|
orderTakerAssetAmount: BigNumber,
|
||||||
|
takerAssetFilledAmount: BigNumber,
|
||||||
|
otherAmount: BigNumber,
|
||||||
|
): Promise<FillResults> {
|
||||||
|
const order = makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount);
|
||||||
|
return testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount);
|
||||||
|
}
|
||||||
|
|
||||||
testCombinatoriallyWithReferenceFunc(
|
testCombinatoriallyWithReferenceFunc(
|
||||||
'calculateFillResults',
|
'calculateFillResults',
|
||||||
referenceCalculateFillResultsAsync,
|
referenceCalculateFillResultsAsync,
|
||||||
@ -104,13 +110,167 @@ blockchainTests.only('Exchange core internal functions', env => {
|
|||||||
[uint256Values, uint256Values, uint256Values],
|
[uint256Values, uint256Values, uint256Values],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('explicit tests', () => {
|
||||||
|
const MAX_UINT256_ROOT = constants.MAX_UINT256_ROOT;
|
||||||
|
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 = ReferenceFunctions.calculateFillResults(order, takerAssetFilledAmount);
|
||||||
|
const actual = await testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount);
|
||||||
|
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.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
return expect(testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount))
|
||||||
|
.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 = LibReferenceFunctions.getPartialAmountFloor(
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
const expectedError = new SafeMathRevertErrors.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
order.makerFee,
|
||||||
|
);
|
||||||
|
return expect(testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount))
|
||||||
|
.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.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerFee,
|
||||||
|
);
|
||||||
|
return expect(testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount))
|
||||||
|
.to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if `order.makerAssetAmount` is 0', async () => {
|
||||||
|
const order = makeOrder({
|
||||||
|
makerAssetAmount: constants.ZERO_AMOUNT,
|
||||||
|
takerAssetAmount: ONE_ETHER,
|
||||||
|
});
|
||||||
|
const takerAssetFilledAmount = ONE_ETHER;
|
||||||
|
const expectedError = new LibMathRevertErrors.DivisionByZeroError();
|
||||||
|
return expect(testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount))
|
||||||
|
.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(testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount))
|
||||||
|
.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(testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount))
|
||||||
|
.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 = LibReferenceFunctions.getPartialAmountFloor(
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
order.makerFee,
|
||||||
|
);
|
||||||
|
return expect(testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount))
|
||||||
|
.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 = LibReferenceFunctions.getPartialAmountFloor(
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
order.takerFee,
|
||||||
|
);
|
||||||
|
return expect(testExchange.calculateFillResults.callAsync(order, takerAssetFilledAmount))
|
||||||
|
.to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
blockchainTests.resets('updateFilledState', async () => {
|
blockchainTests.resets('updateFilledState', async () => {
|
||||||
const ONE_ETHER = new BigNumber(1e18);
|
|
||||||
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
|
||||||
const randomHash = () => hexRandom(constants.WORD_LENGTH);
|
|
||||||
const randomAssetData = () => hexRandom(36);
|
|
||||||
const ORDER_DEFAULTS = {
|
const ORDER_DEFAULTS = {
|
||||||
senderAddress: randomAddress(),
|
senderAddress: randomAddress(),
|
||||||
makerAddress: randomAddress(),
|
makerAddress: randomAddress(),
|
||||||
@ -129,11 +289,7 @@ blockchainTests.only('Exchange core internal functions', env => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function makeOrder(details?: Partial<Order>): Order {
|
function makeOrder(details?: Partial<Order>): Order {
|
||||||
return _.assign(
|
return _.assign({}, ORDER_DEFAULTS, details);
|
||||||
{},
|
|
||||||
ORDER_DEFAULTS,
|
|
||||||
details,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testUpdateFilledStateAsync(
|
async function testUpdateFilledStateAsync(
|
||||||
@ -192,7 +348,7 @@ blockchainTests.only('Exchange core internal functions', env => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws if `leftOrderTakerAssetFilledAmount + fillResults.takerAssetFilledAmount` overflows', async () => {
|
it('reverts if `leftOrderTakerAssetFilledAmount + fillResults.takerAssetFilledAmount` overflows', async () => {
|
||||||
const order = makeOrder();
|
const order = makeOrder();
|
||||||
const orderTakerAssetFilledAmount = constants.MAX_UINT256.dividedToIntegerBy(2);
|
const orderTakerAssetFilledAmount = constants.MAX_UINT256.dividedToIntegerBy(2);
|
||||||
const takerAssetFillAmount = constants.MAX_UINT256.dividedToIntegerBy(2).plus(2);
|
const takerAssetFillAmount = constants.MAX_UINT256.dividedToIntegerBy(2).plus(2);
|
||||||
@ -216,5 +372,71 @@ blockchainTests.only('Exchange core internal functions', env => {
|
|||||||
)).to.revertWith(expectedError);
|
)).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),
|
||||||
|
};
|
||||||
|
const receipt = await logDecoder.getTxWithDecodedLogsAsync(
|
||||||
|
await testExchange.settleOrder.sendTransactionAsync(
|
||||||
|
orderHash,
|
||||||
|
order,
|
||||||
|
takerAddress,
|
||||||
|
fillResults,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
// tslint:disable-line:max-file-line-count
|
// tslint:disable-line:max-file-line-count
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
|
||||||
import {
|
import {
|
||||||
blockchainTests,
|
blockchainTests,
|
||||||
constants,
|
constants,
|
||||||
@ -6,7 +7,7 @@ import {
|
|||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { ExchangeRevertErrors, LibMathRevertErrors } from '@0x/order-utils';
|
import { ExchangeRevertErrors, LibMathRevertErrors } from '@0x/order-utils';
|
||||||
import { FillResults, OrderInfo, OrderStatus, SignatureType } from '@0x/types';
|
import { FillResults, OrderInfo, OrderStatus, SignatureType } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { calculateFillResults } from '../src/reference_functions';
|
import { calculateFillResults } from '../src/reference_functions';
|
||||||
@ -24,8 +25,7 @@ import {
|
|||||||
blockchainTests('Isolated fillOrder() tests', env => {
|
blockchainTests('Isolated fillOrder() tests', env => {
|
||||||
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
const randomAddress = () => hexRandom(constants.ADDRESS_LENGTH);
|
||||||
const getCurrentTime = () => Math.floor(_.now() / 1000);
|
const getCurrentTime = () => Math.floor(_.now() / 1000);
|
||||||
const { ZERO_AMOUNT } = constants;
|
const { ZERO_AMOUNT, ONE_ETHER, MAX_UINT256_ROOT } = constants;
|
||||||
const ONE_ETHER = new BigNumber(10).pow(18);
|
|
||||||
const ONE_DAY = 60 * 60 * 24;
|
const ONE_DAY = 60 * 60 * 24;
|
||||||
const TOMORROW = getCurrentTime() + ONE_DAY;
|
const TOMORROW = getCurrentTime() + ONE_DAY;
|
||||||
const DEFAULT_ORDER: Order = {
|
const DEFAULT_ORDER: Order = {
|
||||||
@ -464,6 +464,61 @@ blockchainTests('Isolated fillOrder() tests', env => {
|
|||||||
.to.revertWith(expectedError);
|
.to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can\'t fill an order that results in a `makerAssetFilledAmount` overflow.', async () => {
|
||||||
|
// All values need to be large to ensure we don't trigger a Rounding.
|
||||||
|
const order = createOrder({
|
||||||
|
makerAssetAmount: MAX_UINT256_ROOT.times(2),
|
||||||
|
takerAssetAmount: MAX_UINT256_ROOT,
|
||||||
|
});
|
||||||
|
const takerAssetFillAmount = MAX_UINT256_ROOT;
|
||||||
|
const expectedError = new SafeMathRevertErrors.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
return expect(exchange.fillOrderAsync(order, takerAssetFillAmount))
|
||||||
|
.to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can\'t fill an order that results in a `makerFeePaid` overflow.', async () => {
|
||||||
|
// All values need to be large to ensure we don't trigger a Rounding.
|
||||||
|
const order = createOrder({
|
||||||
|
makerAssetAmount: MAX_UINT256_ROOT,
|
||||||
|
takerAssetAmount: MAX_UINT256_ROOT,
|
||||||
|
makerFee: MAX_UINT256_ROOT.times(11),
|
||||||
|
});
|
||||||
|
const takerAssetFillAmount = MAX_UINT256_ROOT.dividedToIntegerBy(10);
|
||||||
|
const makerAssetFilledAmount = LibReferenceFunctions.getPartialAmountFloor(
|
||||||
|
takerAssetFillAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
const expectedError = new SafeMathRevertErrors.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
order.makerFee,
|
||||||
|
);
|
||||||
|
return expect(exchange.fillOrderAsync(order, takerAssetFillAmount))
|
||||||
|
.to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can\'t fill an order that results in a `takerFeePaid` overflow.', async () => {
|
||||||
|
// All values need to be large to ensure we don't trigger a Rounding.
|
||||||
|
const order = createOrder({
|
||||||
|
makerAssetAmount: MAX_UINT256_ROOT,
|
||||||
|
takerAssetAmount: MAX_UINT256_ROOT,
|
||||||
|
takerFee: MAX_UINT256_ROOT.times(11),
|
||||||
|
});
|
||||||
|
const takerAssetFillAmount = MAX_UINT256_ROOT.dividedToIntegerBy(10);
|
||||||
|
const expectedError = new SafeMathRevertErrors.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
order.takerFee,
|
||||||
|
);
|
||||||
|
return expect(exchange.fillOrderAsync(order, takerAssetFillAmount))
|
||||||
|
.to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
|
||||||
it('can\'t fill an order with a bad signature', async () => {
|
it('can\'t fill an order with a bad signature', async () => {
|
||||||
const order = createOrder();
|
const order = createOrder();
|
||||||
const signature = createBadSignature();
|
const signature = createBadSignature();
|
||||||
@ -529,6 +584,11 @@ blockchainTests('Isolated fillOrder() tests', env => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('permitted fills', () => {
|
describe('permitted fills', () => {
|
||||||
|
it('should allow takerAssetFillAmount to be zero', async () => {
|
||||||
|
const order = createOrder();
|
||||||
|
return fillOrderAndAssertResultsAsync(order, constants.ZERO_AMOUNT);
|
||||||
|
});
|
||||||
|
|
||||||
it('can fill an order if taker is `takerAddress`', async () => {
|
it('can fill an order if taker is `takerAddress`', async () => {
|
||||||
const order = createOrder({
|
const order = createOrder({
|
||||||
takerAddress,
|
takerAddress,
|
||||||
|
178
contracts/exchange/test/reference_functions.ts
Normal file
178
contracts/exchange/test/reference_functions.ts
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
|
||||||
|
import {
|
||||||
|
constants,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { LibMathRevertErrors } from '@0x/order-utils';
|
||||||
|
import { OrderWithoutDomain as Order } from '@0x/types';
|
||||||
|
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { calculateFillResults } from '../src/reference_functions';
|
||||||
|
|
||||||
|
describe('Reference functions', () => {
|
||||||
|
const ONE_ETHER = constants.ONE_ETHER;
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('calculateFillResults', () => {
|
||||||
|
const MAX_UINT256_ROOT = constants.MAX_UINT256_ROOT;
|
||||||
|
function makeOrder(details?: Partial<Order>): Order {
|
||||||
|
return _.assign(
|
||||||
|
{},
|
||||||
|
EMPTY_ORDER,
|
||||||
|
details,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('reverts if computing `fillResults.makerAssetFilledAmount` overflows', () => {
|
||||||
|
// All values need to be large to ensure we don't trigger a RondingError.
|
||||||
|
const order = makeOrder({
|
||||||
|
makerAssetAmount: MAX_UINT256_ROOT.times(2),
|
||||||
|
takerAssetAmount: MAX_UINT256_ROOT,
|
||||||
|
});
|
||||||
|
const takerAssetFilledAmount = MAX_UINT256_ROOT;
|
||||||
|
const expectedError = new SafeMathRevertErrors.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
return expect(() => calculateFillResults(order, takerAssetFilledAmount))
|
||||||
|
.to.throw(expectedError.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if computing `fillResults.makerFeePaid` overflows', () => {
|
||||||
|
// All values need to be large to ensure we don't trigger a RondingError.
|
||||||
|
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 = LibReferenceFunctions.getPartialAmountFloor(
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
const expectedError = new SafeMathRevertErrors.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
order.makerFee,
|
||||||
|
);
|
||||||
|
return expect(() => calculateFillResults(order, takerAssetFilledAmount))
|
||||||
|
.to.throw(expectedError.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if computing `fillResults.takerFeePaid` overflows', () => {
|
||||||
|
// All values need to be large to ensure we don't trigger a RondingError.
|
||||||
|
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.SafeMathError(
|
||||||
|
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerFee,
|
||||||
|
);
|
||||||
|
return expect(() => calculateFillResults(order, takerAssetFilledAmount))
|
||||||
|
.to.throw(expectedError.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if `order.makerAssetAmount` is 0', () => {
|
||||||
|
const order = makeOrder({
|
||||||
|
makerAssetAmount: constants.ZERO_AMOUNT,
|
||||||
|
takerAssetAmount: ONE_ETHER,
|
||||||
|
});
|
||||||
|
const takerAssetFilledAmount = ONE_ETHER;
|
||||||
|
const expectedError = new LibMathRevertErrors.DivisionByZeroError();
|
||||||
|
return expect(() => calculateFillResults(order, takerAssetFilledAmount))
|
||||||
|
.to.throw(expectedError.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if `order.takerAssetAmount` is 0', () => {
|
||||||
|
const order = makeOrder({
|
||||||
|
makerAssetAmount: ONE_ETHER,
|
||||||
|
takerAssetAmount: constants.ZERO_AMOUNT,
|
||||||
|
});
|
||||||
|
const takerAssetFilledAmount = ONE_ETHER;
|
||||||
|
const expectedError = new LibMathRevertErrors.DivisionByZeroError();
|
||||||
|
return expect(() => calculateFillResults(order, takerAssetFilledAmount))
|
||||||
|
.to.throw(expectedError.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if there is a rounding error computing `makerAsssetFilledAmount`', () => {
|
||||||
|
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(() => calculateFillResults(order, takerAssetFilledAmount))
|
||||||
|
.to.throw(expectedError.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if there is a rounding error computing `makerFeePaid`', () => {
|
||||||
|
const order = makeOrder({
|
||||||
|
makerAssetAmount: ONE_ETHER,
|
||||||
|
takerAssetAmount: ONE_ETHER,
|
||||||
|
makerFee: new BigNumber(100),
|
||||||
|
});
|
||||||
|
const takerAssetFilledAmount = order.takerAssetAmount.dividedToIntegerBy(3);
|
||||||
|
const makerAssetFilledAmount = LibReferenceFunctions.getPartialAmountFloor(
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
order.makerFee,
|
||||||
|
);
|
||||||
|
return expect(() => calculateFillResults(order, takerAssetFilledAmount))
|
||||||
|
.to.throw(expectedError.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if there is a rounding error computing `takerFeePaid`', () => {
|
||||||
|
const order = makeOrder({
|
||||||
|
makerAssetAmount: ONE_ETHER,
|
||||||
|
takerAssetAmount: ONE_ETHER,
|
||||||
|
takerFee: new BigNumber(100),
|
||||||
|
});
|
||||||
|
const takerAssetFilledAmount = order.takerAssetAmount.dividedToIntegerBy(3);
|
||||||
|
const makerAssetFilledAmount = LibReferenceFunctions.getPartialAmountFloor(
|
||||||
|
takerAssetFilledAmount,
|
||||||
|
order.takerAssetAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
);
|
||||||
|
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
order.makerAssetAmount,
|
||||||
|
order.takerFee,
|
||||||
|
);
|
||||||
|
return expect(() => calculateFillResults(order, takerAssetFilledAmount))
|
||||||
|
.to.throw(expectedError.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// tslint:disable-line:max-file-line-count
|
Loading…
x
Reference in New Issue
Block a user