From 7c0ddab927360a6bf8b034f1975b368e455b1d87 Mon Sep 17 00:00:00 2001 From: wangdong Date: Tue, 24 Apr 2018 13:03:37 -0400 Subject: [PATCH 1/2] support all-or-none order option --- contracts/LoopringProtocol.sol | 1 + contracts/LoopringProtocolImpl.sol | 93 +++++++++++++++++++++--------- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/contracts/LoopringProtocol.sol b/contracts/LoopringProtocol.sol index bf065ea3..6c8eada3 100644 --- a/contracts/LoopringProtocol.sol +++ b/contracts/LoopringProtocol.sol @@ -26,6 +26,7 @@ contract LoopringProtocol { uint8 public constant MARGIN_SPLIT_PERCENTAGE_BASE = 100; uint8 public constant OPTION_MASK_CAP_BY_AMOUNTB = 0x01; + uint8 public constant OPTION_MASK_ALL_OR_NONE = 0x02; struct Fill { bytes32 orderHash; diff --git a/contracts/LoopringProtocolImpl.sol b/contracts/LoopringProtocolImpl.sol index d3749d8e..eed4a913 100644 --- a/contracts/LoopringProtocolImpl.sol +++ b/contracts/LoopringProtocolImpl.sol @@ -78,7 +78,8 @@ contract LoopringProtocolImpl is LoopringProtocol { uint validUntil; uint lrcFee; uint8 option; - bool capByAmountB; + bool optCapByAmountB; + bool optAllOrNone; bool marginSplitAsFee; bytes32 orderHash; address trackerAddr; @@ -164,6 +165,7 @@ contract LoopringProtocolImpl is LoopringProtocol { orderValues[4], option, option & OPTION_MASK_CAP_BY_AMOUNTB > 0 ? true : false, + option & OPTION_MASK_ALL_OR_NONE > 0 ? true : false, false, 0x0, 0x0, @@ -199,6 +201,14 @@ contract LoopringProtocolImpl is LoopringProtocol { require(registered, "invalid broker"); } + // For AON orders, must cancel it as a whole. + if (order.optAllOrNone) { + if (order.optCapByAmountB) { + cancelAmount = order.amountB; + } else { + cancelAmount = order.amountS; + } + } TokenTransferDelegate delegate = TokenTransferDelegate(delegateAddress); delegate.addCancelled(orderHash, cancelAmount); delegate.addCancelledOrFilled(orderHash, cancelAmount); @@ -298,7 +308,7 @@ contract LoopringProtocolImpl is LoopringProtocol { verifyMinerSuppliedFillRates(ctx); - scaleRingBasedOnHistoricalRecords(ctx); + scaleOrders(ctx); calculateRingFillAmount(ctx); @@ -378,6 +388,7 @@ contract LoopringProtocolImpl is LoopringProtocol { uintArgs[4], ctx.optionList[i], ctx.optionList[i] & OPTION_MASK_CAP_BY_AMOUNTB > 0 ? true : false, + ctx.optionList[i] & OPTION_MASK_ALL_OR_NONE > 0 ? true : false, marginSplitAsFee, 0x0, 0x0, // brokderTracker @@ -528,7 +539,7 @@ contract LoopringProtocolImpl is LoopringProtocol { /// @dev Scale down all orders based on historical fill or cancellation /// stats but key the order's original exchange rate. - function scaleRingBasedOnHistoricalRecords( + function scaleOrders( Context ctx ) private @@ -539,26 +550,34 @@ contract LoopringProtocolImpl is LoopringProtocol { for (uint i = 0; i < ringSize; i++) { Order memory order = orders[i]; - uint amount; - if (order.capByAmountB) { - amount = order.amountB.tolerantSub( - ctx.delegate.cancelledOrFilled(order.orderHash) + if (order.optAllOrNone) { + require( + ctx.delegate.cancelledOrFilled(order.orderHash) == 0, + "AON-order filled or cancelled already" ); + } else { + uint amount; - order.amountS = amount.mul(order.amountS) / order.amountB; - order.lrcFee = amount.mul(order.lrcFee) / order.amountB; + if (order.optCapByAmountB) { + amount = order.amountB.tolerantSub( + ctx.delegate.cancelledOrFilled(order.orderHash) + ); - order.amountB = amount; - } else { - amount = order.amountS.tolerantSub( - ctx.delegate.cancelledOrFilled(order.orderHash) - ); + order.amountS = amount.mul(order.amountS) / order.amountB; + order.lrcFee = amount.mul(order.lrcFee) / order.amountB; + + order.amountB = amount; + } else { + amount = order.amountS.tolerantSub( + ctx.delegate.cancelledOrFilled(order.orderHash) + ); - order.amountB = amount.mul(order.amountB) / order.amountS; - order.lrcFee = amount.mul(order.lrcFee) / order.amountS; + order.amountB = amount.mul(order.amountB) / order.amountS; + order.lrcFee = amount.mul(order.lrcFee) / order.amountS; - order.amountS = amount; + order.amountS = amount; + } } require(order.amountS > 0, "amountS scaled to 0"); @@ -571,12 +590,19 @@ contract LoopringProtocolImpl is LoopringProtocol { order.signer, order.trackerAddr ); - require(availableAmountS > 0, "spendable is 0"); - order.fillAmountS = ( - order.amountS < availableAmountS ? - order.amountS : availableAmountS - ); + if (order.optAllOrNone) { + require( + availableAmountS >= order.amountS, + "AON-order low spendable" + ); + } else { + require(availableAmountS > 0, "spendable is 0"); + order.fillAmountS = ( + order.amountS < availableAmountS ? + order.amountS : availableAmountS + ); + } } } @@ -698,7 +724,7 @@ contract LoopringProtocolImpl is LoopringProtocol { if (minerLrcSpendable >= order.lrcFeeState) { nextFillAmountS = ctx.orders[(i + 1) % ringSize].fillAmountS; uint split; - if (order.capByAmountB) { + if (order.optCapByAmountB) { split = (nextFillAmountS.mul( order.amountS ) / order.amountB).sub( @@ -712,7 +738,7 @@ contract LoopringProtocolImpl is LoopringProtocol { ) / 2; } - if (order.capByAmountB) { + if (order.optCapByAmountB) { order.splitS = split; } else { order.splitB = split; @@ -763,7 +789,7 @@ contract LoopringProtocolImpl is LoopringProtocol { historyBatch[q++] = order.orderHash; historyBatch[q++] = bytes32( - order.capByAmountB ? nextFillAmountS : order.fillAmountS + order.optCapByAmountB ? nextFillAmountS : order.fillAmountS ); fills[i] = Fill( @@ -811,7 +837,7 @@ contract LoopringProtocolImpl is LoopringProtocol { order.rateB ) / order.rateS; - if (order.capByAmountB) { + if (order.optCapByAmountB) { if (fillAmountB > order.amountB) { fillAmountB = order.amountB; @@ -830,6 +856,21 @@ contract LoopringProtocolImpl is LoopringProtocol { ) / order.amountS; } + // Check All-or-None orders + if (order.optAllOrNone){ + if (order.optCapByAmountB) { + require( + fillAmountB >= order.amountB, + "aon failed on amountB" + ); + } else { + require( + order.fillAmountS >= order.amountS, + "aon failed on amountS" + ); + } + } + if (fillAmountB <= next.fillAmountS) { next.fillAmountS = fillAmountB; } else { From 67de17853a2ea51b85dcf0ab9c276b1532a349e3 Mon Sep 17 00:00:00 2001 From: wangdong Date: Tue, 24 Apr 2018 13:12:46 -0400 Subject: [PATCH 2/2] minor change --- contracts/LoopringProtocolImpl.sol | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/contracts/LoopringProtocolImpl.sol b/contracts/LoopringProtocolImpl.sol index eed4a913..7af0447f 100644 --- a/contracts/LoopringProtocolImpl.sol +++ b/contracts/LoopringProtocolImpl.sol @@ -203,11 +203,7 @@ contract LoopringProtocolImpl is LoopringProtocol { // For AON orders, must cancel it as a whole. if (order.optAllOrNone) { - if (order.optCapByAmountB) { - cancelAmount = order.amountB; - } else { - cancelAmount = order.amountS; - } + cancelAmount = order.optCapByAmountB ? order.amountB : order.amountS; } TokenTransferDelegate delegate = TokenTransferDelegate(delegateAddress); delegate.addCancelled(orderHash, cancelAmount); @@ -554,7 +550,7 @@ contract LoopringProtocolImpl is LoopringProtocol { if (order.optAllOrNone) { require( ctx.delegate.cancelledOrFilled(order.orderHash) == 0, - "AON-order filled or cancelled already" + "AON filled or cancelled already" ); } else { uint amount; @@ -591,10 +587,12 @@ contract LoopringProtocolImpl is LoopringProtocol { order.trackerAddr ); - if (order.optAllOrNone) { + // This check is more strict than it needs to be, in case the + // `optCapByAmountB`is true. + if (order.optAllOrNone) { require( availableAmountS >= order.amountS, - "AON-order low spendable" + "AON spendable" ); } else { require(availableAmountS > 0, "spendable is 0"); @@ -861,12 +859,12 @@ contract LoopringProtocolImpl is LoopringProtocol { if (order.optCapByAmountB) { require( fillAmountB >= order.amountB, - "aon failed on amountB" + "AON failed on amountB" ); } else { require( order.fillAmountS >= order.amountS, - "aon failed on amountS" + "AON failed on amountS" ); } }