@0x:contracts-exchange Addressed review feedback
This commit is contained in:
@@ -52,13 +52,18 @@ library LibFillResults {
|
||||
/// @dev Calculates amounts filled and fees paid by maker and taker.
|
||||
/// @param order to be filled.
|
||||
/// @param takerAssetFilledAmount Amount of takerAsset that will be filled.
|
||||
/// @param protocolFeeMultiplier The current protocol fee of the exchange contract.
|
||||
/// @param gasPrice The gasprice of the transaction. This is provided so that the function call can continue
|
||||
/// to be pure rather than view.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function calculateFillResults(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFilledAmount
|
||||
uint256 takerAssetFilledAmount,
|
||||
uint256 protocolFeeMultiplier,
|
||||
uint256 gasPrice
|
||||
)
|
||||
internal
|
||||
view
|
||||
pure
|
||||
returns (FillResults memory fillResults)
|
||||
{
|
||||
// Compute proportional transfer amounts
|
||||
@@ -79,6 +84,9 @@ library LibFillResults {
|
||||
order.takerFee
|
||||
);
|
||||
|
||||
// Compute the protocol fee that should be paid for a single fill.
|
||||
fillResults.protocolFeePaid = gasPrice.safeMul(protocolFeeMultiplier);
|
||||
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
@@ -90,6 +98,9 @@ library LibFillResults {
|
||||
/// @param rightOrder Second order to match.
|
||||
/// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.
|
||||
/// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.
|
||||
/// @param protocolFeeMultiplier The current protocol fee of the exchange contract.
|
||||
/// @param gasPrice The gasprice of the transaction. This is provided so that the function call can continue
|
||||
/// to be pure rather than view.
|
||||
/// @param shouldMaximallyFillOrders A value that indicates whether or not this calculation should use
|
||||
/// the maximal fill order matching strategy.
|
||||
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
|
||||
@@ -98,10 +109,12 @@ library LibFillResults {
|
||||
LibOrder.Order memory rightOrder,
|
||||
uint256 leftOrderTakerAssetFilledAmount,
|
||||
uint256 rightOrderTakerAssetFilledAmount,
|
||||
uint256 protocolFeeMultiplier,
|
||||
uint256 gasPrice,
|
||||
bool shouldMaximallyFillOrders
|
||||
)
|
||||
internal
|
||||
view
|
||||
pure
|
||||
returns (MatchedFillResults memory matchedFillResults)
|
||||
{
|
||||
// Derive maker asset amounts for left & right orders, given store taker assert amounts
|
||||
@@ -163,6 +176,12 @@ library LibFillResults {
|
||||
rightOrder.takerFee
|
||||
);
|
||||
|
||||
// Compute the protocol fee that should be paid for a single fill. In this
|
||||
// case this should be made the protocol fee for both the left and right orders.
|
||||
uint256 protocolFee = gasPrice.safeMul(protocolFeeMultiplier);
|
||||
matchedFillResults.left.protocolFeePaid = protocolFee;
|
||||
matchedFillResults.right.protocolFeePaid = protocolFee;
|
||||
|
||||
// Return fill results
|
||||
return matchedFillResults;
|
||||
}
|
||||
|
||||
@@ -29,13 +29,20 @@ contract TestLibFillResults {
|
||||
|
||||
function calculateFillResults(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFilledAmount
|
||||
uint256 takerAssetFilledAmount,
|
||||
uint256 protocolFeeMultiplier,
|
||||
uint256 gasPrice
|
||||
)
|
||||
public
|
||||
view
|
||||
pure
|
||||
returns (LibFillResults.FillResults memory fillResults)
|
||||
{
|
||||
fillResults = LibFillResults.calculateFillResults(order, takerAssetFilledAmount);
|
||||
fillResults = LibFillResults.calculateFillResults(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
protocolFeeMultiplier,
|
||||
gasPrice
|
||||
);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
@@ -44,10 +51,12 @@ contract TestLibFillResults {
|
||||
LibOrder.Order memory rightOrder,
|
||||
uint256 leftOrderTakerAssetFilledAmount,
|
||||
uint256 rightOrderTakerAssetFilledAmount,
|
||||
uint256 protocolFeeMultiplier,
|
||||
uint256 gasPrice,
|
||||
bool shouldMaximallyFillOrders
|
||||
)
|
||||
public
|
||||
view
|
||||
pure
|
||||
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
|
||||
{
|
||||
matchedFillResults = LibFillResults.calculateMatchedFillResults(
|
||||
@@ -55,6 +64,8 @@ contract TestLibFillResults {
|
||||
rightOrder,
|
||||
leftOrderTakerAssetFilledAmount,
|
||||
rightOrderTakerAssetFilledAmount,
|
||||
protocolFeeMultiplier,
|
||||
gasPrice,
|
||||
shouldMaximallyFillOrders
|
||||
);
|
||||
return matchedFillResults;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { constants } from '@0x/contracts-test-utils';
|
||||
import { ReferenceFunctions } from '@0x/contracts-utils';
|
||||
import { LibMathRevertErrors } from '@0x/order-utils';
|
||||
import { FillResults, OrderWithoutDomain } from '@0x/types';
|
||||
@@ -94,7 +93,12 @@ export function addFillResults(a: FillResults, b: FillResults): FillResults {
|
||||
/**
|
||||
* Calculates amounts filled and fees paid by maker and taker.
|
||||
*/
|
||||
export function calculateFillResults(order: OrderWithoutDomain, takerAssetFilledAmount: BigNumber): FillResults {
|
||||
export function calculateFillResults(
|
||||
order: OrderWithoutDomain,
|
||||
takerAssetFilledAmount: BigNumber,
|
||||
protocolFeeMultiplier: BigNumber,
|
||||
gasPrice: BigNumber,
|
||||
): FillResults {
|
||||
const makerAssetFilledAmount = safeGetPartialAmountFloor(
|
||||
takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
@@ -107,6 +111,6 @@ export function calculateFillResults(order: OrderWithoutDomain, takerAssetFilled
|
||||
takerAssetFilledAmount,
|
||||
makerFeePaid,
|
||||
takerFeePaid,
|
||||
protocolFeePaid: constants.ZERO_AMOUNT, // This field is not calculated in `calculateFillResults` as a gas optimization.
|
||||
protocolFeePaid: safeMul(protocolFeeMultiplier, gasPrice),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,6 +93,8 @@ blockchainTests('LibFillResults', env => {
|
||||
return ReferenceFunctions.calculateFillResults(
|
||||
makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount),
|
||||
takerAssetFilledAmount,
|
||||
takerAssetFilledAmount, // Using this so that the gas price is distinct from protocolFeeMultiplier
|
||||
otherAmount,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,7 +104,12 @@ blockchainTests('LibFillResults', env => {
|
||||
otherAmount: BigNumber,
|
||||
): Promise<FillResults> {
|
||||
const order = makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount);
|
||||
return libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount);
|
||||
return libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
takerAssetFilledAmount, // Using this so that the gas price is distinct from protocolFeeMultiplier
|
||||
otherAmount,
|
||||
);
|
||||
}
|
||||
|
||||
testCombinatoriallyWithReferenceFunc(
|
||||
@@ -115,6 +122,9 @@ blockchainTests('LibFillResults', env => {
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -127,8 +137,18 @@ blockchainTests('LibFillResults', env => {
|
||||
takerFee: ONE_ETHER.times(0.0025),
|
||||
});
|
||||
const takerAssetFilledAmount = ONE_ETHER.dividedToIntegerBy(3);
|
||||
const expected = ReferenceFunctions.calculateFillResults(order, takerAssetFilledAmount);
|
||||
const actual = await libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount);
|
||||
const expected = ReferenceFunctions.calculateFillResults(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
);
|
||||
const actual = await libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
);
|
||||
expect(actual).to.deep.eq(expected);
|
||||
});
|
||||
|
||||
@@ -144,9 +164,14 @@ blockchainTests('LibFillResults', env => {
|
||||
takerAssetFilledAmount,
|
||||
order.makerAssetAmount,
|
||||
);
|
||||
return expect(libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount)).to.revertWith(
|
||||
expectedError,
|
||||
);
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('reverts if computing `fillResults.makerFeePaid` overflows', async () => {
|
||||
@@ -167,9 +192,14 @@ blockchainTests('LibFillResults', env => {
|
||||
makerAssetFilledAmount,
|
||||
order.makerFee,
|
||||
);
|
||||
return expect(libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount)).to.revertWith(
|
||||
expectedError,
|
||||
);
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('reverts if computing `fillResults.takerFeePaid` overflows', async () => {
|
||||
@@ -185,9 +215,14 @@ blockchainTests('LibFillResults', env => {
|
||||
takerAssetFilledAmount,
|
||||
order.takerFee,
|
||||
);
|
||||
return expect(libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount)).to.revertWith(
|
||||
expectedError,
|
||||
);
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('reverts if `order.makerAssetAmount` is 0', async () => {
|
||||
@@ -197,9 +232,14 @@ blockchainTests('LibFillResults', env => {
|
||||
});
|
||||
const takerAssetFilledAmount = ONE_ETHER;
|
||||
const expectedError = new LibMathRevertErrors.DivisionByZeroError();
|
||||
return expect(libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount)).to.revertWith(
|
||||
expectedError,
|
||||
);
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('reverts if `order.takerAssetAmount` is 0', async () => {
|
||||
@@ -209,9 +249,14 @@ blockchainTests('LibFillResults', env => {
|
||||
});
|
||||
const takerAssetFilledAmount = ONE_ETHER;
|
||||
const expectedError = new LibMathRevertErrors.DivisionByZeroError();
|
||||
return expect(libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount)).to.revertWith(
|
||||
expectedError,
|
||||
);
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('reverts if there is a rounding error computing `makerAsssetFilledAmount`', async () => {
|
||||
@@ -225,9 +270,14 @@ blockchainTests('LibFillResults', env => {
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount,
|
||||
);
|
||||
return expect(libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount)).to.revertWith(
|
||||
expectedError,
|
||||
);
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('reverts if there is a rounding error computing `makerFeePaid`', async () => {
|
||||
@@ -247,9 +297,91 @@ blockchainTests('LibFillResults', env => {
|
||||
order.makerAssetAmount,
|
||||
order.makerFee,
|
||||
);
|
||||
return expect(libsContract.calculateFillResults.callAsync(order, takerAssetFilledAmount)).to.revertWith(
|
||||
expectedError,
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).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 = ReferenceFunctions.getPartialAmountFloor(
|
||||
takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount,
|
||||
);
|
||||
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||
makerAssetFilledAmount,
|
||||
order.makerAssetAmount,
|
||||
order.takerFee,
|
||||
);
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).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.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
MAX_UINT256,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).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 = ReferenceFunctions.getPartialAmountFloor(
|
||||
takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount,
|
||||
);
|
||||
const expectedError = new LibMathRevertErrors.RoundingError(
|
||||
makerAssetFilledAmount,
|
||||
order.makerAssetAmount,
|
||||
order.makerFee,
|
||||
);
|
||||
return expect(
|
||||
libsContract.calculateFillResults.callAsync(
|
||||
order,
|
||||
takerAssetFilledAmount,
|
||||
DEFAULT_PROTOCOL_FEE_MULTIPLIER,
|
||||
DEFAULT_GAS_PRICE,
|
||||
),
|
||||
).to.revertWith(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -358,14 +490,14 @@ blockchainTests('LibFillResults', env => {
|
||||
takerAssetFilledAmount: Web3Wrapper.toBaseUnitAmount(10, 18),
|
||||
makerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
|
||||
takerFeePaid: Web3Wrapper.toBaseUnitAmount(100, 16),
|
||||
protocolFeePaid: constants.ZERO_AMOUNT,
|
||||
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: constants.ZERO_AMOUNT,
|
||||
protocolFeePaid: Web3Wrapper.toBaseUnitAmount(15, 4),
|
||||
},
|
||||
profitInLeftMakerAsset: Web3Wrapper.toBaseUnitAmount(3, 18),
|
||||
profitInRightMakerAsset: constants.ZERO_AMOUNT,
|
||||
@@ -401,6 +533,8 @@ blockchainTests('LibFillResults', env => {
|
||||
rightOrder,
|
||||
leftOrderTakerAssetFilledAmount,
|
||||
rightOrderTakerAssetFilledAmount,
|
||||
protocolFeeMultiplier,
|
||||
gasPrice,
|
||||
false,
|
||||
{ from },
|
||||
);
|
||||
@@ -1072,6 +1206,8 @@ blockchainTests('LibFillResults', env => {
|
||||
rightOrder,
|
||||
leftOrderTakerAssetFilledAmount,
|
||||
rightOrderTakerAssetFilledAmount,
|
||||
protocolFeeMultiplier,
|
||||
gasPrice,
|
||||
true,
|
||||
{ from },
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user