@0x/contracts-exchange
: Rename marketSellOrders
and marketBuyOrders
back to marketSellOrdersNoThrow
and marketBuyOrdersNoThrow
.
`@0x/contracts-exchange`: Introduce new `marketSellOrdersFillOrKill` and `marketBuyOrdersFillOrKill` functions. `@0x/contracts-exchange`: Add new rich error types: `IncompleteMarketBuyError` and `IncompleteMarketSellError`. `@0x/contracts-exchange`: Use `abi.decode()` in `LibExchangeRichErrorDecoder` over `LibBytes`.
This commit is contained in:
@@ -170,7 +170,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
function marketSellOrders(
|
||||
function marketSellOrdersNoThrow(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes[] memory signatures
|
||||
@@ -214,7 +214,7 @@ contract MixinWrapperFunctions is
|
||||
/// @param makerAssetFillAmount Desired amount of makerAsset to buy.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
function marketBuyOrders(
|
||||
function marketBuyOrdersNoThrow(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 makerAssetFillAmount,
|
||||
bytes[] memory signatures
|
||||
@@ -261,6 +261,50 @@ contract MixinWrapperFunctions is
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
/// @dev Calls marketSellOrdersNoThrow then reverts if < takerAssetFillAmount has been sold.
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param takerAssetFillAmount Minimum amount of takerAsset to sell.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
function marketSellOrdersFillOrKill(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
public
|
||||
returns (LibFillResults.FillResults memory fillResults)
|
||||
{
|
||||
fillResults = marketSellOrdersNoThrow(orders, takerAssetFillAmount, signatures);
|
||||
if (fillResults.takerAssetFilledAmount < takerAssetFillAmount) {
|
||||
LibRichErrors.rrevert(LibExchangeRichErrors.IncompleteMarketSellError(
|
||||
takerAssetFillAmount,
|
||||
_getOrderHashes(orders)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Calls marketBuyOrdersNoThrow then reverts if < makerAssetFillAmount has been bought.
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param makerAssetFillAmount Minimum amount of makerAsset to buy.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
function marketBuyOrdersFillOrKill(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 makerAssetFillAmount,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
public
|
||||
returns (LibFillResults.FillResults memory fillResults)
|
||||
{
|
||||
fillResults = marketBuyOrdersNoThrow(orders, makerAssetFillAmount, signatures);
|
||||
if (fillResults.makerAssetFilledAmount < makerAssetFillAmount) {
|
||||
LibRichErrors.rrevert(LibExchangeRichErrors.IncompleteMarketBuyError(
|
||||
makerAssetFillAmount,
|
||||
_getOrderHashes(orders)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Executes multiple calls of cancelOrder.
|
||||
/// @param orders Array of order specifications.
|
||||
function batchCancelOrders(LibOrder.Order[] memory orders)
|
||||
@@ -308,9 +352,25 @@ contract MixinWrapperFunctions is
|
||||
);
|
||||
if (fillResults.takerAssetFilledAmount != takerAssetFillAmount) {
|
||||
LibRichErrors.rrevert(LibExchangeRichErrors.IncompleteFillError(
|
||||
takerAssetFillAmount,
|
||||
getOrderInfo(order).orderHash
|
||||
));
|
||||
}
|
||||
return fillResults;
|
||||
}
|
||||
|
||||
/// @dev Computes the hashes of multiple orders.
|
||||
/// @param orders Array of orders.
|
||||
/// @return orderHashes Array of order hashes for each respective order in `orders`.
|
||||
function _getOrderHashes(LibOrder.Order[] memory orders)
|
||||
internal
|
||||
view
|
||||
returns (bytes32[] memory orderHashes)
|
||||
{
|
||||
uint256 ordersLength = orders.length;
|
||||
orderHashes = new bytes32[](ordersLength);
|
||||
for (uint256 i = 0; i != ordersLength; i++) {
|
||||
orderHashes[i] = orders[i].getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -96,7 +96,7 @@ contract IWrapperFunctions {
|
||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
function marketSellOrders(
|
||||
function marketSellOrdersNoThrow(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes[] memory signatures
|
||||
@@ -109,7 +109,33 @@ contract IWrapperFunctions {
|
||||
/// @param makerAssetFillAmount Desired amount of makerAsset to buy.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
function marketBuyOrders(
|
||||
function marketBuyOrdersNoThrow(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 makerAssetFillAmount,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
public
|
||||
returns (LibFillResults.FillResults memory fillResults);
|
||||
|
||||
/// @dev Calls marketSellOrdersNoThrow then reverts if < takerAssetFillAmount has been sold.
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param takerAssetFillAmount Minimum amount of takerAsset to sell.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
function marketSellOrdersFillOrKill(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes[] memory signatures
|
||||
)
|
||||
public
|
||||
returns (LibFillResults.FillResults memory fillResults);
|
||||
|
||||
/// @dev Calls marketBuyOrdersNoThrow then reverts if < makerAssetFillAmount has been bought.
|
||||
/// @param orders Array of order specifications.
|
||||
/// @param makerAssetFillAmount Minimum amount of makerAsset to buy.
|
||||
/// @param signatures Proofs that orders have been signed by makers.
|
||||
/// @return Amounts filled and fees paid by makers and taker.
|
||||
function marketBuyOrdersFillOrKill(
|
||||
LibOrder.Order[] memory orders,
|
||||
uint256 makerAssetFillAmount,
|
||||
bytes[] memory signatures
|
||||
|
@@ -25,6 +25,8 @@ import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
|
||||
contract LibExchangeRichErrorDecoder {
|
||||
|
||||
using LibBytes for bytes;
|
||||
|
||||
/// @dev Decompose an ABI-encoded SignatureError.
|
||||
/// @param encoded ABI-encoded revert error.
|
||||
/// @return errorCode The error code.
|
||||
@@ -41,10 +43,12 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureErrorSelector());
|
||||
errorCode = LibExchangeRichErrors.SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
||||
hash = _readErrorParameterAsBytes32(encoded, 1);
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 2);
|
||||
signature = _readErrorParameterAsBytes(encoded, 3);
|
||||
uint8 _errorCode;
|
||||
(_errorCode, hash, signerAddress, signature) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(uint8, bytes32, address, bytes)
|
||||
);
|
||||
errorCode = LibExchangeRichErrors.SignatureErrorCodes(_errorCode);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded SignatureValidatorError.
|
||||
@@ -64,11 +68,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureValidatorErrorSelector());
|
||||
hash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
validatorAddress = _readErrorParameterAsAddress(encoded, 2);
|
||||
signature = _readErrorParameterAsBytes(encoded, 3);
|
||||
errorData = _readErrorParameterAsBytes(encoded, 4);
|
||||
(hash, signerAddress, validatorAddress, signature, errorData) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, address, address, bytes, bytes)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded SignatureValidatorNotApprovedError.
|
||||
@@ -84,8 +87,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureValidatorNotApprovedErrorSelector());
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 0);
|
||||
validatorAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
(signerAddress, validatorAddress) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(address, address)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded SignatureWalletError.
|
||||
@@ -105,10 +110,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.SignatureWalletErrorSelector());
|
||||
hash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
signature = _readErrorParameterAsBytes(encoded, 2);
|
||||
errorData = _readErrorParameterAsBytes(encoded, 3);
|
||||
(hash, signerAddress, signature, errorData) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, address, bytes, bytes)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded OrderStatusError.
|
||||
@@ -124,8 +129,12 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.OrderStatusErrorSelector());
|
||||
orderHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
orderStatus = LibOrder.OrderStatus(_readErrorParameterAsUint256(encoded, 1));
|
||||
uint8 _orderStatus;
|
||||
(orderHash, _orderStatus) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, uint8)
|
||||
);
|
||||
orderStatus = LibOrder.OrderStatus(_orderStatus);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded InvalidSenderError.
|
||||
@@ -141,8 +150,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.InvalidSenderErrorSelector());
|
||||
orderHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
senderAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
(orderHash, senderAddress) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, address)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded InvalidMakerError.
|
||||
@@ -158,8 +169,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.InvalidMakerErrorSelector());
|
||||
orderHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
makerAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
(orderHash, makerAddress) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, address)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded InvalidTaker.
|
||||
@@ -175,8 +188,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.InvalidTakerErrorSelector());
|
||||
orderHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
takerAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
(orderHash, takerAddress) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, address)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded FillError.
|
||||
@@ -192,8 +207,12 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.FillErrorSelector());
|
||||
errorCode = LibExchangeRichErrors.FillErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
||||
orderHash = _readErrorParameterAsBytes32(encoded, 1);
|
||||
uint8 _errorCode;
|
||||
(_errorCode, orderHash) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(uint8, bytes32)
|
||||
);
|
||||
errorCode = LibExchangeRichErrors.FillErrorCodes(_errorCode);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded OrderEpochError.
|
||||
@@ -211,9 +230,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.OrderEpochErrorSelector());
|
||||
makerAddress = _readErrorParameterAsAddress(encoded, 0);
|
||||
orderSenderAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
currentEpoch = _readErrorParameterAsUint256(encoded, 2);
|
||||
(makerAddress, orderSenderAddress, currentEpoch) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(address, address, uint256)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded AssetProxyExistsError.
|
||||
@@ -225,7 +245,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
returns (address assetProxyAddress)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyExistsErrorSelector());
|
||||
assetProxyAddress = _readErrorParameterAsAddress(encoded, 0);
|
||||
(assetProxyAddress) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(address)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded AssetProxyDispatchError.
|
||||
@@ -243,9 +266,12 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyDispatchErrorSelector());
|
||||
errorCode = LibExchangeRichErrors.AssetProxyDispatchErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
||||
orderHash = _readErrorParameterAsBytes32(encoded, 1);
|
||||
assetData = _readErrorParameterAsBytes(encoded, 2);
|
||||
uint8 _errorCode;
|
||||
(_errorCode, orderHash, assetData) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(uint8, bytes32, bytes)
|
||||
);
|
||||
errorCode = LibExchangeRichErrors.AssetProxyDispatchErrorCodes(_errorCode);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded AssetProxyTransferError.
|
||||
@@ -263,9 +289,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.AssetProxyTransferErrorSelector());
|
||||
orderHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
assetData = _readErrorParameterAsBytes(encoded, 1);
|
||||
errorData = _readErrorParameterAsBytes(encoded, 2);
|
||||
(orderHash, assetData, errorData) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, bytes, bytes)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded NegativeSpreadError.
|
||||
@@ -281,8 +308,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.NegativeSpreadErrorSelector());
|
||||
leftOrderHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
rightOrderHash = _readErrorParameterAsBytes32(encoded, 1);
|
||||
(leftOrderHash, rightOrderHash) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, bytes32)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded TransactionError.
|
||||
@@ -298,8 +327,12 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.TransactionErrorSelector());
|
||||
errorCode = LibExchangeRichErrors.TransactionErrorCodes(_readErrorParameterAsUint256(encoded, 0));
|
||||
transactionHash = _readErrorParameterAsBytes32(encoded, 1);
|
||||
uint8 _errorCode;
|
||||
(_errorCode, transactionHash) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(uint8, bytes32)
|
||||
);
|
||||
errorCode = LibExchangeRichErrors.TransactionErrorCodes(_errorCode);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded TransactionSignatureError.
|
||||
@@ -317,9 +350,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.TransactionSignatureErrorSelector());
|
||||
transactionHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
signature = _readErrorParameterAsBytes(encoded, 2);
|
||||
(transactionHash, signerAddress, signature) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, address, bytes)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded TransactionExecutionError.
|
||||
@@ -335,8 +369,10 @@ contract LibExchangeRichErrorDecoder {
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.TransactionExecutionErrorSelector());
|
||||
transactionHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
errorData = _readErrorParameterAsBytes(encoded, 1);
|
||||
(transactionHash, errorData) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(bytes32, bytes)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded IncompleteFillError.
|
||||
@@ -346,11 +382,51 @@ contract LibExchangeRichErrorDecoder {
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
uint256 fillAmount,
|
||||
bytes32 orderHash
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.IncompleteFillErrorSelector());
|
||||
orderHash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
(fillAmount, orderHash) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(uint256, bytes32)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded IncompleteMarketSellError.
|
||||
/// @param encoded ABI-encoded revert error.
|
||||
/// @return orderHash Hash of the order being filled.
|
||||
function decodeIncompleteMarketSellError(bytes memory encoded)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
uint256 takerAssetFillAmount,
|
||||
bytes32[] memory orderHashes
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.IncompleteMarketSellErrorSelector());
|
||||
(takerAssetFillAmount, orderHashes) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(uint256, bytes32[])
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded IncompleteMarketBuyError.
|
||||
/// @param encoded ABI-encoded revert error.
|
||||
/// @return orderHash Hash of the order being filled.
|
||||
function decodeIncompleteMarketBuyError(bytes memory encoded)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
uint256 makerAssetFillAmount,
|
||||
bytes32[] memory orderHashes
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, LibExchangeRichErrors.IncompleteMarketBuyErrorSelector());
|
||||
(makerAssetFillAmount, orderHashes) = abi.decode(
|
||||
encoded.sliceDestructive(4, encoded.length),
|
||||
(uint256, bytes32[])
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Revert if the leading 4 bytes of `encoded` is not `selector`.
|
||||
@@ -364,53 +440,4 @@ contract LibExchangeRichErrorDecoder {
|
||||
"BAD_SELECTOR"
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Read a parameter at index `index` as a uint256.
|
||||
function _readErrorParameterAsUint256(bytes memory encoded, uint256 index)
|
||||
private
|
||||
pure
|
||||
returns (uint256 value)
|
||||
{
|
||||
uint256 parameterOffset = 4 + index * 32;
|
||||
return LibBytes.readUint256(encoded, parameterOffset);
|
||||
}
|
||||
|
||||
/// @dev Read a parameter at index `index` as a bytes32.
|
||||
function _readErrorParameterAsBytes32(bytes memory encoded, uint256 index)
|
||||
private
|
||||
pure
|
||||
returns (bytes32 value)
|
||||
{
|
||||
uint256 parameterOffset = 4 + index * 32;
|
||||
return LibBytes.readBytes32(encoded, parameterOffset);
|
||||
}
|
||||
|
||||
/// @dev Read a parameter at index `index` as an address.
|
||||
function _readErrorParameterAsAddress(bytes memory encoded, uint256 index)
|
||||
private
|
||||
pure
|
||||
returns (address value)
|
||||
{
|
||||
uint256 parameterOffset = 4 + index * 32;
|
||||
return address(uint160(LibBytes.readUint256(encoded, parameterOffset)));
|
||||
}
|
||||
|
||||
/// @dev Read a parameter at index `index` as a bytes.
|
||||
function _readErrorParameterAsBytes(bytes memory encoded, uint256 index)
|
||||
private
|
||||
pure
|
||||
returns (bytes memory value)
|
||||
{
|
||||
uint256 dataOffset = 4 + _readErrorParameterAsUint256(encoded, index);
|
||||
return LibBytes.readBytesWithLength(encoded, dataOffset);
|
||||
}
|
||||
|
||||
/// @dev Read a parameter at index `index` as a string.
|
||||
function _readErrorParameterAsString(bytes memory encoded, uint256 index)
|
||||
private
|
||||
pure
|
||||
returns (string memory value)
|
||||
{
|
||||
return string(_readErrorParameterAsBytes(encoded, index));
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,14 @@
|
||||
import { addressUtils, blockchainTests, expect, hexRandom, OrderStatus, orderUtils } from '@0x/contracts-test-utils';
|
||||
import {
|
||||
addressUtils,
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
hexRandom,
|
||||
OrderStatus,
|
||||
orderUtils,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { ExchangeRevertErrors, generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { RevertError } from '@0x/utils';
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts, TestLibExchangeRichErrorDecoderContract } from '../src';
|
||||
@@ -9,6 +17,7 @@ blockchainTests.resets('LibExchangeRichErrorDecoder', ({ provider, txDefaults })
|
||||
const SIGNATURE_LENGTH = 66;
|
||||
const ASSET_DATA_LENGTH = 36;
|
||||
const ERROR_DATA_LENGTH = 100;
|
||||
const { WORD_LENGTH } = constants;
|
||||
let decoder: TestLibExchangeRichErrorDecoderContract;
|
||||
|
||||
before(async () => {
|
||||
@@ -138,6 +147,19 @@ blockchainTests.resets('LibExchangeRichErrorDecoder', ({ provider, txDefaults })
|
||||
|
||||
(() => {
|
||||
const orderHash = orderUtils.generatePseudoRandomOrderHash();
|
||||
createDecodeTest(ExchangeRevertErrors.IncompleteFillError, [orderHash]);
|
||||
const amount = new BigNumber(hexRandom(WORD_LENGTH));
|
||||
createDecodeTest(ExchangeRevertErrors.IncompleteFillError, [amount, orderHash]);
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const orderHashes = _.times(3, () => orderUtils.generatePseudoRandomOrderHash());
|
||||
const amount = new BigNumber(hexRandom(WORD_LENGTH));
|
||||
createDecodeTest(ExchangeRevertErrors.IncompleteMarketSellError, [amount, orderHashes]);
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const orderHashes = _.times(3, () => orderUtils.generatePseudoRandomOrderHash());
|
||||
const amount = new BigNumber(hexRandom(WORD_LENGTH));
|
||||
createDecodeTest(ExchangeRevertErrors.IncompleteMarketBuyError, [amount, orderHashes]);
|
||||
})();
|
||||
});
|
||||
|
@@ -354,8 +354,10 @@ blockchainTests.resets('Exchange transactions', env => {
|
||||
[
|
||||
ExchangeFunctionName.FillOrderNoThrow,
|
||||
ExchangeFunctionName.BatchFillOrdersNoThrow,
|
||||
ExchangeFunctionName.MarketBuyOrders,
|
||||
ExchangeFunctionName.MarketSellOrders,
|
||||
ExchangeFunctionName.MarketBuyOrdersNoThrow,
|
||||
ExchangeFunctionName.MarketSellOrdersNoThrow,
|
||||
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
|
||||
ExchangeFunctionName.MarketSellOrdersFillOrKill,
|
||||
].indexOf(fnName) === -1
|
||||
) {
|
||||
it(`${fnName} should revert and rethrow error if the underlying function reverts`, async () => {
|
||||
|
@@ -20,7 +20,12 @@ export const constants = {
|
||||
ExchangeFunctionName.BatchFillOrKillOrders,
|
||||
ExchangeFunctionName.BatchFillOrdersNoThrow,
|
||||
],
|
||||
MARKET_FILL_FN_NAMES: [ExchangeFunctionName.MarketBuyOrders, ExchangeFunctionName.MarketSellOrders],
|
||||
MARKET_FILL_FN_NAMES: [
|
||||
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
|
||||
ExchangeFunctionName.MarketSellOrdersFillOrKill,
|
||||
ExchangeFunctionName.MarketBuyOrdersNoThrow,
|
||||
ExchangeFunctionName.MarketSellOrdersNoThrow,
|
||||
],
|
||||
};
|
||||
|
||||
export enum ValidatorWalletAction {
|
||||
|
@@ -129,12 +129,12 @@ export class ExchangeWrapper {
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async marketSellOrdersAsync(
|
||||
public async marketSellOrdersNoThrowAsync(
|
||||
orders: SignedOrder[],
|
||||
from: string,
|
||||
opts: { takerAssetFillAmount: BigNumber; gas?: number },
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._exchange.marketSellOrders.sendTransactionAsync(
|
||||
const txHash = await this._exchange.marketSellOrdersNoThrow.sendTransactionAsync(
|
||||
orders,
|
||||
opts.takerAssetFillAmount,
|
||||
orders.map(signedOrder => signedOrder.signature),
|
||||
@@ -143,12 +143,40 @@ export class ExchangeWrapper {
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async marketBuyOrdersAsync(
|
||||
public async marketBuyOrdersNoThrowAsync(
|
||||
orders: SignedOrder[],
|
||||
from: string,
|
||||
opts: { makerAssetFillAmount: BigNumber; gas?: number },
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._exchange.marketBuyOrders.sendTransactionAsync(
|
||||
const txHash = await this._exchange.marketBuyOrdersNoThrow.sendTransactionAsync(
|
||||
orders,
|
||||
opts.makerAssetFillAmount,
|
||||
orders.map(signedOrder => signedOrder.signature),
|
||||
{ from, gas: opts.gas },
|
||||
);
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async marketSellOrdersFillOrKillAsync(
|
||||
orders: SignedOrder[],
|
||||
from: string,
|
||||
opts: { takerAssetFillAmount: BigNumber; gas?: number },
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._exchange.marketSellOrdersFillOrKill.sendTransactionAsync(
|
||||
orders,
|
||||
opts.takerAssetFillAmount,
|
||||
orders.map(signedOrder => signedOrder.signature),
|
||||
{ from, gas: opts.gas },
|
||||
);
|
||||
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async marketBuyOrdersFillOrKillAsync(
|
||||
orders: SignedOrder[],
|
||||
from: string,
|
||||
opts: { makerAssetFillAmount: BigNumber; gas?: number },
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._exchange.marketBuyOrdersFillOrKill.sendTransactionAsync(
|
||||
orders,
|
||||
opts.makerAssetFillAmount,
|
||||
orders.map(signedOrder => signedOrder.signature),
|
||||
|
@@ -21,8 +21,10 @@ export enum ExchangeFunctionName {
|
||||
FillOrKillOrder = 'fillOrKillOrder',
|
||||
FillOrder = 'fillOrder',
|
||||
FillOrderNoThrow = 'fillOrderNoThrow',
|
||||
MarketBuyOrders = 'marketBuyOrders',
|
||||
MarketSellOrders = 'marketSellOrders',
|
||||
MarketBuyOrdersNoThrow = 'marketBuyOrdersNoThrow',
|
||||
MarketSellOrdersNoThrow = 'marketSellOrdersNoThrow',
|
||||
MarketBuyOrdersFillOrKill = 'marketBuyOrdersFillOrKill',
|
||||
MarketSellOrdersFillOrKill = 'marketSellOrdersFillOrKill',
|
||||
MatchOrders = 'matchOrders',
|
||||
MatchOrdersWithMaximalFill = 'matchOrdersWithMaximalFill',
|
||||
PreSign = 'preSign',
|
||||
|
@@ -191,13 +191,14 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
|
||||
it('should revert if entire takerAssetFillAmount not filled', async () => {
|
||||
const signedOrder = await orderFactory.newSignedOrderAsync();
|
||||
const takerAssetFillAmount = signedOrder.takerAssetAmount.dividedToIntegerBy(2);
|
||||
|
||||
await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
|
||||
takerAssetFillAmount: signedOrder.takerAssetAmount.div(2),
|
||||
takerAssetFillAmount,
|
||||
});
|
||||
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const expectedError = new ExchangeRevertErrors.IncompleteFillError(orderHashHex);
|
||||
const expectedError = new ExchangeRevertErrors.IncompleteFillError(takerAssetFillAmount, orderHashHex);
|
||||
const tx = exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
@@ -771,19 +772,19 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('marketSellOrders', () => {
|
||||
describe('marketSellOrdersNoThrow', () => {
|
||||
it('should stop when the entire takerAssetFillAmount is filled', async () => {
|
||||
const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus(
|
||||
signedOrders[1].takerAssetAmount.div(2),
|
||||
);
|
||||
|
||||
const fillResults = await exchange.marketSellOrders.callAsync(
|
||||
const fillResults = await exchange.marketSellOrdersNoThrow.callAsync(
|
||||
signedOrders,
|
||||
takerAssetFillAmount,
|
||||
signedOrders.map(signedOrder => signedOrder.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
await exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, {
|
||||
await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
|
||||
takerAssetFillAmount,
|
||||
// HACK(albrow): We need to hardcode the gas estimate here because
|
||||
// the Geth gas estimator doesn't work with the way we use
|
||||
@@ -852,13 +853,13 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
].plus(signedOrder.makerFee.plus(signedOrder.takerFee));
|
||||
});
|
||||
|
||||
const fillResults = await exchange.marketSellOrders.callAsync(
|
||||
const fillResults = await exchange.marketSellOrdersNoThrow.callAsync(
|
||||
signedOrders,
|
||||
takerAssetFillAmount,
|
||||
signedOrders.map(signedOrder => signedOrder.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
await exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, {
|
||||
await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
|
||||
takerAssetFillAmount,
|
||||
// HACK(albrow): We need to hardcode the gas estimate here because
|
||||
// the Geth gas estimator doesn't work with the way we use
|
||||
@@ -926,13 +927,13 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
].plus(signedOrder.makerFee.plus(signedOrder.takerFee));
|
||||
});
|
||||
|
||||
const fillResults = await exchange.marketSellOrders.callAsync(
|
||||
const fillResults = await exchange.marketSellOrdersNoThrow.callAsync(
|
||||
signedOrders,
|
||||
takerAssetFillAmount,
|
||||
signedOrders.map(signedOrder => signedOrder.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
await exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, {
|
||||
await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, {
|
||||
takerAssetFillAmount,
|
||||
// HACK(albrow): We need to hardcode the gas estimate here because
|
||||
// the Geth gas estimator doesn't work with the way we use
|
||||
@@ -967,19 +968,19 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('marketBuyOrders', () => {
|
||||
describe('marketBuyOrdersNoThrow', () => {
|
||||
it('should stop when the entire makerAssetFillAmount is filled', async () => {
|
||||
const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus(
|
||||
signedOrders[1].makerAssetAmount.div(2),
|
||||
);
|
||||
|
||||
const fillResults = await exchange.marketBuyOrders.callAsync(
|
||||
const fillResults = await exchange.marketBuyOrdersNoThrow.callAsync(
|
||||
signedOrders,
|
||||
makerAssetFillAmount,
|
||||
signedOrders.map(signedOrder => signedOrder.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
await exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, {
|
||||
await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, {
|
||||
makerAssetFillAmount,
|
||||
// HACK(albrow): We need to hardcode the gas estimate here because
|
||||
// the Geth gas estimator doesn't work with the way we use
|
||||
@@ -1048,13 +1049,13 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
].plus(signedOrder.makerFee.plus(signedOrder.takerFee));
|
||||
});
|
||||
|
||||
const fillResults = await exchange.marketBuyOrders.callAsync(
|
||||
const fillResults = await exchange.marketBuyOrdersNoThrow.callAsync(
|
||||
signedOrders,
|
||||
makerAssetFillAmount,
|
||||
signedOrders.map(signedOrder => signedOrder.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
await exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, {
|
||||
await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, {
|
||||
makerAssetFillAmount,
|
||||
// HACK(albrow): We need to hardcode the gas estimate here because
|
||||
// the Geth gas estimator doesn't work with the way we use
|
||||
@@ -1123,13 +1124,13 @@ blockchainTests.resets('Exchange wrappers', env => {
|
||||
].plus(signedOrder.makerFee.plus(signedOrder.takerFee));
|
||||
});
|
||||
|
||||
const fillResults = await exchange.marketBuyOrders.callAsync(
|
||||
const fillResults = await exchange.marketBuyOrdersNoThrow.callAsync(
|
||||
signedOrders,
|
||||
makerAssetFillAmount,
|
||||
signedOrders.map(signedOrder => signedOrder.signature),
|
||||
{ from: takerAddress },
|
||||
);
|
||||
await exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, {
|
||||
await exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, {
|
||||
makerAssetFillAmount,
|
||||
// HACK(albrow): We need to hardcode the gas estimate here because
|
||||
// the Geth gas estimator doesn't work with the way we use
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user