@0x/contracts-exchange: Make assertValidFill and calculateMatchedFillResults public

This commit is contained in:
Lawrence Forman
2019-06-26 16:55:21 -04:00
committed by Amir Bandeali
parent 4791c120fe
commit 2f5a1eebe0
5 changed files with 132 additions and 93 deletions

View File

@@ -105,6 +105,10 @@
{
"note": "Add `validatorAddress` field to `SignatureValidatorError` rich reverts",
"pr": 1885
},
{
"note": "Make `assertValidFill` and `calculateMatchedFillResults` public",
"pr": 1885
}
]
},

View File

@@ -171,6 +171,66 @@ contract MixinExchangeCore is
return orderInfo;
}
/// @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
)
public
pure
{
// Revert if fill amount is invalid
// TODO: reconsider necessity for v2.1
if (takerAssetFillAmount == 0) {
_rrevert(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) {
_rrevert(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) {
_rrevert(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)) {
_rrevert(FillError(FillErrorCodes.INVALID_FILL_PRICE, orderInfo.orderHash));
}
}
/// @dev Fills the input order.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
@@ -203,7 +263,7 @@ contract MixinExchangeCore is
uint256 takerAssetFilledAmount = _min256(takerAssetFillAmount, remainingTakerAssetAmount);
// Validate context
_assertValidFill(
assertValidFill(
order,
orderInfo,
takerAssetFillAmount,
@@ -372,66 +432,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) {
_rrevert(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) {
_rrevert(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) {
_rrevert(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)) {
_rrevert(FillError(FillErrorCodes.INVALID_FILL_PRICE, orderInfo.orderHash));
}
}
/// @dev Validates context for cancelOrder. Succeeds or throws.
/// @param order to be cancelled.
/// @param orderInfo OrderStatus, orderHash, and amount already filled of order.

View File

@@ -81,7 +81,7 @@ contract MixinMatchOrders is
_assertValidMatch(leftOrder, rightOrder);
// Compute proportional fill amounts
matchedFillResults = _calculateMatchedFillResults(
matchedFillResults = calculateMatchedFillResults(
leftOrder,
rightOrder,
leftOrderInfo.orderTakerAssetFilledAmount,
@@ -89,14 +89,14 @@ contract MixinMatchOrders is
);
// Validate fill contexts
_assertValidFill(
assertValidFill(
leftOrder,
leftOrderInfo,
matchedFillResults.left.takerAssetFilledAmount,
matchedFillResults.left.takerAssetFilledAmount,
matchedFillResults.left.makerAssetFilledAmount
);
_assertValidFill(
assertValidFill(
rightOrder,
rightOrderInfo,
matchedFillResults.right.takerAssetFilledAmount,
@@ -133,33 +133,6 @@ contract MixinMatchOrders is
return matchedFillResults;
}
/// @dev Validates context for matchOrders. Succeeds or throws.
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
function _assertValidMatch(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder
)
internal
view
{
// Make sure there is a profitable spread.
// There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater
// than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount).
// This is satisfied by the equations below:
// <leftOrder.makerAssetAmount> / <leftOrder.takerAssetAmount> >= <rightOrder.takerAssetAmount> / <rightOrder.makerAssetAmount>
// AND
// <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount> >= <leftOrder.takerAssetAmount> / <leftOrder.makerAssetAmount>
// These equations can be combined to get the following:
if (_safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) <
_safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount)) {
_rrevert(NegativeSpreadError(
getOrderHash(leftOrder),
getOrderHash(rightOrder)
));
}
}
/// @dev Calculates fill amounts for the matched orders.
/// Each order is filled at their respective price point. However, the calculations are
/// carried out as though the orders are both being filled at the right order's price point.
@@ -169,13 +142,13 @@ contract MixinMatchOrders is
/// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.
/// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
function _calculateMatchedFillResults(
function calculateMatchedFillResults(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftOrderTakerAssetFilledAmount,
uint256 rightOrderTakerAssetFilledAmount
)
internal
public
pure
returns (LibFillResults.MatchedFillResults memory matchedFillResults)
{
@@ -262,6 +235,33 @@ contract MixinMatchOrders is
return matchedFillResults;
}
/// @dev Validates context for matchOrders. Succeeds or throws.
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
function _assertValidMatch(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder
)
internal
view
{
// Make sure there is a profitable spread.
// There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater
// than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount).
// This is satisfied by the equations below:
// <leftOrder.makerAssetAmount> / <leftOrder.takerAssetAmount> >= <rightOrder.takerAssetAmount> / <rightOrder.makerAssetAmount>
// AND
// <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount> >= <leftOrder.takerAssetAmount> / <leftOrder.makerAssetAmount>
// These equations can be combined to get the following:
if (_safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) <
_safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount)) {
_rrevert(NegativeSpreadError(
getOrderHash(leftOrder),
getOrderHash(rightOrder)
));
}
}
/// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.
/// @param leftOrderHash First matched order hash.
/// @param rightOrderHash Second matched order hash.

View File

@@ -99,4 +99,20 @@ contract IExchangeCore {
public
view
returns (bytes32 orderHash);
/// @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(
LibOrder.Order memory order,
LibOrder.OrderInfo memory orderInfo,
uint256 takerAssetFillAmount, // TODO: use FillResults
uint256 takerAssetFilledAmount,
uint256 makerAssetFilledAmount
)
public
pure;
}

View File

@@ -42,4 +42,23 @@ contract IMatchOrders {
)
public
returns (LibFillResults.MatchedFillResults memory matchedFillResults);
/// @dev Calculates fill amounts for the matched orders.
/// Each order is filled at their respective price point. However, the calculations are
/// carried out as though the orders are both being filled at the right order's price point.
/// The profit made by the leftOrder order goes to the taker (who matched the two orders).
/// @param leftOrder First order to match.
/// @param rightOrder Second order to match.
/// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.
/// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
function calculateMatchedFillResults(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftOrderTakerAssetFilledAmount,
uint256 rightOrderTakerAssetFilledAmount
)
public
pure
returns (LibFillResults.MatchedFillResults memory matchedFillResults);
}