Move FillResults calculations into LibFillResults

This commit is contained in:
Amir Bandeali
2019-07-24 14:46:50 -07:00
parent 242715240b
commit b05a2a90d0
4 changed files with 360 additions and 355 deletions

View File

@@ -17,12 +17,14 @@
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/SafeMath.sol";
import "./LibMath.sol";
import "./LibOrder.sol";
contract LibFillResults is
SafeMath
LibMath
{
struct BatchMatchedFillResults {
FillResults[] left; // Fill results for left orders
@@ -45,17 +47,358 @@ contract LibFillResults is
uint256 profitInRightMakerAsset; // Profit taken from the right maker
}
/// @dev Adds properties of both FillResults instances.
/// Modifies the first FillResults instance specified.
/// @param totalFillResults Fill results instance that will be added onto.
/// @param singleFillResults Fill results instance that will be added to totalFillResults.
function _addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)
/// @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.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function calculateFillResults(
LibOrder.Order memory order,
uint256 takerAssetFilledAmount
)
public
pure
returns (FillResults memory fillResults)
{
// Compute proportional transfer amounts
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
fillResults.makerAssetFilledAmount = _safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount
);
fillResults.makerFeePaid = _safeGetPartialAmountFloor(
fillResults.makerAssetFilledAmount,
order.makerAssetAmount,
order.makerFee
);
fillResults.takerFeePaid = _safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.takerFee
);
return fillResults;
}
/// @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 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.
function calculateMatchedFillResults(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftOrderTakerAssetFilledAmount,
uint256 rightOrderTakerAssetFilledAmount,
bool shouldMaximallyFillOrders
)
public
pure
returns (MatchedFillResults memory matchedFillResults)
{
// Derive maker asset amounts for left & right orders, given store taker assert amounts
uint256 leftTakerAssetAmountRemaining = _safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);
uint256 leftMakerAssetAmountRemaining = _safeGetPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
leftTakerAssetAmountRemaining
);
uint256 rightTakerAssetAmountRemaining = _safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);
uint256 rightMakerAssetAmountRemaining = _safeGetPartialAmountFloor(
rightOrder.makerAssetAmount,
rightOrder.takerAssetAmount,
rightTakerAssetAmountRemaining
);
// Maximally fill the orders and pay out profits to the matcher in one or both of the maker assets.
if (shouldMaximallyFillOrders) {
matchedFillResults = _calculateMatchedFillResultsWithMaximalFill(
leftOrder,
rightOrder,
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
} else {
matchedFillResults = _calculateMatchedFillResults(
leftOrder,
rightOrder,
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
}
// Compute fees for left order
matchedFillResults.left.makerFeePaid = _safeGetPartialAmountFloor(
matchedFillResults.left.makerAssetFilledAmount,
leftOrder.makerAssetAmount,
leftOrder.makerFee
);
matchedFillResults.left.takerFeePaid = _safeGetPartialAmountFloor(
matchedFillResults.left.takerAssetFilledAmount,
leftOrder.takerAssetAmount,
leftOrder.takerFee
);
// Compute fees for right order
matchedFillResults.right.makerFeePaid = _safeGetPartialAmountFloor(
matchedFillResults.right.makerAssetFilledAmount,
rightOrder.makerAssetAmount,
rightOrder.makerFee
);
matchedFillResults.right.takerFeePaid = _safeGetPartialAmountFloor(
matchedFillResults.right.takerAssetFilledAmount,
rightOrder.takerAssetAmount,
rightOrder.takerFee
);
// Return fill results
return matchedFillResults;
}
/// @dev Calculates part of the matched fill results for a given situation using the fill strategy that only
/// awards profit denominated in the left maker asset.
/// @param leftOrder The left order in the order matching situation.
/// @param rightOrder The right order in the order matching situation.
/// @param leftMakerAssetAmountRemaining The amount of the left order maker asset that can still be filled.
/// @param leftTakerAssetAmountRemaining The amount of the left order taker asset that can still be filled.
/// @param rightMakerAssetAmountRemaining The amount of the right order maker asset that can still be filled.
/// @param rightTakerAssetAmountRemaining The amount of the right order taker asset that can still be filled.
/// @return MatchFillResults struct that does not include fees paid.
function _calculateMatchedFillResults(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
internal
pure
returns (MatchedFillResults memory matchedFillResults)
{
totalFillResults.makerAssetFilledAmount = _safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount);
totalFillResults.takerAssetFilledAmount = _safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount);
totalFillResults.makerFeePaid = _safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);
totalFillResults.takerFeePaid = _safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);
// Calculate fill results for maker and taker assets: at least one order will be fully filled.
// The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`
// The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`
// We have two distinct cases for calculating the fill results:
// Case 1.
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
// If the left maker can buy exactly what the right maker can sell, then both orders are fully filled.
// Case 2.
// If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled.
// Case 3.
// If the left maker can buy exactly as much as the right maker can sell, then both orders are fully filled.
if (leftTakerAssetAmountRemaining > rightMakerAssetAmountRemaining) {
// Case 1: Right order is fully filled
matchedFillResults = _calculateCompleteRightFill(
leftOrder,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
} else if (leftTakerAssetAmountRemaining < rightMakerAssetAmountRemaining) {
// Case 2: Left order is fully filled
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
matchedFillResults.right.makerAssetFilledAmount = leftTakerAssetAmountRemaining;
// Round up to ensure the maker's exchange rate does not exceed the price specified by the order.
// We favor the maker when the exchange rate must be rounded.
matchedFillResults.right.takerAssetFilledAmount = _safeGetPartialAmountCeil(
rightOrder.takerAssetAmount,
rightOrder.makerAssetAmount,
leftTakerAssetAmountRemaining // matchedFillResults.right.makerAssetFilledAmount
);
} else {
// leftTakerAssetAmountRemaining == rightMakerAssetAmountRemaining
// Case 3: Both orders are fully filled. Technically, this could be captured by the above cases, but
// this calculation will be more precise since it does not include rounding.
matchedFillResults = _calculateCompleteFillBoth(
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
}
// Calculate amount given to taker
matchedFillResults.profitInLeftMakerAsset = _safeSub(
matchedFillResults.left.makerAssetFilledAmount,
matchedFillResults.right.takerAssetFilledAmount
);
return matchedFillResults;
}
/// @dev Calculates part of the matched fill results for a given situation using the maximal fill order matching
/// strategy.
/// @param leftOrder The left order in the order matching situation.
/// @param rightOrder The right order in the order matching situation.
/// @param leftMakerAssetAmountRemaining The amount of the left order maker asset that can still be filled.
/// @param leftTakerAssetAmountRemaining The amount of the left order taker asset that can still be filled.
/// @param rightMakerAssetAmountRemaining The amount of the right order maker asset that can still be filled.
/// @param rightTakerAssetAmountRemaining The amount of the right order taker asset that can still be filled.
/// @return MatchFillResults struct that does not include fees paid.
function _calculateMatchedFillResultsWithMaximalFill(
LibOrder.Order memory leftOrder,
LibOrder.Order memory rightOrder,
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
internal
pure
returns (MatchedFillResults memory matchedFillResults)
{
// If a maker asset is greater than the opposite taker asset, than there will be a spread denominated in that maker asset.
bool doesLeftMakerAssetProfitExist = leftMakerAssetAmountRemaining > rightTakerAssetAmountRemaining;
bool doesRightMakerAssetProfitExist = rightMakerAssetAmountRemaining > leftTakerAssetAmountRemaining;
// Calculate the maximum fill results for the maker and taker assets. At least one of the orders will be fully filled.
//
// The maximum that the left maker can possibly buy is the amount that the right order can sell.
// The maximum that the right maker can possibly buy is the amount that the left order can sell.
//
// If the left order is fully filled, profit will be paid out in the left maker asset. If the right order is fully filled,
// the profit will be out in the right maker asset.
//
// There are three cases to consider:
// Case 1.
// If the left maker can buy more than the right maker can sell, then only the right order is fully filled.
// Case 2.
// If the right maker can buy more than the left maker can sell, then only the right order is fully filled.
// Case 3.
// If the right maker can sell the max of what the left maker can buy and the left maker can sell the max of
// what the right maker can buy, then both orders are fully filled.
if (leftTakerAssetAmountRemaining > rightMakerAssetAmountRemaining) {
// Case 1: Right order is fully filled with the profit paid in the left makerAsset
matchedFillResults = _calculateCompleteRightFill(
leftOrder,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
} else if (rightTakerAssetAmountRemaining > leftMakerAssetAmountRemaining) {
// Case 2: Left order is fully filled with the profit paid in the right makerAsset.
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
// Round down to ensure the right maker's exchange rate does not exceed the price specified by the order.
// We favor the right maker when the exchange rate must be rounded and the profit is being paid in the
// right maker asset.
matchedFillResults.right.makerAssetFilledAmount = _safeGetPartialAmountFloor(
rightOrder.makerAssetAmount,
rightOrder.takerAssetAmount,
leftMakerAssetAmountRemaining
);
matchedFillResults.right.takerAssetFilledAmount = leftMakerAssetAmountRemaining;
} else {
// Case 3: The right and left orders are fully filled
matchedFillResults = _calculateCompleteFillBoth(
leftMakerAssetAmountRemaining,
leftTakerAssetAmountRemaining,
rightMakerAssetAmountRemaining,
rightTakerAssetAmountRemaining
);
}
// Calculate amount given to taker in the left order's maker asset if the left spread will be part of the profit.
if (doesLeftMakerAssetProfitExist) {
matchedFillResults.profitInLeftMakerAsset = _safeSub(
matchedFillResults.left.makerAssetFilledAmount,
matchedFillResults.right.takerAssetFilledAmount
);
}
// Calculate amount given to taker in the right order's maker asset if the right spread will be part of the profit.
if (doesRightMakerAssetProfitExist) {
matchedFillResults.profitInRightMakerAsset = _safeSub(
matchedFillResults.right.makerAssetFilledAmount,
matchedFillResults.left.takerAssetFilledAmount
);
}
return matchedFillResults;
}
/// @dev Calculates the fill results for the maker and taker in the order matching and writes the results
/// to the fillResults that are being collected on the order. Both orders will be fully filled in this
/// case.
/// @param leftMakerAssetAmountRemaining The amount of the left maker asset that is remaining to be filled.
/// @param leftTakerAssetAmountRemaining The amount of the left taker asset that is remaining to be filled.
/// @param rightMakerAssetAmountRemaining The amount of the right maker asset that is remaining to be filled.
/// @param rightTakerAssetAmountRemaining The amount of the right taker asset that is remaining to be filled.
/// @return MatchFillResults struct that does not include fees paid or spreads taken.
function _calculateCompleteFillBoth(
uint256 leftMakerAssetAmountRemaining,
uint256 leftTakerAssetAmountRemaining,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
internal
pure
returns (MatchedFillResults memory matchedFillResults)
{
// Calculate the fully filled results for both orders.
matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
return matchedFillResults;
}
/// @dev Calculates the fill results for the maker and taker in the order matching and writes the results
/// to the fillResults that are being collected on the order.
/// @param leftOrder The left order that is being maximally filled. All of the information about fill amounts
/// can be derived from this order and the right asset remaining fields.
/// @param rightMakerAssetAmountRemaining The amount of the right maker asset that is remaining to be filled.
/// @param rightTakerAssetAmountRemaining The amount of the right taker asset that is remaining to be filled.
/// @return MatchFillResults struct that does not include fees paid or spreads taken.
function _calculateCompleteRightFill(
LibOrder.Order memory leftOrder,
uint256 rightMakerAssetAmountRemaining,
uint256 rightTakerAssetAmountRemaining
)
internal
pure
returns (MatchedFillResults memory matchedFillResults)
{
matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;
matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;
matchedFillResults.left.takerAssetFilledAmount = rightMakerAssetAmountRemaining;
// Round down to ensure the left maker's exchange rate does not exceed the price specified by the order.
// We favor the left maker when the exchange rate must be rounded and the profit is being paid in the
// left maker asset.
matchedFillResults.left.makerAssetFilledAmount = _safeGetPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
rightMakerAssetAmountRemaining
);
return matchedFillResults;
}
/// @dev Adds properties of both FillResults instances.
/// @param fillResults1 The first FillResults.
/// @param fillResults2 The second FillResults.
/// @return The sum of both fill results.
function _addFillResults(FillResults memory fillResults1, FillResults memory fillResults2)
internal
pure
returns (FillResults memory totalFillResults)
{
totalFillResults.makerAssetFilledAmount = _safeAdd(fillResults1.makerAssetFilledAmount, fillResults2.makerAssetFilledAmount);
totalFillResults.takerAssetFilledAmount = _safeAdd(fillResults1.takerAssetFilledAmount, fillResults2.takerAssetFilledAmount);
totalFillResults.makerFeePaid = _safeAdd(fillResults1.makerFeePaid, fillResults2.makerFeePaid);
totalFillResults.takerFeePaid = _safeAdd(fillResults1.takerFeePaid, fillResults2.takerFeePaid);
return totalFillResults;
}
}