diff options
author | Amir Bandeali <abandeali1@gmail.com> | 2018-02-09 02:57:33 +0800 |
---|---|---|
committer | Amir Bandeali <abandeali1@gmail.com> | 2018-04-21 04:56:15 +0800 |
commit | 1ad31ab007c82e9538793b6a331e435344285ef8 (patch) | |
tree | 4e2bb1a4986114a2b0909b2bc9ea06e18f06217c | |
parent | 914db52c4da9f6824666c359df5efceabee51ff0 (diff) | |
download | dexon-0x-contracts-1ad31ab007c82e9538793b6a331e435344285ef8.tar.gz dexon-0x-contracts-1ad31ab007c82e9538793b6a331e435344285ef8.tar.zst dexon-0x-contracts-1ad31ab007c82e9538793b6a331e435344285ef8.zip |
Add fillOrderNoThrow and wrapper functions
10 files changed, 386 insertions, 285 deletions
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol index b899eb105..66c49c84f 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol @@ -18,12 +18,11 @@ contract IExchange { address indexed feeRecipient, address makerToken, address takerToken, - uint filledMakerTokenAmount, - uint filledTakerTokenAmount, - uint paidMakerFee, - uint paidTakerFee, - bytes32 indexed tokens, // keccak256(makerToken, takerToken), allows subscribing to a token pair - bytes32 orderHash + uint256 makerTokenFilledAmount, + uint256 takerTokenFilledAmount, + uint256 makerFeePaid, + uint256 takerFeePaid, + bytes32 indexed orderHash ); event LogCancel( @@ -31,10 +30,9 @@ contract IExchange { address indexed feeRecipient, address makerToken, address takerToken, - uint cancelledMakerTokenAmount, - uint cancelledTakerTokenAmount, - bytes32 indexed tokens, - bytes32 orderHash + uint256 makerTokenCancelledAmount, + uint256 takerTokenCancelledAmount, + bytes32 indexed orderHash ); function ZRX_TOKEN_CONTRACT() @@ -65,35 +63,34 @@ contract IExchange { /// @param orderHash The Keccak-256 hash of the given order. /// @return Sum of values already filled and cancelled. function getUnavailableTakerTokenAmount(bytes32 orderHash) - public constant - returns (uint); + public view + returns (uint256 unavailableTakerTokenAmount); /// @dev Calculates partial value given a numerator and denominator. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to calculate partial of. /// @return Partial value of target. - function getPartialAmount(uint numerator, uint denominator, uint target) - public constant - returns (uint); + function getPartialAmount(uint256 numerator, uint256 denominator, uint256 target) + public pure + returns (uint256 partialAmount); /// @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(uint numerator, uint denominator, uint target) - public constant - returns (bool); + function isRoundingError(uint256 numerator, uint256 denominator, uint256 target) + public pure + returns (bool isError); /// @dev Calculates Keccak-256 hash of order with specified parameters. /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. /// @return Keccak-256 hash of order. - function getOrderHash(address[5] orderAddresses, uint[6] orderValues) - public - constant - returns (bytes32); + function getOrderHash(address[5] orderAddresses, uint256[6] orderValues) + public view + returns (bytes32 orderHash); /// @dev Verifies that an order signature is valid. /// @param signer address of signer. @@ -108,119 +105,166 @@ contract IExchange { uint8 v, bytes32 r, bytes32 s) - public constant - returns (bool); + public pure + returns (bool isValid); /// @dev Fills the input order. /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. - /// @param fillTakerTokenAmount Desired amount of takerToken to fill. - /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfer will fail before attempting. + /// @param takerTokenFillAmount Desired amount of takerToken to fill. /// @param v ECDSA signature parameter v. /// @param r ECDSA signature parameters r. /// @param s ECDSA signature parameters s. /// @return Total amount of takerToken filled in trade. function fillOrder( address[5] orderAddresses, - uint[6] orderValues, - uint fillTakerTokenAmount, - bool shouldThrowOnInsufficientBalanceOrAllowance, + uint256[6] orderValues, + uint256 takerTokenFillAmount, uint8 v, bytes32 r, bytes32 s) public - returns (uint filledTakerTokenAmount); + returns (uint256 takerTokenFilledAmount); /// @dev Cancels the input order. /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. - /// @param cancelTakerTokenAmount Desired amount of takerToken to cancel in order. + /// @param takerTokenCancelAmount Desired amount of takerToken to cancel in order. /// @return Amount of takerToken cancelled. function cancelOrder( address[5] orderAddresses, - uint[6] orderValues, - uint cancelTakerTokenAmount) + uint256[6] orderValues, + uint256 takerTokenCancelAmount) public - returns (uint); + returns (uint256 takerTokenCancelledAmount); - /// @dev Fills an order with specified parameters and ECDSA signature, throws if specified amount not filled entirely. + /// @dev Fills an order with specified parameters and ECDSA signature. Throws if specified amount not filled entirely. /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. - /// @param fillTakerTokenAmount Desired amount of takerToken to fill. + /// @param takerTokenFillAmount Desired amount of takerToken to fill. /// @param v ECDSA signature parameter v. /// @param r ECDSA signature parameters r. /// @param s ECDSA signature parameters s. function fillOrKillOrder( address[5] orderAddresses, - uint[6] orderValues, - uint fillTakerTokenAmount, + uint256[6] orderValues, + uint256 takerTokenFillAmount, uint8 v, bytes32 r, bytes32 s) public; - /// @dev Synchronously executes multiple fill orders in a single transaction. + /// @dev Fills an order with specified parameters and ECDSA signature. Returns false if the transaction would otherwise revert. + /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. + /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. + /// @param takerTokenFillAmount Desired amount of takerToken to fill. + /// @param v ECDSA signature parameter v. + /// @param r ECDSA signature parameters r. + /// @param s ECDSA signature parameters s. + /// @return Success if the transaction did not revert. + /// @return Total amount of takerToken filled in trade. + function fillOrderNoThrow( + address[5] orderAddresses, + uint256[6] orderValues, + uint256 takerTokenFillAmount, + uint8 v, + bytes32 r, + bytes32 s) + public + returns (bool success, uint256 takerTokenFilledAmount); + + /// @dev Synchronously executes multiple calls of fillOrder in a single transaction. /// @param orderAddresses Array of address arrays containing individual order addresses. - /// @param orderValues Array of uint arrays containing individual order values. - /// @param fillTakerTokenAmounts Array of desired amounts of takerToken to fill in orders. - /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfers will fail before attempting. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmounts Array of desired amounts of takerToken to fill in orders. /// @param v Array ECDSA signature v parameters. /// @param r Array of ECDSA signature r parameters. /// @param s Array of ECDSA signature s parameters. function batchFillOrders( address[5][] orderAddresses, - uint[6][] orderValues, - uint[] fillTakerTokenAmounts, - bool shouldThrowOnInsufficientBalanceOrAllowance, + uint256[6][] orderValues, + uint256[] takerTokenFillAmounts, uint8[] v, bytes32[] r, bytes32[] s) - public; + external; - /// @dev Synchronously executes multiple fillOrKill orders in a single transaction. + /// @dev Synchronously executes multiple calls of fillOrKill in a single transaction. /// @param orderAddresses Array of address arrays containing individual order addresses. - /// @param orderValues Array of uint arrays containing individual order values. - /// @param fillTakerTokenAmounts Array of desired amounts of takerToken to fill in orders. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmounts Array of desired amounts of takerToken to fill in orders. /// @param v Array ECDSA signature v parameters. /// @param r Array of ECDSA signature r parameters. /// @param s Array of ECDSA signature s parameters. function batchFillOrKillOrders( address[5][] orderAddresses, - uint[6][] orderValues, - uint[] fillTakerTokenAmounts, + uint256[6][] orderValues, + uint256[] takerTokenFillAmounts, uint8[] v, bytes32[] r, bytes32[] s) - public; + external; - /// @dev Synchronously executes multiple fill orders in a single transaction until total fillTakerTokenAmount filled. + /// @dev Synchronously executes multiple calls of fillOrderNoThrow in a single transaction. /// @param orderAddresses Array of address arrays containing individual order addresses. - /// @param orderValues Array of uint arrays containing individual order values. - /// @param fillTakerTokenAmount Desired total amount of takerToken to fill in orders. - /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfers will fail before attempting. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmounts Array of desired amounts of takerToken to fill in orders. /// @param v Array ECDSA signature v parameters. /// @param r Array of ECDSA signature r parameters. /// @param s Array of ECDSA signature s parameters. - /// @return Total amount of fillTakerTokenAmount filled in orders. - function fillOrdersUpTo( + function batchFillOrdersNoThrow( address[5][] orderAddresses, - uint[6][] orderValues, - uint fillTakerTokenAmount, - bool shouldThrowOnInsufficientBalanceOrAllowance, + uint256[6][] orderValues, + uint256[] takerTokenFillAmounts, uint8[] v, bytes32[] r, bytes32[] s) - public - returns (uint); + external; + + /// @dev Synchronously executes multiple fill orders in a single transaction until total takerTokenFillAmount filled. + /// @param orderAddresses Array of address arrays containing individual order addresses. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmount Desired total amount of takerToken to fill in orders. + /// @param v Array ECDSA signature v parameters. + /// @param r Array of ECDSA signature r parameters. + /// @param s Array of ECDSA signature s parameters. + /// @return Total amount of takerTokenFillAmount filled in orders. + function marketFillOrders( + address[5][] orderAddresses, + uint256[6][] orderValues, + uint256 takerTokenFillAmount, + uint8[] v, + bytes32[] r, + bytes32[] s) + external + returns (uint256 totalTakerTokenFilledAmount); + + /// @dev Synchronously executes multiple calls of fillOrderNoThrow in a single transaction until total takerTokenFillAmount filled. + /// @param orderAddresses Array of address arrays containing individual order addresses. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmount Desired total amount of takerToken to fill in orders. + /// @param v Array ECDSA signature v parameters. + /// @param r Array of ECDSA signature r parameters. + /// @param s Array of ECDSA signature s parameters. + /// @return Total amount of takerTokenFillAmount filled in orders. + function marketFillOrdersNoThrow( + address[5][] orderAddresses, + uint256[6][] orderValues, + uint256 takerTokenFillAmount, + uint8[] v, + bytes32[] r, + bytes32[] s) + external + returns (uint256 totalTakerTokenFilledAmount); /// @dev Synchronously cancels multiple orders in a single transaction. /// @param orderAddresses Array of address arrays containing individual order addresses. - /// @param orderValues Array of uint arrays containing individual order values. - /// @param cancelTakerTokenAmounts Array of desired amounts of takerToken to cancel in orders. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenCancelAmounts Array of desired amounts of takerToken to cancel in orders. function batchCancelOrders( address[5][] orderAddresses, - uint[6][] orderValues, - uint[] cancelTakerTokenAmounts) - public; + uint256[6][] orderValues, + uint256[] takerTokenCancelAmounts) + external; } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol index 8f87d05a4..f4d031510 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol @@ -26,11 +26,11 @@ contract LibOrder { address makerToken; address takerToken; address feeRecipient; - uint makerTokenAmount; - uint takerTokenAmount; - uint makerFee; - uint takerFee; - uint expirationTimestampInSec; + uint256 makerTokenAmount; + uint256 takerTokenAmount; + uint256 makerFee; + uint256 takerFee; + uint256 expirationTimestampInSec; bytes32 orderHash; } @@ -38,11 +38,11 @@ contract LibOrder { /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. /// @return Keccak-256 hash of order. - function getOrderHash(address[5] orderAddresses, uint[6] orderValues) + function getOrderHash(address[5] orderAddresses, uint256[6] orderValues) public view - returns (bytes32) + returns (bytes32 orderHash) { - return keccak256( + orderHash = keccak256( address(this), orderAddresses[0], // maker orderAddresses[1], // taker @@ -56,5 +56,6 @@ contract LibOrder { orderValues[4], // expirationTimestampInSec orderValues[5] // salt ); + return orderHash; } } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol index 3dcae8341..cf4a65082 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol @@ -27,11 +27,12 @@ contract LibPartialAmount is SafeMath { /// @param denominator Denominator. /// @param target Value to calculate partial of. /// @return Partial value of target. - function getPartialAmount(uint numerator, uint denominator, uint target) + function getPartialAmount(uint256 numerator, uint256 denominator, uint256 target) public pure - returns (uint) + returns (uint256 partialAmount) { - return safeDiv(safeMul(numerator, target), denominator); + partialAmount = safeDiv(safeMul(numerator, target), denominator); + return partialAmount; } } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol index 836e9dd9b..922f9e6dc 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol @@ -39,8 +39,8 @@ contract MixinExchangeCore is LibPartialAmount { // Mappings of orderHash => amounts of takerTokenAmount filled or cancelled. - mapping (bytes32 => uint) public filled; - mapping (bytes32 => uint) public cancelled; + mapping (bytes32 => uint256) public filled; + mapping (bytes32 => uint256) public cancelled; event LogFill( address indexed maker, @@ -48,12 +48,11 @@ contract MixinExchangeCore is address indexed feeRecipient, address makerToken, address takerToken, - uint filledMakerTokenAmount, - uint filledTakerTokenAmount, - uint paidMakerFee, - uint paidTakerFee, - bytes32 indexed tokens, // keccak256(makerToken, takerToken), allows subscribing to a token pair - bytes32 orderHash + uint256 makerTokenFilledAmount, + uint256 takerTokenFilledAmount, + uint256 makerFeePaid, + uint256 takerFeePaid, + bytes32 indexed orderHash ); event LogCancel( @@ -61,10 +60,9 @@ contract MixinExchangeCore is address indexed feeRecipient, address makerToken, address takerToken, - uint cancelledMakerTokenAmount, - uint cancelledTakerTokenAmount, - bytes32 indexed tokens, - bytes32 orderHash + uint256 makerTokenCancelledAmount, + uint256 takerTokenCancelledAmount, + bytes32 indexed orderHash ); /* @@ -74,22 +72,20 @@ contract MixinExchangeCore is /// @dev Fills the input order. /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. - /// @param fillTakerTokenAmount Desired amount of takerToken to fill. - /// @param shouldThrowOnInsufficientBalanceOrAllowance Test if transfer will fail before attempting. + /// @param takerTokenFillAmount Desired amount of takerToken to fill. /// @param v ECDSA signature parameter v. /// @param r ECDSA signature parameters r. /// @param s ECDSA signature parameters s. /// @return Total amount of takerToken filled in trade. function fillOrder( address[5] orderAddresses, - uint[6] orderValues, - uint fillTakerTokenAmount, - bool shouldThrowOnInsufficientBalanceOrAllowance, + uint256[6] orderValues, + uint256 takerTokenFillAmount, uint8 v, bytes32 r, bytes32 s) public - returns (uint filledTakerTokenAmount) + returns (uint256 takerTokenFilledAmount) { Order memory order = Order({ maker: orderAddresses[0], @@ -106,7 +102,7 @@ contract MixinExchangeCore is }); require(order.taker == address(0) || order.taker == msg.sender); - require(order.makerTokenAmount > 0 && order.takerTokenAmount > 0 && fillTakerTokenAmount > 0); + require(order.makerTokenAmount > 0 && order.takerTokenAmount > 0 && takerTokenFillAmount > 0); require(isValidSignature( order.maker, order.orderHash, @@ -120,32 +116,27 @@ contract MixinExchangeCore is return 0; } - uint remainingTakerTokenAmount = safeSub(order.takerTokenAmount, getUnavailableTakerTokenAmount(order.orderHash)); - filledTakerTokenAmount = min256(fillTakerTokenAmount, remainingTakerTokenAmount); - if (filledTakerTokenAmount == 0) { + uint256 remainingTakerTokenAmount = safeSub(order.takerTokenAmount, getUnavailableTakerTokenAmount(order.orderHash)); + takerTokenFilledAmount = min256(takerTokenFillAmount, remainingTakerTokenAmount); + if (takerTokenFilledAmount == 0) { LogError(uint8(Errors.ORDER_FULLY_FILLED_OR_CANCELLED), order.orderHash); return 0; } - if (isRoundingError(filledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount)) { + if (isRoundingError(takerTokenFilledAmount, order.takerTokenAmount, order.makerTokenAmount)) { LogError(uint8(Errors.ROUNDING_ERROR_TOO_LARGE), order.orderHash); return 0; } - if (!shouldThrowOnInsufficientBalanceOrAllowance && !isTransferable(order, filledTakerTokenAmount)) { - LogError(uint8(Errors.INSUFFICIENT_BALANCE_OR_ALLOWANCE), order.orderHash); - return 0; - } - // Update state - filled[order.orderHash] = safeAdd(filled[order.orderHash], filledTakerTokenAmount); + filled[order.orderHash] = safeAdd(filled[order.orderHash], takerTokenFilledAmount); // Settle order uint256 filledMakerTokenAmount; - uint256 paidMakerFee; - uint256 paidTakerFee; - (filledMakerTokenAmount, paidMakerFee, paidTakerFee) = - settleOrder(order, msg.sender, filledTakerTokenAmount); + uint256 makerFeePaid; + uint256 takerFeePaid; + (filledMakerTokenAmount, makerFeePaid, takerFeePaid) = + settleOrder(order, msg.sender, takerTokenFilledAmount); // Log order LogFill( @@ -155,26 +146,25 @@ contract MixinExchangeCore is order.makerToken, order.takerToken, filledMakerTokenAmount, - filledTakerTokenAmount, - paidMakerFee, - paidTakerFee, - keccak256(order.makerToken, order.takerToken), + takerTokenFilledAmount, + makerFeePaid, + takerFeePaid, order.orderHash ); - return filledTakerTokenAmount; + return takerTokenFilledAmount; } /// @dev Cancels the input order. /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. - /// @param cancelTakerTokenAmount Desired amount of takerToken to cancel in order. + /// @param takerTokenCancelAmount Desired amount of takerToken to cancel in order. /// @return Amount of takerToken cancelled. function cancelOrder( address[5] orderAddresses, - uint[6] orderValues, - uint cancelTakerTokenAmount) + uint256[6] orderValues, + uint256 takerTokenCancelAmount) public - returns (uint) + returns (uint256 takerTokenCancelledAmount) { Order memory order = Order({ maker: orderAddresses[0], @@ -191,33 +181,32 @@ contract MixinExchangeCore is }); require(order.maker == msg.sender); - require(order.makerTokenAmount > 0 && order.takerTokenAmount > 0 && cancelTakerTokenAmount > 0); + require(order.makerTokenAmount > 0 && order.takerTokenAmount > 0 && takerTokenCancelAmount > 0); if (block.timestamp >= order.expirationTimestampInSec) { LogError(uint8(Errors.ORDER_EXPIRED), order.orderHash); return 0; } - uint remainingTakerTokenAmount = safeSub(order.takerTokenAmount, getUnavailableTakerTokenAmount(order.orderHash)); - uint cancelledTakerTokenAmount = min256(cancelTakerTokenAmount, remainingTakerTokenAmount); - if (cancelledTakerTokenAmount == 0) { + uint256 remainingTakerTokenAmount = safeSub(order.takerTokenAmount, getUnavailableTakerTokenAmount(order.orderHash)); + takerTokenCancelledAmount = min256(takerTokenCancelAmount, remainingTakerTokenAmount); + if (takerTokenCancelledAmount == 0) { LogError(uint8(Errors.ORDER_FULLY_FILLED_OR_CANCELLED), order.orderHash); return 0; } - cancelled[order.orderHash] = safeAdd(cancelled[order.orderHash], cancelledTakerTokenAmount); + cancelled[order.orderHash] = safeAdd(cancelled[order.orderHash], takerTokenCancelledAmount); LogCancel( order.maker, order.feeRecipient, order.makerToken, order.takerToken, - getPartialAmount(cancelledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount), - cancelledTakerTokenAmount, - keccak256(order.makerToken, order.takerToken), + getPartialAmount(takerTokenCancelledAmount, order.takerTokenAmount, order.makerTokenAmount), + takerTokenCancelledAmount, order.orderHash ); - return cancelledTakerTokenAmount; + return takerTokenCancelledAmount; } /// @dev Checks if rounding error > 0.1%. @@ -225,29 +214,29 @@ contract MixinExchangeCore is /// @param denominator Denominator. /// @param target Value to multiply with numerator/denominator. /// @return Rounding error is present. - function isRoundingError(uint numerator, uint denominator, uint target) - public - pure - returns (bool) + function isRoundingError(uint256 numerator, uint256 denominator, uint256 target) + public pure + returns (bool isError) { - uint remainder = mulmod(target, numerator, denominator); + uint256 remainder = mulmod(target, numerator, denominator); if (remainder == 0) return false; // No rounding error. - uint errPercentageTimes1000000 = safeDiv( + uint256 errPercentageTimes1000000 = safeDiv( safeMul(remainder, 1000000), safeMul(numerator, target) ); - return errPercentageTimes1000000 > 1000; + isError = errPercentageTimes1000000 > 1000; + return isError; } - /// @dev Calculates the sum of values already filled and cancelled for a given order. + /// @dev Calculates the sum of values already filled and cancelled for a given order. /// @param orderHash The Keccak-256 hash of the given order. /// @return Sum of values already filled and cancelled. function getUnavailableTakerTokenAmount(bytes32 orderHash) - public - constant - returns (uint) + public view + returns (uint256 unavailableTakerTokenAmount) { - return safeAdd(filled[orderHash], cancelled[orderHash]); + unavailableTakerTokenAmount = safeAdd(filled[orderHash], cancelled[orderHash]); + return unavailableTakerTokenAmount; } } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol index d66223b4d..85488b383 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol @@ -29,8 +29,6 @@ contract MixinSettlementProxy is LibPartialAmount { - uint16 constant public EXTERNAL_QUERY_GAS_LIMIT = 4999; // Changes to state require at least 5000 gas - address public TOKEN_TRANSFER_PROXY_CONTRACT; address public ZRX_TOKEN_CONTRACT; @@ -38,54 +36,55 @@ contract MixinSettlementProxy is function MixinSettlementProxy(address proxyContract, address zrxToken) public { - ZRX_TOKEN_CONTRACT = zrxToken; - TOKEN_TRANSFER_PROXY_CONTRACT = proxyContract; + ZRX_TOKEN_CONTRACT = zrxToken; + TOKEN_TRANSFER_PROXY_CONTRACT = proxyContract; } function settleOrder( Order order, address taker, - uint filledTakerTokenAmount) + uint256 takerTokenFilledAmount) internal returns ( - uint filledMakerTokenAmount, - uint paidMakerFee, - uint paidTakerFee + uint256 makerTokenFilledAmount, + uint256 makerFeePaid, + uint256 takerFeePaid ) { - filledMakerTokenAmount = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount); + makerTokenFilledAmount = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerTokenAmount); require(transferViaTokenTransferProxy( order.makerToken, order.maker, taker, - filledMakerTokenAmount + makerTokenFilledAmount )); require(transferViaTokenTransferProxy( order.takerToken, taker, order.maker, - filledTakerTokenAmount + takerTokenFilledAmount )); if (order.feeRecipient != address(0)) { if (order.makerFee > 0) { - paidMakerFee = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.makerFee); + makerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerFee); require(transferViaTokenTransferProxy( ZRX_TOKEN_CONTRACT, order.maker, order.feeRecipient, - paidMakerFee + makerFeePaid )); } if (order.takerFee > 0) { - paidTakerFee = getPartialAmount(filledTakerTokenAmount, order.takerTokenAmount, order.takerFee); + takerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.takerFee); require(transferViaTokenTransferProxy( ZRX_TOKEN_CONTRACT, taker, order.feeRecipient, - paidTakerFee + takerFeePaid )); } } + return (makerTokenFilledAmount, makerFeePaid, takerFeePaid); } /// @dev Transfers a token using TokenTransferProxy transferFrom function. @@ -98,75 +97,12 @@ contract MixinSettlementProxy is address token, address from, address to, - uint value) + uint256 value) internal - returns (bool) + returns (bool success) { - return ITokenTransferProxy(TOKEN_TRANSFER_PROXY_CONTRACT).transferFrom(token, from, to, value); + success = ITokenTransferProxy(TOKEN_TRANSFER_PROXY_CONTRACT).transferFrom(token, from, to, value); + return success; } - /// @dev Checks if any order transfers will fail. - /// @param order Order struct of params that will be checked. - /// @param fillTakerTokenAmount Desired amount of takerToken to fill. - /// @return Predicted result of transfers. - function isTransferable(Order order, uint fillTakerTokenAmount) - internal - constant // The called token contracts may attempt to change state, but will not be able to due to gas limits on getBalance and getAllowance. - returns (bool) - { - address taker = msg.sender; - uint fillMakerTokenAmount = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.makerTokenAmount); - - if (order.feeRecipient != address(0)) { - bool isMakerTokenZRX = order.makerToken == ZRX_TOKEN_CONTRACT; - bool isTakerTokenZRX = order.takerToken == ZRX_TOKEN_CONTRACT; - uint paidMakerFee = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.makerFee); - uint paidTakerFee = getPartialAmount(fillTakerTokenAmount, order.takerTokenAmount, order.takerFee); - uint requiredMakerZRX = isMakerTokenZRX ? safeAdd(fillMakerTokenAmount, paidMakerFee) : paidMakerFee; - uint requiredTakerZRX = isTakerTokenZRX ? safeAdd(fillTakerTokenAmount, paidTakerFee) : paidTakerFee; - - if ( getBalance(ZRX_TOKEN_CONTRACT, order.maker) < requiredMakerZRX - || getAllowance(ZRX_TOKEN_CONTRACT, order.maker) < requiredMakerZRX - || getBalance(ZRX_TOKEN_CONTRACT, taker) < requiredTakerZRX - || getAllowance(ZRX_TOKEN_CONTRACT, taker) < requiredTakerZRX - ) return false; - - if (!isMakerTokenZRX && ( getBalance(order.makerToken, order.maker) < fillMakerTokenAmount // Don't double check makerToken if ZRX - || getAllowance(order.makerToken, order.maker) < fillMakerTokenAmount) - ) return false; - if (!isTakerTokenZRX && ( getBalance(order.takerToken, taker) < fillTakerTokenAmount // Don't double check takerToken if ZRX - || getAllowance(order.takerToken, taker) < fillTakerTokenAmount) - ) return false; - } else if ( getBalance(order.makerToken, order.maker) < fillMakerTokenAmount - || getAllowance(order.makerToken, order.maker) < fillMakerTokenAmount - || getBalance(order.takerToken, taker) < fillTakerTokenAmount - || getAllowance(order.takerToken, taker) < fillTakerTokenAmount - ) return false; - - return true; - } - - /// @dev Get token balance of an address. - /// @param token Address of token. - /// @param owner Address of owner. - /// @return Token balance of owner. - function getBalance(address token, address owner) - internal - constant // The called token contract may attempt to change state, but will not be able to due to an added gas limit. - returns (uint) - { - return IToken(token).balanceOf.gas(EXTERNAL_QUERY_GAS_LIMIT)(owner); // Limit gas to prevent reentrancy - } - - /// @dev Get allowance of token given to TokenTransferProxy by an address. - /// @param token Address of token. - /// @param owner Address of owner. - /// @return Allowance of token given to TokenTransferProxy by owner. - function getAllowance(address token, address owner) - internal - constant // The called token contract may attempt to change state, but will not be able to due to an added gas limit. - returns (uint) - { - return IToken(token).allowance.gas(EXTERNAL_QUERY_GAS_LIMIT)(owner, TOKEN_TRANSFER_PROXY_CONTRACT); // Limit gas to prevent reentrancy - } } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidatorEcrecover.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidatorEcrecover.sol index 08eb15344..7c9155921 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidatorEcrecover.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidatorEcrecover.sol @@ -32,13 +32,14 @@ contract MixinSignatureValidatorEcrecover is bytes32 s) public constant - returns (bool) + returns (bool isValid) { - return signer == ecrecover( + isValid = signer == ecrecover( keccak256("\x19Ethereum Signed Message:\n32", hash), v, r, s ); + return isValid; } } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol index eb1a7b6f5..fb91cf9d6 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol @@ -27,17 +27,17 @@ contract MixinWrapperFunctions is SafeMath { - /// @dev Fills an order with specified parameters and ECDSA signature, throws if specified amount not filled entirely. + /// @dev Fills an order with specified parameters and ECDSA signature. Throws if specified amount not filled entirely. /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. - /// @param fillTakerTokenAmount Desired amount of takerToken to fill. + /// @param takerTokenFillAmount Desired amount of takerToken to fill. /// @param v ECDSA signature parameter v. /// @param r ECDSA signature parameters r. /// @param s ECDSA signature parameters s. function fillOrKillOrder( address[5] orderAddresses, - uint[6] orderValues, - uint fillTakerTokenAmount, + uint256[6] orderValues, + uint256 takerTokenFillAmount, uint8 v, bytes32 r, bytes32 s) @@ -46,30 +46,90 @@ contract MixinWrapperFunctions is require(fillOrder( orderAddresses, orderValues, - fillTakerTokenAmount, - false, + takerTokenFillAmount, v, r, s - ) == fillTakerTokenAmount); + ) == takerTokenFillAmount); } + /// @dev Fills an order with specified parameters and ECDSA signature. Returns false if the transaction would otherwise revert. + /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient. + /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt. + /// @param takerTokenFillAmount Desired amount of takerToken to fill. + /// @param v ECDSA signature parameter v. + /// @param r ECDSA signature parameters r. + /// @param s ECDSA signature parameters s. + /// @return Success if the transaction did not revert. + /// @return Total amount of takerToken filled in trade. + function fillOrderNoThrow( + address[5] orderAddresses, + uint256[6] orderValues, + uint256 takerTokenFillAmount, + uint8 v, + bytes32 r, + bytes32 s) + public + returns (bool success, uint256 takerTokenFilledAmount) + { + bytes4 FILL_ORDER_FUNCTION_SIGNATURE = bytes4(keccak256("fillOrder(address[5],uint256[6],uint256,uint8,bytes32,bytes32)")); + + assembly { + let x := mload(0x40) // free memory pointer + mstore(x, FILL_ORDER_FUNCTION_SIGNATURE) + + // first 32 bytes of an array contains length + mstore(add(x, 4), add(orderAddresses, 32)) // maker + mstore(add(x, 36), add(orderAddresses, 64)) // taker + mstore(add(x, 68), add(orderAddresses, 96)) // makerToken + mstore(add(x, 100), add(orderAddresses, 128)) // takerToken + mstore(add(x, 132), add(orderAddresses, 160)) // feeRecipient + mstore(add(x, 164), add(orderValues, 32)) // makerTokenAmount + mstore(add(x, 196), add(orderValues, 64)) // takerTokenAmount + mstore(add(x, 228), add(orderValues, 96)) // makerFee + mstore(add(x, 260), add(orderValues, 128)) // takerFee + mstore(add(x, 292), add(orderValues, 160)) // expirationTimestampInSec + mstore(add(x, 324), add(orderValues, 192)) // salt + mstore(add(x, 356), takerTokenFillAmount) + mstore(add(x, 388), v) + mstore(add(x, 420), r) + mstore(add(x, 452), s) + + success := delegatecall( + gas, // TODO: don't send all gas, save some for returning is case of throw + address, // call this contract + x, // inputs start at x + 484, // inputs are 484 bytes long (4 + 15*32) + x, // store output over input + 32 // output is 32 bytes + ) + + takerTokenFilledAmount := mload(x) + } + return (success, takerTokenFilledAmount); + } + + /// @dev Synchronously executes multiple calls of fillOrder in a single transaction. + /// @param orderAddresses Array of address arrays containing individual order addresses. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmounts Array of desired amounts of takerToken to fill in orders. + /// @param v Array ECDSA signature v parameters. + /// @param r Array of ECDSA signature r parameters. + /// @param s Array of ECDSA signature s parameters. function batchFillOrders( address[5][] orderAddresses, - uint[6][] orderValues, - uint[] fillTakerTokenAmounts, - bool shouldThrowOnInsufficientBalanceOrAllowance, + uint256[6][] orderValues, + uint256[] takerTokenFillAmounts, uint8[] v, bytes32[] r, bytes32[] s) external { - for (uint i = 0; i < orderAddresses.length; i++) { + for (uint256 i = 0; i < orderAddresses.length; i++) { fillOrder( orderAddresses[i], orderValues[i], - fillTakerTokenAmounts[i], - shouldThrowOnInsufficientBalanceOrAllowance, + takerTokenFillAmounts[i], v[i], r[i], s[i] @@ -77,20 +137,27 @@ contract MixinWrapperFunctions is } } + /// @dev Synchronously executes multiple calls of fillOrKill in a single transaction. + /// @param orderAddresses Array of address arrays containing individual order addresses. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmounts Array of desired amounts of takerToken to fill in orders. + /// @param v Array ECDSA signature v parameters. + /// @param r Array of ECDSA signature r parameters. + /// @param s Array of ECDSA signature s parameters. function batchFillOrKillOrders( address[5][] orderAddresses, - uint[6][] orderValues, - uint[] fillTakerTokenAmounts, + uint256[6][] orderValues, + uint256[] takerTokenFillAmounts, uint8[] v, bytes32[] r, bytes32[] s) external { - for (uint i = 0; i < orderAddresses.length; i++) { + for (uint256 i = 0; i < orderAddresses.length; i++) { fillOrKillOrder( orderAddresses[i], orderValues[i], - fillTakerTokenAmounts[i], + takerTokenFillAmounts[i], v[i], r[i], s[i] @@ -98,45 +165,116 @@ contract MixinWrapperFunctions is } } - function fillOrdersUpTo( + /// @dev Synchronously executes multiple calls of fillOrderNoThrow in a single transaction. + /// @param orderAddresses Array of address arrays containing individual order addresses. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmounts Array of desired amounts of takerToken to fill in orders. + /// @param v Array ECDSA signature v parameters. + /// @param r Array of ECDSA signature r parameters. + /// @param s Array of ECDSA signature s parameters. + function batchFillOrdersNoThrow( address[5][] orderAddresses, - uint[6][] orderValues, - uint fillTakerTokenAmount, - bool shouldThrowOnInsufficientBalanceOrAllowance, + uint256[6][] orderValues, + uint256[] takerTokenFillAmounts, uint8[] v, bytes32[] r, bytes32[] s) - public /// Stack to deep when set to external - returns (uint) + external { - uint filledTakerTokenAmount = 0; - for (uint i = 0; i < orderAddresses.length; i++) { + for (uint256 i = 0; i < orderAddresses.length; i++) { + fillOrderNoThrow( + orderAddresses[i], + orderValues[i], + takerTokenFillAmounts[i], + v[i], + r[i], + s[i] + ); + } + } + + /// @dev Synchronously executes multiple fill orders in a single transaction until total takerTokenFillAmount filled. + /// @param orderAddresses Array of address arrays containing individual order addresses. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmount Desired total amount of takerToken to fill in orders. + /// @param v Array ECDSA signature v parameters. + /// @param r Array of ECDSA signature r parameters. + /// @param s Array of ECDSA signature s parameters. + /// @return Total amount of takerTokenFillAmount filled in orders. + function marketFillOrders( + address[5][] orderAddresses, + uint256[6][] orderValues, + uint256 takerTokenFillAmount, + uint8[] v, + bytes32[] r, + bytes32[] s) + external + returns (uint256 totalTakerTokenFilledAmount) + { + for (uint256 i = 0; i < orderAddresses.length; i++) { require(orderAddresses[i][3] == orderAddresses[0][3]); // takerToken must be the same for each order - filledTakerTokenAmount = safeAdd(filledTakerTokenAmount, fillOrder( + totalTakerTokenFilledAmount = safeAdd(totalTakerTokenFilledAmount, fillOrder( orderAddresses[i], orderValues[i], - safeSub(fillTakerTokenAmount, filledTakerTokenAmount), - shouldThrowOnInsufficientBalanceOrAllowance, + safeSub(takerTokenFillAmount, totalTakerTokenFilledAmount), v[i], r[i], s[i] )); - if (filledTakerTokenAmount == fillTakerTokenAmount) break; + if (totalTakerTokenFilledAmount == takerTokenFillAmount) break; + } + return totalTakerTokenFilledAmount; + } + + /// @dev Synchronously executes multiple calls of fillOrderNoThrow in a single transaction until total takerTokenFillAmount filled. + /// @param orderAddresses Array of address arrays containing individual order addresses. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenFillAmount Desired total amount of takerToken to fill in orders. + /// @param v Array ECDSA signature v parameters. + /// @param r Array of ECDSA signature r parameters. + /// @param s Array of ECDSA signature s parameters. + /// @return Total amount of takerTokenFillAmount filled in orders. + function marketFillOrdersNoThrow( + address[5][] orderAddresses, + uint256[6][] orderValues, + uint256 takerTokenFillAmount, + uint8[] v, + bytes32[] r, + bytes32[] s) + external + returns (uint256 totalTakerTokenFilledAmount) + { + for (uint256 i = 0; i < orderAddresses.length; i++) { + require(orderAddresses[i][3] == orderAddresses[0][3]); // takerToken must be the same for each order + var (, takerTokenFilledAmount) = fillOrderNoThrow( + orderAddresses[i], + orderValues[i], + safeSub(takerTokenFillAmount, totalTakerTokenFilledAmount), + v[i], + r[i], + s[i] + ); + totalTakerTokenFilledAmount = safeAdd(totalTakerTokenFilledAmount, takerTokenFilledAmount); + if (totalTakerTokenFilledAmount == takerTokenFillAmount) break; } - return filledTakerTokenAmount; + return totalTakerTokenFilledAmount; } + /// @dev Synchronously cancels multiple orders in a single transaction. + /// @param orderAddresses Array of address arrays containing individual order addresses. + /// @param orderValues Array of uint256 arrays containing individual order values. + /// @param takerTokenCancelAmounts Array of desired amounts of takerToken to cancel in orders. function batchCancelOrders( address[5][] orderAddresses, - uint[6][] orderValues, - uint[] cancelTakerTokenAmounts) + uint256[6][] orderValues, + uint256[] takerTokenCancelAmounts) external { - for (uint i = 0; i < orderAddresses.length; i++) { + for (uint256 i = 0; i < orderAddresses.length; i++) { cancelOrder( orderAddresses[i], orderValues[i], - cancelTakerTokenAmounts[i] + takerTokenCancelAmounts[i] ); } } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol index 2525b6930..3977c3bbd 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol @@ -21,21 +21,20 @@ pragma solidity ^0.4.19; contract MExchangeCore { function fillOrder( - address[5] orderAddresses, - uint[6] orderValues, - uint fillTakerTokenAmount, - bool shouldThrowOnInsufficientBalanceOrAllowance, - uint8 v, - bytes32 r, - bytes32 s) - public - returns (uint filledTakerTokenAmount); + address[5] orderAddresses, + uint256[6] orderValues, + uint256 takerTokenFillAmount, + uint8 v, + bytes32 r, + bytes32 s) + public + returns (uint256 takerTokenFilledAmount); function cancelOrder( address[5] orderAddresses, - uint[6] orderValues, - uint cancelTakerTokenAmount) + uint256[6] orderValues, + uint256 takerTokenCancelAmount) public - returns (uint); + returns (uint256 takerTokenCancelledAmount); } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol index 0988050af..9f9e0996f 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol @@ -25,20 +25,12 @@ contract MSettlement is LibOrder { function settleOrder( Order order, address taker, - uint filledTakerTokenAmount) + uint256 takerTokenFilledAmount) internal returns ( - uint filledMakerTokenAmount, - uint paidMakerFee, - uint paidTakerFee + uint256 makerTokenFilledAmount, + uint256 makerFeePaid, + uint256 takerFeePaid ); - /// @dev Checks if any order transfers will fail. - /// @param order Order struct of params that will be checked. - /// @param fillTakerTokenAmount Desired amount of takerToken to fill. - /// @return Predicted result of transfers. - function isTransferable(Order order, uint fillTakerTokenAmount) - internal - constant // The called token contracts may attempt to change state, but will not be able to due to gas limits on getBalance and getAllowance. - returns (bool); } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol index 7321db24f..b4ca4bb48 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol @@ -34,5 +34,5 @@ contract MSignatureValidator { bytes32 r, bytes32 s) public view - returns (bool); + returns (bool isValid); } |