Atomic Order Matching - Smart Contracts.
This commit is contained in:
@@ -25,24 +25,28 @@ import "./MixinSettlement.sol";
|
||||
import "./MixinWrapperFunctions.sol";
|
||||
import "./MixinAssetProxyDispatcher.sol";
|
||||
import "./MixinTransactions.sol";
|
||||
import "./MixinMatchOrders.sol";
|
||||
|
||||
contract Exchange is
|
||||
MixinWrapperFunctions,
|
||||
MixinExchangeCore,
|
||||
MixinMatchOrders,
|
||||
MixinSettlement,
|
||||
MixinSignatureValidator,
|
||||
MixinTransactions,
|
||||
MixinAssetProxyDispatcher
|
||||
MixinAssetProxyDispatcher,
|
||||
MixinWrapperFunctions
|
||||
{
|
||||
|
||||
string constant public VERSION = "2.0.1-alpha";
|
||||
|
||||
constructor (bytes memory _zrxProxyData)
|
||||
public
|
||||
MixinExchangeCore()
|
||||
MixinSignatureValidator()
|
||||
MixinMatchOrders()
|
||||
MixinSettlement(_zrxProxyData)
|
||||
MixinWrapperFunctions()
|
||||
MixinAssetProxyDispatcher()
|
||||
MixinSignatureValidator()
|
||||
MixinTransactions()
|
||||
MixinAssetProxyDispatcher()
|
||||
MixinWrapperFunctions()
|
||||
{}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ pragma experimental ABIEncoderV2;
|
||||
import "./libs/LibFillResults.sol";
|
||||
import "./libs/LibOrder.sol";
|
||||
import "./libs/LibMath.sol";
|
||||
import "./libs/LibStatus.sol";
|
||||
import "./libs/LibExchangeErrors.sol";
|
||||
import "./mixins/MExchangeCore.sol";
|
||||
import "./mixins/MSettlement.sol";
|
||||
@@ -29,9 +30,11 @@ import "./mixins/MSignatureValidator.sol";
|
||||
import "./mixins/MTransactions.sol";
|
||||
|
||||
contract MixinExchangeCore is
|
||||
SafeMath,
|
||||
LibMath,
|
||||
LibStatus,
|
||||
LibOrder,
|
||||
LibFillResults,
|
||||
LibMath,
|
||||
LibExchangeErrors,
|
||||
MExchangeCore,
|
||||
MSettlement,
|
||||
@@ -48,6 +51,347 @@ contract MixinExchangeCore is
|
||||
// Orders with a salt less than their maker's epoch are considered cancelled
|
||||
mapping (address => uint256) public makerEpoch;
|
||||
|
||||
////// Core exchange functions //////
|
||||
|
||||
/// @dev Gets information about an order.
|
||||
/// @param order Order to gather information on.
|
||||
/// @return status Status of order. Statuses are defined in the LibStatus.Status struct.
|
||||
/// @return orderHash Keccak-256 EIP712 hash of the order.
|
||||
/// @return takerAssetFilledAmount Amount of order that has been filled.
|
||||
function getOrderInfo(Order memory order)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
uint8 orderStatus,
|
||||
bytes32 orderHash,
|
||||
uint256 takerAssetFilledAmount)
|
||||
{
|
||||
// Compute the order hash and fetch filled amount
|
||||
orderHash = getOrderHash(order);
|
||||
takerAssetFilledAmount = filled[orderHash];
|
||||
|
||||
// If order.takerAssetAmount is zero, then the order will always
|
||||
// be considered filled because 0 == takerAssetAmount == takerAssetFilledAmount
|
||||
// Instead of distinguishing between unfilled and filled zero taker
|
||||
// amount orders, we choose not to support them.
|
||||
if (order.takerAssetAmount == 0) {
|
||||
orderStatus = uint8(Status.ORDER_INVALID_TAKER_ASSET_AMOUNT);
|
||||
return (orderStatus, orderHash, takerAssetFilledAmount);
|
||||
}
|
||||
|
||||
// If order.makerAssetAmount is zero, we also reject the order.
|
||||
// While the Exchange contract handles them correctly, they create
|
||||
// edge cases in the supporting infrastructure because they have
|
||||
// an 'infinite' price when computed by a simple division.
|
||||
if (order.makerAssetAmount == 0) {
|
||||
orderStatus = uint8(Status.ORDER_INVALID_MAKER_ASSET_AMOUNT);
|
||||
return (orderStatus, orderHash, takerAssetFilledAmount);
|
||||
}
|
||||
|
||||
// Validate order expiration
|
||||
if (block.timestamp >= order.expirationTimeSeconds) {
|
||||
orderStatus = uint8(Status.ORDER_EXPIRED);
|
||||
return (orderStatus, orderHash, takerAssetFilledAmount);
|
||||
}
|
||||
|
||||
// Validate order availability
|
||||
if (takerAssetFilledAmount >= order.takerAssetAmount) {
|
||||
orderStatus = uint8(Status.ORDER_FULLY_FILLED);
|
||||
return (orderStatus, orderHash, takerAssetFilledAmount);
|
||||
}
|
||||
|
||||
// Check if order has been cancelled
|
||||
if (cancelled[orderHash]) {
|
||||
orderStatus = uint8(Status.ORDER_CANCELLED);
|
||||
return (orderStatus, orderHash, takerAssetFilledAmount);
|
||||
}
|
||||
|
||||
// Validate order is not cancelled
|
||||
if (makerEpoch[order.makerAddress] > order.salt) {
|
||||
orderStatus = uint8(Status.ORDER_CANCELLED);
|
||||
return (orderStatus, orderHash, takerAssetFilledAmount);
|
||||
}
|
||||
|
||||
// Order is Fillable
|
||||
orderStatus = uint8(Status.ORDER_FILLABLE);
|
||||
return (orderStatus, orderHash, takerAssetFilledAmount);
|
||||
}
|
||||
|
||||
/// @dev Validates context for fillOrder. Succeeds or throws.
|
||||
/// @param order to be filled.
|
||||
/// @param orderStatus Status of order to be filled.
|
||||
/// @param orderHash Hash of order to be filled.
|
||||
/// @param takerAssetFilledAmount Amount of order already filled.
|
||||
/// @param signature Proof that the orders was created by its maker.
|
||||
/// @param takerAddress Address of order taker.
|
||||
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
|
||||
function validateFillOrderContextOrRevert(
|
||||
Order memory order,
|
||||
uint8 orderStatus,
|
||||
bytes32 orderHash,
|
||||
uint256 takerAssetFilledAmount,
|
||||
bytes memory signature,
|
||||
address takerAddress,
|
||||
uint256 takerAssetFillAmount)
|
||||
internal
|
||||
{
|
||||
// Ensure order is valid
|
||||
require(
|
||||
orderStatus != uint8(Status.ORDER_INVALID_MAKER_ASSET_AMOUNT),
|
||||
INVALID_ORDER_MAKER_ASSET_AMOUNT
|
||||
);
|
||||
require(
|
||||
orderStatus != uint8(Status.ORDER_INVALID_TAKER_ASSET_AMOUNT),
|
||||
INVALID_ORDER_TAKER_ASSET_AMOUNT
|
||||
);
|
||||
|
||||
// Validate Maker signature (check only if first time seen)
|
||||
if (takerAssetFilledAmount == 0) {
|
||||
require(
|
||||
isValidSignature(orderHash, order.makerAddress, signature),
|
||||
SIGNATURE_VALIDATION_FAILED
|
||||
);
|
||||
}
|
||||
|
||||
// Validate sender is allowed to fill this order
|
||||
if (order.senderAddress != address(0)) {
|
||||
require(
|
||||
order.senderAddress == msg.sender,
|
||||
INVALID_SENDER
|
||||
);
|
||||
}
|
||||
|
||||
// Validate taker is allowed to fill this order
|
||||
if (order.takerAddress != address(0)) {
|
||||
require(
|
||||
order.takerAddress == takerAddress,
|
||||
INVALID_CONTEXT
|
||||
);
|
||||
}
|
||||
require(
|
||||
takerAssetFillAmount > 0,
|
||||
GT_ZERO_AMOUNT_REQUIRED
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Calculates amounts filled and fees paid by maker and taker.
|
||||
/// @param order to be filled.
|
||||
/// @param orderStatus Status of order to be filled.
|
||||
/// @param takerAssetFilledAmount Amount of order already filled.
|
||||
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
|
||||
/// @return status Return status of calculating fill amounts. Returns Status.SUCCESS on success.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function calculateFillResults(
|
||||
Order memory order,
|
||||
uint8 orderStatus,
|
||||
uint256 takerAssetFilledAmount,
|
||||
uint256 takerAssetFillAmount)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
uint8 status,
|
||||
FillResults memory fillResults)
|
||||
{
|
||||
// Fill Amount must be greater than 0
|
||||
if (takerAssetFillAmount <= 0) {
|
||||
status = uint8(Status.TAKER_ASSET_FILL_AMOUNT_TOO_LOW);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the order is fillable
|
||||
if (orderStatus != uint8(Status.ORDER_FILLABLE)) {
|
||||
status = uint8(orderStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute takerAssetFilledAmount
|
||||
uint256 remainingtakerAssetAmount = safeSub(order.takerAssetAmount, takerAssetFilledAmount);
|
||||
fillResults.takerAssetFilledAmount = min256(takerAssetFillAmount, remainingtakerAssetAmount);
|
||||
|
||||
// Validate fill order rounding
|
||||
if (isRoundingError(
|
||||
fillResults.takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount))
|
||||
{
|
||||
status = uint8(Status.ROUNDING_ERROR_TOO_LARGE);
|
||||
fillResults.takerAssetFilledAmount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute proportional transfer amounts
|
||||
// TODO: All three are multiplied by the same fraction. This can
|
||||
// potentially be optimized.
|
||||
fillResults.makerAssetFilledAmount = getPartialAmount(
|
||||
fillResults.takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerAssetAmount);
|
||||
fillResults.makerFeePaid = getPartialAmount(
|
||||
fillResults.takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.makerFee);
|
||||
fillResults.takerFeePaid = getPartialAmount(
|
||||
fillResults.takerAssetFilledAmount,
|
||||
order.takerAssetAmount,
|
||||
order.takerFee);
|
||||
|
||||
status = uint8(Status.SUCCESS);
|
||||
return;
|
||||
}
|
||||
|
||||
/// @dev Updates state with results of a fill order.
|
||||
/// @param order that was filled.
|
||||
/// @param takerAddress Address of taker who filled the order.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function updateFilledState(
|
||||
Order memory order,
|
||||
address takerAddress,
|
||||
bytes32 orderHash,
|
||||
FillResults memory fillResults)
|
||||
internal
|
||||
{
|
||||
// Update state
|
||||
filled[orderHash] = safeAdd(filled[orderHash], fillResults.takerAssetFilledAmount);
|
||||
|
||||
// Log order
|
||||
emitFillEvent(order, takerAddress, orderHash, fillResults);
|
||||
}
|
||||
|
||||
/// @dev Fills the input order.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerToken to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
function fillOrder(
|
||||
Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes memory signature)
|
||||
public
|
||||
returns (FillResults memory fillResults)
|
||||
{
|
||||
// Fetch current order status
|
||||
bytes32 orderHash;
|
||||
uint8 orderStatus;
|
||||
uint256 takerAssetFilledAmount;
|
||||
(orderStatus, orderHash, takerAssetFilledAmount) = getOrderInfo(order);
|
||||
|
||||
// Fetch taker address
|
||||
address takerAddress = getCurrentContextAddress();
|
||||
|
||||
// Either our context is valid or we revert
|
||||
validateFillOrderContextOrRevert(order, orderStatus, orderHash, takerAssetFilledAmount, signature, takerAddress, takerAssetFillAmount);
|
||||
|
||||
// Compute proportional fill amounts
|
||||
uint8 status;
|
||||
(status, fillResults) = calculateFillResults(order, orderStatus, takerAssetFilledAmount, takerAssetFillAmount);
|
||||
if (status != uint8(Status.SUCCESS)) {
|
||||
emit ExchangeStatus(uint8(status), orderHash);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
// Settle order
|
||||
(fillResults.makerAssetFilledAmount, fillResults.makerFeePaid, fillResults.takerFeePaid) =
|
||||
settleOrder(order, takerAddress, fillResults.takerAssetFilledAmount);
|
||||
|
||||
// Update exchange internal state
|
||||
updateFilledState(order, takerAddress, orderHash, fillResults);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
/// @dev Validates context for cancelOrder. Succeeds or throws.
|
||||
/// @param order that was cancelled.
|
||||
/// @param orderStatus Status of order that was cancelled.
|
||||
/// @param orderHash Hash of order that was cancelled.
|
||||
function validateCancelOrderContextOrRevert(
|
||||
Order memory order,
|
||||
uint8 orderStatus,
|
||||
bytes32 orderHash)
|
||||
internal
|
||||
{
|
||||
// Ensure order is valid
|
||||
require(
|
||||
orderStatus != uint8(Status.ORDER_INVALID_MAKER_ASSET_AMOUNT),
|
||||
INVALID_ORDER_MAKER_ASSET_AMOUNT
|
||||
);
|
||||
require(
|
||||
orderStatus != uint8(Status.ORDER_INVALID_TAKER_ASSET_AMOUNT),
|
||||
INVALID_ORDER_TAKER_ASSET_AMOUNT
|
||||
);
|
||||
|
||||
// Validate transaction signed by maker
|
||||
address makerAddress = getCurrentContextAddress();
|
||||
require(
|
||||
order.makerAddress == makerAddress,
|
||||
INVALID_CONTEXT
|
||||
);
|
||||
|
||||
// Validate sender is allowed to cancel this order
|
||||
if (order.senderAddress != address(0)) {
|
||||
require(
|
||||
order.senderAddress == msg.sender,
|
||||
INVALID_SENDER
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Updates state with results of cancelling an order.
|
||||
/// State is only updated if the order is currently fillable.
|
||||
/// Otherwise, updating state would have no effect.
|
||||
/// @param order that was cancelled.
|
||||
/// @param orderStatus Status of order that was cancelled.
|
||||
/// @param orderHash Hash of order that was cancelled.
|
||||
/// @return stateUpdated Returns true only if state was updated.
|
||||
function updateCancelledState(
|
||||
Order memory order,
|
||||
uint8 orderStatus,
|
||||
bytes32 orderHash)
|
||||
internal
|
||||
returns (bool stateUpdated)
|
||||
{
|
||||
// Ensure order is fillable (otherwise cancelling does nothing)
|
||||
if (orderStatus != uint8(Status.ORDER_FILLABLE)) {
|
||||
emit ExchangeStatus(uint8(orderStatus), orderHash);
|
||||
stateUpdated = false;
|
||||
return stateUpdated;
|
||||
}
|
||||
|
||||
// Perform cancel
|
||||
cancelled[orderHash] = true;
|
||||
stateUpdated = true;
|
||||
|
||||
// Log cancel
|
||||
emit Cancel(
|
||||
order.makerAddress,
|
||||
order.feeRecipientAddress,
|
||||
orderHash,
|
||||
order.makerAssetData,
|
||||
order.takerAssetData
|
||||
);
|
||||
|
||||
return stateUpdated;
|
||||
}
|
||||
|
||||
/// @dev After calling, the order can not be filled anymore.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @return True if the order state changed to cancelled.
|
||||
/// False if the transaction was already cancelled or expired.
|
||||
function cancelOrder(Order memory order)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
// Fetch current order status
|
||||
bytes32 orderHash;
|
||||
uint8 orderStatus;
|
||||
uint256 takerAssetFilledAmount;
|
||||
(orderStatus, orderHash, takerAssetFilledAmount) = getOrderInfo(order);
|
||||
|
||||
// Validate context
|
||||
validateCancelOrderContextOrRevert(order, orderStatus, orderHash);
|
||||
|
||||
// Perform cancel
|
||||
return updateCancelledState(order, orderStatus, orderHash);
|
||||
}
|
||||
|
||||
/// @dev Cancels all orders reated by sender with a salt less than or equal to the specified salt value.
|
||||
/// @param salt Orders created with a salt less or equal to this value will be cancelled.
|
||||
function cancelOrdersUpTo(uint256 salt)
|
||||
@@ -62,162 +406,6 @@ contract MixinExchangeCore is
|
||||
emit CancelUpTo(msg.sender, newMakerEpoch);
|
||||
}
|
||||
|
||||
/// @dev Fills the input order.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
function fillOrder(
|
||||
Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes memory signature)
|
||||
public
|
||||
returns (FillResults memory fillResults)
|
||||
{
|
||||
// Compute the order hash
|
||||
bytes32 orderHash = getOrderHash(order);
|
||||
|
||||
// Check if order has been cancelled by salt value
|
||||
if (order.salt < makerEpoch[order.makerAddress]) {
|
||||
emit ExchangeError(uint8(Errors.ORDER_CANCELLED), orderHash);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
// Check if order has been cancelled by orderHash
|
||||
if (cancelled[orderHash]) {
|
||||
emit ExchangeError(uint8(Errors.ORDER_CANCELLED), orderHash);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
// Validate order and maker only if first time seen
|
||||
// TODO: Read filled and cancelled only once
|
||||
if (filled[orderHash] == 0) {
|
||||
require(
|
||||
order.makerAssetAmount > 0,
|
||||
GT_ZERO_AMOUNT_REQUIRED
|
||||
);
|
||||
require(
|
||||
order.takerAssetAmount > 0,
|
||||
GT_ZERO_AMOUNT_REQUIRED
|
||||
);
|
||||
require(
|
||||
isValidSignature(orderHash, order.makerAddress, signature),
|
||||
SIGNATURE_VALIDATION_FAILED
|
||||
);
|
||||
}
|
||||
|
||||
// Validate sender is allowed to fill this order
|
||||
if (order.senderAddress != address(0)) {
|
||||
require(
|
||||
order.senderAddress == msg.sender,
|
||||
INVALID_SENDER
|
||||
);
|
||||
}
|
||||
|
||||
// Validate taker is allowed to fill this order
|
||||
address takerAddress = getCurrentContextAddress();
|
||||
if (order.takerAddress != address(0)) {
|
||||
require(
|
||||
order.takerAddress == takerAddress,
|
||||
INVALID_CONTEXT
|
||||
);
|
||||
}
|
||||
require(
|
||||
takerAssetFillAmount > 0,
|
||||
GT_ZERO_AMOUNT_REQUIRED
|
||||
);
|
||||
|
||||
// Validate order expiration
|
||||
if (block.timestamp >= order.expirationTimeSeconds) {
|
||||
emit ExchangeError(uint8(Errors.ORDER_EXPIRED), orderHash);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
// Validate order availability
|
||||
uint256 remainingTakerAssetFillAmount = safeSub(order.takerAssetAmount, filled[orderHash]);
|
||||
if (remainingTakerAssetFillAmount == 0) {
|
||||
emit ExchangeError(uint8(Errors.ORDER_FULLY_FILLED), orderHash);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
// Validate fill order rounding
|
||||
fillResults.takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetFillAmount);
|
||||
if (isRoundingError(fillResults.takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount)) {
|
||||
emit ExchangeError(uint8(Errors.ROUNDING_ERROR_TOO_LARGE), orderHash);
|
||||
fillResults.takerAssetFilledAmount = 0;
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
// Update state
|
||||
filled[orderHash] = safeAdd(filled[orderHash], fillResults.takerAssetFilledAmount);
|
||||
|
||||
// Settle order
|
||||
(fillResults.makerAssetFilledAmount, fillResults.makerFeePaid, fillResults.takerFeePaid) =
|
||||
settleOrder(order, takerAddress, fillResults.takerAssetFilledAmount);
|
||||
|
||||
// Log order
|
||||
emitFillEvent(order, takerAddress, orderHash, fillResults);
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
/// @dev After calling, the order can not be filled anymore.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @return True if the order state changed to cancelled.
|
||||
/// False if the transaction was already cancelled or expired.
|
||||
function cancelOrder(Order memory order)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
// Compute the order hash
|
||||
bytes32 orderHash = getOrderHash(order);
|
||||
|
||||
// Validate the order
|
||||
require(
|
||||
order.makerAssetAmount > 0,
|
||||
GT_ZERO_AMOUNT_REQUIRED
|
||||
);
|
||||
require(
|
||||
order.takerAssetAmount > 0,
|
||||
GT_ZERO_AMOUNT_REQUIRED
|
||||
);
|
||||
|
||||
// Validate sender is allowed to cancel this order
|
||||
if (order.senderAddress != address(0)) {
|
||||
require(
|
||||
order.senderAddress == msg.sender,
|
||||
INVALID_SENDER
|
||||
);
|
||||
}
|
||||
|
||||
// Validate transaction signed by maker
|
||||
address makerAddress = getCurrentContextAddress();
|
||||
require(
|
||||
order.makerAddress == makerAddress,
|
||||
INVALID_CONTEXT
|
||||
);
|
||||
|
||||
if (block.timestamp >= order.expirationTimeSeconds) {
|
||||
emit ExchangeError(uint8(Errors.ORDER_EXPIRED), orderHash);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cancelled[orderHash]) {
|
||||
emit ExchangeError(uint8(Errors.ORDER_CANCELLED), orderHash);
|
||||
return false;
|
||||
}
|
||||
|
||||
cancelled[orderHash] = true;
|
||||
|
||||
emit Cancel(
|
||||
order.makerAddress,
|
||||
order.feeRecipientAddress,
|
||||
orderHash,
|
||||
order.makerAssetData,
|
||||
order.takerAssetData
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Logs a Fill event with the given arguments.
|
||||
/// The sole purpose of this function is to get around the stack variable limit.
|
||||
function emitFillEvent(
|
||||
|
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.21;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./mixins/MExchangeCore.sol";
|
||||
import "./mixins/MMatchOrders.sol";
|
||||
import "./mixins/MSettlement.sol";
|
||||
import "./mixins/MTransactions.sol";
|
||||
import "../../utils/SafeMath/SafeMath.sol";
|
||||
import "./libs/LibMath.sol";
|
||||
import "./libs/LibOrder.sol";
|
||||
import "./libs/LibStatus.sol";
|
||||
import "../../utils/LibBytes/LibBytes.sol";
|
||||
|
||||
contract MixinMatchOrders is
|
||||
SafeMath,
|
||||
LibBytes,
|
||||
LibMath,
|
||||
LibStatus,
|
||||
LibOrder,
|
||||
MExchangeCore,
|
||||
MMatchOrders,
|
||||
MSettlement,
|
||||
MTransactions
|
||||
{
|
||||
|
||||
/// @dev Validates context for matchOrders. Succeeds or throws.
|
||||
/// @param leftOrder First order to match.
|
||||
/// @param rightOrder Second order to match.
|
||||
function validateMatchOrdersContextOrRevert(
|
||||
Order memory leftOrder,
|
||||
Order memory rightOrder)
|
||||
internal
|
||||
{
|
||||
// The leftOrder maker asset must be the same as the rightOrder taker asset.
|
||||
require(areBytesEqual(leftOrder.makerAssetData, rightOrder.takerAssetData));
|
||||
|
||||
// The leftOrder taker asset must be the same as the rightOrder maker asset.
|
||||
require(areBytesEqual(leftOrder.takerAssetData, rightOrder.makerAssetData));
|
||||
|
||||
// Make sure there is a positive spread.
|
||||
// There is a positive 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:
|
||||
require(
|
||||
safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) >=
|
||||
safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Validates matched fill results. Succeeds or throws.
|
||||
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
|
||||
function validateMatchedOrderFillResultsOrThrow(MatchedFillResults memory matchedFillResults)
|
||||
internal
|
||||
{
|
||||
// The right order must spend at least as much as we're transferring to the left order's maker.
|
||||
// If the amount transferred from the right order is greater than what is transferred, it is a rounding error amount.
|
||||
// Ensure this difference is negligible by dividing the values with each other. The result should equal to ~1.
|
||||
require(matchedFillResults.right.makerAssetFilledAmount >= matchedFillResults.left.takerAssetFilledAmount);
|
||||
require(!isRoundingError(matchedFillResults.right.makerAssetFilledAmount, matchedFillResults.left.takerAssetFilledAmount, 1));
|
||||
}
|
||||
|
||||
/// @dev Calculates partial value given a numerator and denominator.
|
||||
/// Throws if there is a rounding error.
|
||||
/// @param numerator Numerator.
|
||||
/// @param denominator Denominator.
|
||||
/// @param target Value to calculate partial of.
|
||||
/// @return Partial value of target.
|
||||
function safeGetPartialAmount(
|
||||
uint256 numerator,
|
||||
uint256 denominator,
|
||||
uint256 target)
|
||||
internal pure
|
||||
returns (uint256 partialAmount)
|
||||
{
|
||||
require(!isRoundingError(numerator, denominator, target));
|
||||
return getPartialAmount(numerator, denominator, target);
|
||||
}
|
||||
|
||||
/// @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 leftOrderStatus Order status of left order.
|
||||
/// @param rightOrderStatus Order status of right order.
|
||||
/// @param leftOrderFilledAmount Amount of left order already filled.
|
||||
/// @param rightOrderFilledAmount Amount of right order already filled.
|
||||
/// @return status Return status of calculating fill amounts. Returns Status.SUCCESS on success.
|
||||
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
|
||||
function calculateMatchedFillResults(
|
||||
Order memory leftOrder,
|
||||
Order memory rightOrder,
|
||||
uint8 leftOrderStatus,
|
||||
uint8 rightOrderStatus,
|
||||
uint256 leftOrderFilledAmount,
|
||||
uint256 rightOrderFilledAmount)
|
||||
internal
|
||||
returns (
|
||||
uint8 status,
|
||||
MatchedFillResults memory matchedFillResults)
|
||||
{
|
||||
// We settle orders at the price point defined by the right order (profit goes to the order taker)
|
||||
// The constraint can be either on the left or on the right.
|
||||
// The constraint is on the left iff the amount required to fill the left order
|
||||
// is less than or equal to the amount we can spend from the right order:
|
||||
// <leftTakerAssetAmountRemaining> <= <rightTakerAssetAmountRemaining> * <rightMakerToTakerRatio>
|
||||
// <leftTakerAssetAmountRemaining> <= <rightTakerAssetAmountRemaining> * <rightOrder.makerAssetAmount> / <rightOrder.takerAssetAmount>
|
||||
// <leftTakerAssetAmountRemaining> * <rightOrder.takerAssetAmount> <= <rightTakerAssetAmountRemaining> * <rightOrder.makerAssetAmount>
|
||||
uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderFilledAmount);
|
||||
uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderFilledAmount);
|
||||
uint256 leftOrderAmountToFill = 0;
|
||||
uint256 rightOrderAmountToFill = 0;
|
||||
if (
|
||||
safeMul(leftTakerAssetAmountRemaining, rightOrder.takerAssetAmount) <=
|
||||
safeMul(rightTakerAssetAmountRemaining, rightOrder.makerAssetAmount)
|
||||
) {
|
||||
// Left order is the constraint: maximally fill left
|
||||
leftOrderAmountToFill = leftTakerAssetAmountRemaining;
|
||||
|
||||
// The right order receives an amount proportional to how much was spent.
|
||||
// TODO: Ensure rounding error is in the correct direction.
|
||||
rightOrderAmountToFill = safeGetPartialAmount(
|
||||
rightOrder.takerAssetAmount,
|
||||
rightOrder.makerAssetAmount,
|
||||
leftOrderAmountToFill);
|
||||
} else {
|
||||
// Right order is the constraint: maximally fill right
|
||||
rightOrderAmountToFill = rightTakerAssetAmountRemaining;
|
||||
|
||||
// The left order receives an amount proportional to how much was spent.
|
||||
// TODO: Ensure rounding error is in the correct direction.
|
||||
leftOrderAmountToFill = safeGetPartialAmount(
|
||||
rightOrder.makerAssetAmount,
|
||||
rightOrder.takerAssetAmount,
|
||||
rightOrderAmountToFill);
|
||||
}
|
||||
|
||||
// Calculate fill results for left order
|
||||
( status,
|
||||
matchedFillResults.left
|
||||
) = calculateFillResults(
|
||||
leftOrder,
|
||||
leftOrderStatus,
|
||||
leftOrderFilledAmount,
|
||||
leftOrderAmountToFill);
|
||||
if (status != uint8(Status.SUCCESS)) {
|
||||
return (status, matchedFillResults);
|
||||
}
|
||||
|
||||
// Calculate fill results for right order
|
||||
( status,
|
||||
matchedFillResults.right
|
||||
) = calculateFillResults(
|
||||
rightOrder,
|
||||
rightOrderStatus,
|
||||
rightOrderFilledAmount,
|
||||
rightOrderAmountToFill);
|
||||
if (status != uint8(Status.SUCCESS)) {
|
||||
return (status, matchedFillResults);
|
||||
}
|
||||
|
||||
// Validate the fill results
|
||||
validateMatchedOrderFillResultsOrThrow(matchedFillResults);
|
||||
|
||||
// Return status & fill results
|
||||
return (status, matchedFillResults);
|
||||
}
|
||||
|
||||
/// @dev Match two complementary orders that have a positive spread.
|
||||
/// 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 left order goes to the taker (who matched the two orders).
|
||||
/// @param leftOrder First order to match.
|
||||
/// @param rightOrder Second order to match.
|
||||
/// @param leftSignature Proof that order was created by the left maker.
|
||||
/// @param rightSignature Proof that order was created by the right maker.
|
||||
/// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.
|
||||
function matchOrders(
|
||||
Order memory leftOrder,
|
||||
Order memory rightOrder,
|
||||
bytes leftSignature,
|
||||
bytes rightSignature)
|
||||
public
|
||||
returns (MatchedFillResults memory matchedFillResults)
|
||||
{
|
||||
// Get left status
|
||||
OrderInfo memory leftOrderInfo;
|
||||
( leftOrderInfo.orderStatus,
|
||||
leftOrderInfo.orderHash,
|
||||
leftOrderInfo.orderFilledAmount
|
||||
) = getOrderInfo(leftOrder);
|
||||
if (leftOrderInfo.orderStatus != uint8(Status.ORDER_FILLABLE)) {
|
||||
emit ExchangeStatus(uint8(leftOrderInfo.orderStatus), leftOrderInfo.orderHash);
|
||||
return matchedFillResults;
|
||||
}
|
||||
|
||||
// Get right status
|
||||
OrderInfo memory rightOrderInfo;
|
||||
( rightOrderInfo.orderStatus,
|
||||
rightOrderInfo.orderHash,
|
||||
rightOrderInfo.orderFilledAmount
|
||||
) = getOrderInfo(rightOrder);
|
||||
if (rightOrderInfo.orderStatus != uint8(Status.ORDER_FILLABLE)) {
|
||||
emit ExchangeStatus(uint8(rightOrderInfo.orderStatus), rightOrderInfo.orderHash);
|
||||
return matchedFillResults;
|
||||
}
|
||||
|
||||
// Fetch taker address
|
||||
address takerAddress = getCurrentContextAddress();
|
||||
|
||||
// Either our context is valid or we revert
|
||||
validateMatchOrdersContextOrRevert(leftOrder, rightOrder);
|
||||
|
||||
// Compute proportional fill amounts
|
||||
uint8 matchedFillAmountsStatus;
|
||||
( matchedFillAmountsStatus,
|
||||
matchedFillResults
|
||||
) = calculateMatchedFillResults(
|
||||
leftOrder,
|
||||
rightOrder,
|
||||
leftOrderInfo.orderStatus,
|
||||
rightOrderInfo.orderStatus,
|
||||
leftOrderInfo.orderFilledAmount,
|
||||
rightOrderInfo.orderFilledAmount);
|
||||
if (matchedFillAmountsStatus != uint8(Status.SUCCESS)) {
|
||||
return matchedFillResults;
|
||||
}
|
||||
|
||||
// Validate fill contexts
|
||||
validateFillOrderContextOrRevert(
|
||||
leftOrder,
|
||||
leftOrderInfo.orderStatus,
|
||||
leftOrderInfo.orderHash,
|
||||
leftOrderInfo.orderFilledAmount,
|
||||
leftSignature,
|
||||
rightOrder.makerAddress,
|
||||
matchedFillResults.left.takerAssetFilledAmount);
|
||||
validateFillOrderContextOrRevert(
|
||||
rightOrder,
|
||||
rightOrderInfo.orderStatus,
|
||||
rightOrderInfo.orderHash,
|
||||
rightOrderInfo.orderFilledAmount,
|
||||
rightSignature,
|
||||
leftOrder.makerAddress,
|
||||
matchedFillResults.right.takerAssetFilledAmount);
|
||||
|
||||
// Settle matched orders. Succeeds or throws.
|
||||
settleMatchedOrders(leftOrder, rightOrder, matchedFillResults, takerAddress);
|
||||
|
||||
// Update exchange state
|
||||
updateFilledState(
|
||||
leftOrder,
|
||||
rightOrder.makerAddress,
|
||||
leftOrderInfo.orderHash,
|
||||
matchedFillResults.left
|
||||
);
|
||||
updateFilledState(
|
||||
rightOrder,
|
||||
leftOrder.makerAddress,
|
||||
rightOrderInfo.orderHash,
|
||||
matchedFillResults.right
|
||||
);
|
||||
|
||||
// Return results
|
||||
return matchedFillResults;
|
||||
}
|
||||
}
|
@@ -22,9 +22,11 @@ import "./mixins/MSettlement.sol";
|
||||
import "./mixins/MAssetProxyDispatcher.sol";
|
||||
import "./libs/LibOrder.sol";
|
||||
import "./libs/LibMath.sol";
|
||||
import "./mixins/MMatchOrders.sol";
|
||||
|
||||
contract MixinSettlement is
|
||||
LibMath,
|
||||
MMatchOrders,
|
||||
MSettlement,
|
||||
MAssetProxyDispatcher
|
||||
{
|
||||
@@ -96,4 +98,87 @@ contract MixinSettlement is
|
||||
);
|
||||
return (makerAssetFilledAmount, makerFeePaid, takerFeePaid);
|
||||
}
|
||||
|
||||
/// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.
|
||||
/// @param leftOrder First matched order.
|
||||
/// @param rightOrder Second matched order.
|
||||
/// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.
|
||||
/// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.
|
||||
function settleMatchedOrders(
|
||||
LibOrder.Order memory leftOrder,
|
||||
LibOrder.Order memory rightOrder,
|
||||
MatchedFillResults memory matchedFillResults,
|
||||
address takerAddress)
|
||||
internal
|
||||
{
|
||||
// Optimized for:
|
||||
// * leftOrder.feeRecipient =?= rightOrder.feeRecipient
|
||||
|
||||
// Not optimized for:
|
||||
// * {left, right}.{MakerAsset, TakerAsset} == ZRX
|
||||
// * {left, right}.maker, takerAddress == {left, right}.feeRecipient
|
||||
|
||||
// leftOrder.MakerAsset == rightOrder.TakerAsset
|
||||
// Taker should be left with a positive balance (the spread)
|
||||
dispatchTransferFrom(
|
||||
leftOrder.makerAssetData,
|
||||
leftOrder.makerAddress,
|
||||
takerAddress,
|
||||
matchedFillResults.left.makerAssetFilledAmount);
|
||||
dispatchTransferFrom(
|
||||
leftOrder.makerAssetData,
|
||||
takerAddress,
|
||||
rightOrder.makerAddress,
|
||||
matchedFillResults.right.takerAssetFilledAmount);
|
||||
|
||||
// rightOrder.MakerAsset == leftOrder.TakerAsset
|
||||
// leftOrder.takerAssetFilledAmount ~ rightOrder.makerAssetFilledAmount
|
||||
// The change goes to right, not to taker.
|
||||
assert(matchedFillResults.right.makerAssetFilledAmount >= matchedFillResults.left.takerAssetFilledAmount);
|
||||
dispatchTransferFrom(
|
||||
rightOrder.makerAssetData,
|
||||
rightOrder.makerAddress,
|
||||
leftOrder.makerAddress,
|
||||
matchedFillResults.right.makerAssetFilledAmount);
|
||||
|
||||
// Maker fees
|
||||
dispatchTransferFrom(
|
||||
ZRX_PROXY_DATA,
|
||||
leftOrder.makerAddress,
|
||||
leftOrder.feeRecipientAddress,
|
||||
matchedFillResults.left.makerFeePaid);
|
||||
dispatchTransferFrom(
|
||||
ZRX_PROXY_DATA,
|
||||
rightOrder.makerAddress,
|
||||
rightOrder.feeRecipientAddress,
|
||||
matchedFillResults.right.makerFeePaid);
|
||||
|
||||
// Taker fees
|
||||
// If we assume distinct(left, right, takerAddress) and
|
||||
// distinct(MakerAsset, TakerAsset, zrx) then the only remaining
|
||||
// opportunity for optimization is when both feeRecipientAddress' are
|
||||
// the same.
|
||||
if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) {
|
||||
dispatchTransferFrom(
|
||||
ZRX_PROXY_DATA,
|
||||
takerAddress,
|
||||
leftOrder.feeRecipientAddress,
|
||||
safeAdd(
|
||||
matchedFillResults.left.takerFeePaid,
|
||||
matchedFillResults.right.takerFeePaid
|
||||
)
|
||||
);
|
||||
} else {
|
||||
dispatchTransferFrom(
|
||||
ZRX_PROXY_DATA,
|
||||
takerAddress,
|
||||
leftOrder.feeRecipientAddress,
|
||||
matchedFillResults.left.takerFeePaid);
|
||||
dispatchTransferFrom(
|
||||
ZRX_PROXY_DATA,
|
||||
takerAddress,
|
||||
rightOrder.feeRecipientAddress,
|
||||
matchedFillResults.right.takerFeePaid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,10 +27,11 @@ import "./libs/LibFillResults.sol";
|
||||
import "./libs/LibExchangeErrors.sol";
|
||||
|
||||
contract MixinWrapperFunctions is
|
||||
SafeMath,
|
||||
LibBytes,
|
||||
LibMath,
|
||||
LibOrder,
|
||||
LibFillResults,
|
||||
LibMath,
|
||||
LibBytes,
|
||||
LibExchangeErrors,
|
||||
MExchangeCore
|
||||
{
|
||||
|
@@ -23,10 +23,10 @@ import "./libs/LibOrder.sol";
|
||||
import "./libs/LibFillResults.sol";
|
||||
|
||||
contract IWrapperFunctions is
|
||||
LibBytes,
|
||||
LibMath,
|
||||
LibOrder,
|
||||
LibFillResults,
|
||||
LibMath,
|
||||
LibBytes,
|
||||
LibExchangeErrors,
|
||||
MExchangeCore
|
||||
{
|
||||
|
@@ -20,17 +20,6 @@ pragma solidity ^0.4.23;
|
||||
|
||||
contract LibExchangeErrors {
|
||||
|
||||
// Error Codes
|
||||
enum Errors {
|
||||
ORDER_EXPIRED, // Order has already expired
|
||||
ORDER_FULLY_FILLED, // Order has already been fully filled
|
||||
ORDER_CANCELLED, // Order has already been cancelled
|
||||
ROUNDING_ERROR_TOO_LARGE, // Rounding error too large
|
||||
INSUFFICIENT_BALANCE_OR_ALLOWANCE // Insufficient balance or allowance for token transfer
|
||||
}
|
||||
|
||||
event ExchangeError(uint8 indexed errorId, bytes32 indexed orderHash);
|
||||
|
||||
// Core revert reasons
|
||||
string constant GT_ZERO_AMOUNT_REQUIRED = "Amount must be greater than 0.";
|
||||
string constant SIGNATURE_VALIDATION_FAILED = "Signature validation failed.";
|
||||
@@ -38,6 +27,10 @@ contract LibExchangeErrors {
|
||||
string constant INVALID_CONTEXT = "Function called in an invalid context.";
|
||||
string constant INVALID_NEW_MAKER_EPOCH = "Specified salt must be greater than or equal to existing makerEpoch.";
|
||||
|
||||
// Order revert reasons
|
||||
string constant INVALID_ORDER_TAKER_ASSET_AMOUNT = "Invalid order taker asset amount: expected a non-zero value.";
|
||||
string constant INVALID_ORDER_MAKER_ASSET_AMOUNT = "Invalid order maker asset amount: expected a non-zero value.";
|
||||
|
||||
// Transaction revert reasons
|
||||
string constant DUPLICATE_TRANSACTION_HASH = "Transaction has already been executed.";
|
||||
string constant TRANSACTION_EXECUTION_FAILED = "Transaction execution failed.";
|
||||
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.21;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract LibStatus {
|
||||
|
||||
// Exchange Status Codes
|
||||
enum Status {
|
||||
/// Default Status ///
|
||||
INVALID, // General invalid status
|
||||
|
||||
/// General Exchange Statuses ///
|
||||
SUCCESS, // Indicates a successful operation
|
||||
ROUNDING_ERROR_TOO_LARGE, // Rounding error too large
|
||||
INSUFFICIENT_BALANCE_OR_ALLOWANCE, // Insufficient balance or allowance for token transfer
|
||||
TAKER_ASSET_FILL_AMOUNT_TOO_LOW, // takerAssetFillAmount is <= 0
|
||||
INVALID_SIGNATURE, // Invalid signature
|
||||
INVALID_SENDER, // Invalid sender
|
||||
INVALID_TAKER, // Invalid taker
|
||||
INVALID_MAKER, // Invalid maker
|
||||
|
||||
/// Order State Statuses ///
|
||||
ORDER_INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount
|
||||
ORDER_INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount
|
||||
ORDER_FILLABLE, // Order is fillable
|
||||
ORDER_EXPIRED, // Order has already expired
|
||||
ORDER_FULLY_FILLED, // Order is fully filled
|
||||
ORDER_CANCELLED // Order has been cancelled
|
||||
}
|
||||
|
||||
event ExchangeStatus(uint8 indexed statusId, bytes32 indexed orderHash);
|
||||
}
|
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.23;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../interfaces/IAssetProxyDispatcher.sol";
|
||||
|
||||
|
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.23;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../libs/LibOrder.sol";
|
||||
import "../libs/LibFillResults.sol";
|
||||
@@ -63,4 +64,122 @@ contract MExchangeCore is
|
||||
bytes32 orderHash,
|
||||
LibFillResults.FillResults memory fillResults)
|
||||
internal;
|
||||
|
||||
/// @dev Gets information about an order.
|
||||
/// @param order Order to gather information on.
|
||||
/// @return status Status of order. Statuses are defined in the LibStatus.Status struct.
|
||||
/// @return orderHash Keccak-256 EIP712 hash of the order.
|
||||
/// @return takerAssetFilledAmount Amount of order that has been filled.
|
||||
function getOrderInfo(LibOrder.Order memory order)
|
||||
public
|
||||
view
|
||||
returns (
|
||||
uint8 orderStatus,
|
||||
bytes32 orderHash,
|
||||
uint256 takerAssetFilledAmount);
|
||||
|
||||
/// @dev Validates context for fillOrder. Succeeds or throws.
|
||||
/// @param order to be filled.
|
||||
/// @param orderStatus Status of order to be filled.
|
||||
/// @param orderHash Hash of order to be filled.
|
||||
/// @param takerAssetFilledAmount Amount of order already filled.
|
||||
/// @param signature Proof that the orders was created by its maker.
|
||||
/// @param takerAddress Address of order taker.
|
||||
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
|
||||
function validateFillOrderContextOrRevert(
|
||||
LibOrder.Order memory order,
|
||||
uint8 orderStatus,
|
||||
bytes32 orderHash,
|
||||
uint256 takerAssetFilledAmount,
|
||||
bytes memory signature,
|
||||
address takerAddress,
|
||||
uint256 takerAssetFillAmount)
|
||||
internal;
|
||||
|
||||
/// @dev Calculates amounts filled and fees paid by maker and taker.
|
||||
/// @param order to be filled.
|
||||
/// @param orderStatus Status of order to be filled.
|
||||
/// @param takerAssetFilledAmount Amount of order already filled.
|
||||
/// @param takerAssetFillAmount Desired amount of order to fill by taker.
|
||||
/// @return status Return status of calculating fill amounts. Returns Status.SUCCESS on success.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function calculateFillResults(
|
||||
LibOrder.Order memory order,
|
||||
uint8 orderStatus,
|
||||
uint256 takerAssetFilledAmount,
|
||||
uint256 takerAssetFillAmount)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
uint8 status,
|
||||
LibFillResults.FillResults memory fillResults);
|
||||
|
||||
/// @dev Updates state with results of a fill order.
|
||||
/// @param order that was filled.
|
||||
/// @param takerAddress Address of taker who filled the order.
|
||||
/// @return fillResults Amounts filled and fees paid by maker and taker.
|
||||
function updateFilledState(
|
||||
LibOrder.Order memory order,
|
||||
address takerAddress,
|
||||
bytes32 orderHash,
|
||||
LibFillResults.FillResults memory fillResults)
|
||||
internal;
|
||||
|
||||
/// @dev Fills the input order.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @param takerAssetFillAmount Desired amount of takerToken to sell.
|
||||
/// @param signature Proof that order has been created by maker.
|
||||
/// @return Amounts filled and fees paid by maker and taker.
|
||||
function fillOrder(
|
||||
LibOrder.Order memory order,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes memory signature)
|
||||
public
|
||||
returns (LibFillResults.FillResults memory fillResults);
|
||||
|
||||
/// @dev Validates context for cancelOrder. Succeeds or throws.
|
||||
/// @param order that was cancelled.
|
||||
/// @param orderStatus Status of order that was cancelled.
|
||||
/// @param orderHash Hash of order that was cancelled.
|
||||
function validateCancelOrderContextOrRevert(
|
||||
LibOrder.Order memory order,
|
||||
uint8 orderStatus,
|
||||
bytes32 orderHash)
|
||||
internal;
|
||||
|
||||
/// @dev Updates state with results of cancelling an order.
|
||||
/// State is only updated if the order is currently fillable.
|
||||
/// Otherwise, updating state would have no effect.
|
||||
/// @param order that was cancelled.
|
||||
/// @param orderStatus Status of order that was cancelled.
|
||||
/// @param orderHash Hash of order that was cancelled.
|
||||
/// @return stateUpdated Returns true only if state was updated.
|
||||
function updateCancelledState(
|
||||
LibOrder.Order memory order,
|
||||
uint8 orderStatus,
|
||||
bytes32 orderHash)
|
||||
internal
|
||||
returns (bool stateUpdated);
|
||||
|
||||
/// @dev After calling, the order can not be filled anymore.
|
||||
/// @param order Order struct containing order specifications.
|
||||
/// @return True if the order state changed to cancelled.
|
||||
/// False if the transaction was already cancelled or expired.
|
||||
function cancelOrder(LibOrder.Order memory order)
|
||||
public
|
||||
returns (bool);
|
||||
|
||||
/// @param salt Orders created with a salt less or equal to this value will be cancelled.
|
||||
function cancelOrdersUpTo(uint256 salt)
|
||||
external;
|
||||
|
||||
/*
|
||||
/// @dev Checks if rounding error > 0.1%.
|
||||
/// @param numerator Numerator.
|
||||
/// @param denominator Denominator.
|
||||
/// @param target Value to multiply with numerator/denominator.
|
||||
/// @return Rounding error is present.
|
||||
function isRoundingError(uint256 numerator, uint256 denominator, uint256 target)
|
||||
public pure
|
||||
returns (bool isError); */
|
||||
}
|
||||
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
pragma solidity ^0.4.21;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../libs/LibOrder.sol";
|
||||
import "../libs/LibFillResults.sol";
|
||||
import "./MExchangeCore.sol";
|
||||
|
||||
contract MMatchOrders {
|
||||
|
||||
struct MatchedFillResults {
|
||||
LibFillResults.FillResults left;
|
||||
LibFillResults.FillResults right;
|
||||
}
|
||||
|
||||
/// This struct exists solely to avoid the stack limit constraint
|
||||
/// in matchOrders
|
||||
struct OrderInfo {
|
||||
uint8 orderStatus;
|
||||
bytes32 orderHash;
|
||||
uint256 orderFilledAmount;
|
||||
}
|
||||
|
||||
/// @dev Validates context for matchOrders. Succeeds or throws.
|
||||
/// @param leftOrder First order to match.
|
||||
/// @param rightOrder Second order to match.
|
||||
function validateMatchOrdersContextOrRevert(
|
||||
LibOrder.Order memory leftOrder,
|
||||
LibOrder.Order memory rightOrder)
|
||||
internal;
|
||||
|
||||
/// @dev Validates matched fill results. Succeeds or throws.
|
||||
/// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.
|
||||
function validateMatchedOrderFillResultsOrThrow(MatchedFillResults memory matchedFillResults)
|
||||
internal;
|
||||
|
||||
/// @dev Calculates partial value given a numerator and denominator.
|
||||
/// Throws if there is a rounding error.
|
||||
/// @param numerator Numerator.
|
||||
/// @param denominator Denominator.
|
||||
/// @param target Value to calculate partial of.
|
||||
/// @return Partial value of target.
|
||||
function safeGetPartialAmount(
|
||||
uint256 numerator,
|
||||
uint256 denominator,
|
||||
uint256 target)
|
||||
internal pure
|
||||
returns (uint256 partialAmount);
|
||||
|
||||
/// @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 leftOrderStatus Order status of left order.
|
||||
/// @param rightOrderStatus Order status of right order.
|
||||
/// @param leftOrderFilledAmount Amount of left order already filled.
|
||||
/// @param rightOrderFilledAmount Amount of right order already filled.
|
||||
/// @return status Return status of calculating fill amounts. Returns Status.SUCCESS on success.
|
||||
/// @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,
|
||||
uint8 leftOrderStatus,
|
||||
uint8 rightOrderStatus,
|
||||
uint256 leftOrderFilledAmount,
|
||||
uint256 rightOrderFilledAmount)
|
||||
internal
|
||||
returns (
|
||||
uint8 status,
|
||||
MatchedFillResults memory matchedFillResults);
|
||||
|
||||
/// @dev Match two complementary orders that have a positive spread.
|
||||
/// 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 left order goes to the taker (who matched the two orders).
|
||||
/// @param leftOrder First order to match.
|
||||
/// @param rightOrder Second order to match.
|
||||
/// @param leftSignature Proof that order was created by the left maker.
|
||||
/// @param rightSignature Proof that order was created by the right maker.
|
||||
/// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.
|
||||
function matchOrders(
|
||||
LibOrder.Order memory leftOrder,
|
||||
LibOrder.Order memory rightOrder,
|
||||
bytes leftSignature,
|
||||
bytes rightSignature)
|
||||
public
|
||||
returns (MatchedFillResults memory matchedFillResults);
|
||||
}
|
@@ -19,6 +19,7 @@
|
||||
pragma solidity ^0.4.23;
|
||||
|
||||
import "../libs/LibOrder.sol";
|
||||
import "./MMatchOrders.sol";
|
||||
|
||||
contract MSettlement {
|
||||
|
||||
@@ -37,5 +38,16 @@ contract MSettlement {
|
||||
uint256 makerFeePaid,
|
||||
uint256 takerFeePaid
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.
|
||||
/// @param leftOrder First matched order.
|
||||
/// @param rightOrder Second matched order.
|
||||
/// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.
|
||||
/// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.
|
||||
function settleMatchedOrders(
|
||||
LibOrder.Order memory leftOrder,
|
||||
LibOrder.Order memory rightOrder,
|
||||
MMatchOrders.MatchedFillResults memory matchedFillResults,
|
||||
address takerAddress)
|
||||
internal;
|
||||
}
|
||||
|
@@ -38,4 +38,4 @@ contract TestSignatureValidator is MixinSignatureValidator {
|
||||
);
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user