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'; describe('Reference functions', () => { const ONE_ETHER = constants.ONE_ETHER; const DEFAULT_GAS_PRICE = new BigNumber(2); const DEFAULT_PROTOCOL_FEE_MULTIPLIER = new BigNumber(150); 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 { 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 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(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, DEFAULT_PROTOCOL_FEE_MULTIPLIER, DEFAULT_GAS_PRICE, ), ).to.throw(expectedError.message); }); it('reverts if computing `fillResults.makerFeePaid` overflows', () => { // 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.Uint256BinOpError( SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow, makerAssetFilledAmount, order.makerFee, ); return expect(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, DEFAULT_PROTOCOL_FEE_MULTIPLIER, DEFAULT_GAS_PRICE, ), ).to.throw(expectedError.message); }); it('reverts if computing `fillResults.takerFeePaid` overflows', () => { // 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(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, DEFAULT_PROTOCOL_FEE_MULTIPLIER, DEFAULT_GAS_PRICE, ), ).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(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, DEFAULT_PROTOCOL_FEE_MULTIPLIER, DEFAULT_GAS_PRICE, ), ).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(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, DEFAULT_PROTOCOL_FEE_MULTIPLIER, DEFAULT_GAS_PRICE, ), ).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(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, DEFAULT_PROTOCOL_FEE_MULTIPLIER, DEFAULT_GAS_PRICE, ), ).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(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, DEFAULT_PROTOCOL_FEE_MULTIPLIER, DEFAULT_GAS_PRICE, ), ).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(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, DEFAULT_PROTOCOL_FEE_MULTIPLIER, DEFAULT_GAS_PRICE, ), ).to.throw(expectedError.message); }); it('reverts if there is an overflow computing `protocolFeePaid`', () => { const order = makeOrder({ makerAssetAmount: ONE_ETHER, takerAssetAmount: ONE_ETHER, takerFee: constants.ZERO_AMOUNT, }); const takerAssetFilledAmount = order.takerAssetAmount.dividedToIntegerBy(3); const expectedError = new SafeMathRevertErrors.Uint256BinOpError( SafeMathRevertErrors.BinOpErrorCodes.MultiplicationOverflow, MAX_UINT256_ROOT, MAX_UINT256_ROOT, ); return expect(() => LibReferenceFunctions.calculateFillResults( order, takerAssetFilledAmount, MAX_UINT256_ROOT, MAX_UINT256_ROOT, ), ).to.throw(expectedError.message); }); }); }); // tslint:disable-line:max-file-line-count