EP: Swallow reverts in batchGetLimit/RfqRelevantStates() (#117)

* `@0x/contracts-zero-ex`: Swallow reverts in `batchGetLimit/RfqRelevantStates()`.

* `@0x/contracts-zero-ex`: Fix typos

* `@0x/contracts-zero-ex`: Fix misleading RFQ typehash comment in `LibNativeOrder.sol`

Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
Lawrence Forman 2021-01-20 00:25:48 -05:00 committed by GitHub
parent a7a905de4c
commit 50068750f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 18 deletions

View File

@ -1,4 +1,13 @@
[ [
{
"version": "0.18.1",
"changes": [
{
"note": "Swallow reverts in `batchGetLimitOrderRelevantStates()` and `batchGetRfqOrderRelevantStates()`",
"pr": 117
}
]
},
{ {
"version": "0.18.0", "version": "0.18.0",
"changes": [ "changes": [

View File

@ -387,7 +387,9 @@ interface INativeOrdersFeature {
bool isSignatureValid bool isSignatureValid
); );
/// @dev Batch version of `getLimitOrderRelevantState()`. /// @dev Batch version of `getLimitOrderRelevantState()`, without reverting.
/// Orders that would normally cause `getLimitOrderRelevantState()`
/// to revert will have empty results.
/// @param orders The limit orders. /// @param orders The limit orders.
/// @param signatures The order signatures. /// @param signatures The order signatures.
/// @return orderInfos Info about the orders. /// @return orderInfos Info about the orders.
@ -406,7 +408,9 @@ interface INativeOrdersFeature {
bool[] memory isSignatureValids bool[] memory isSignatureValids
); );
/// @dev Batch version of `getRfqOrderRelevantState()`. /// @dev Batch version of `getRfqOrderRelevantState()`, without reverting.
/// Orders that would normally cause `getRfqOrderRelevantState()`
/// to revert will have empty results.
/// @param orders The RFQ orders. /// @param orders The RFQ orders.
/// @param signatures The order signatures. /// @param signatures The order signatures.
/// @return orderInfos Info about the orders. /// @return orderInfos Info about the orders.

View File

@ -111,7 +111,7 @@ contract NativeOrdersFeature is
/// @dev Name of this feature. /// @dev Name of this feature.
string public constant override FEATURE_NAME = "LimitOrders"; string public constant override FEATURE_NAME = "LimitOrders";
/// @dev Version of this feature. /// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0); uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
/// @dev Highest bit of a uint256, used to flag cancelled orders. /// @dev Highest bit of a uint256, used to flag cancelled orders.
uint256 private constant HIGH_BIT = 1 << 255; uint256 private constant HIGH_BIT = 1 << 255;
@ -772,7 +772,9 @@ contract NativeOrdersFeature is
LibSignature.getSignerOfHash(orderInfo.orderHash, signature); LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
} }
/// @dev Batch version of `getLimitOrderRelevantState()`. /// @dev Batch version of `getLimitOrderRelevantState()`, without reverting.
/// Orders that would normally cause `getLimitOrderRelevantState()`
/// to revert will have empty results.
/// @param orders The limit orders. /// @param orders The limit orders.
/// @param signatures The order signatures. /// @param signatures The order signatures.
/// @return orderInfos Info about the orders. /// @return orderInfos Info about the orders.
@ -800,15 +802,25 @@ contract NativeOrdersFeature is
actualFillableTakerTokenAmounts = new uint128[](orders.length); actualFillableTakerTokenAmounts = new uint128[](orders.length);
isSignatureValids = new bool[](orders.length); isSignatureValids = new bool[](orders.length);
for (uint256 i = 0; i < orders.length; ++i) { for (uint256 i = 0; i < orders.length; ++i) {
( try
orderInfos[i], this.getLimitOrderRelevantState(orders[i], signatures[i])
actualFillableTakerTokenAmounts[i], returns (
isSignatureValids[i] LibNativeOrder.OrderInfo memory orderInfo,
) = getLimitOrderRelevantState(orders[i], signatures[i]); uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
)
{
orderInfos[i] = orderInfo;
actualFillableTakerTokenAmounts[i] = actualFillableTakerTokenAmount;
isSignatureValids[i] = isSignatureValid;
}
catch {}
} }
} }
/// @dev Batch version of `getRfqOrderRelevantState()`. /// @dev Batch version of `getRfqOrderRelevantState()`, without reverting.
/// Orders that would normally cause `getRfqOrderRelevantState()`
/// to revert will have empty results.
/// @param orders The RFQ orders. /// @param orders The RFQ orders.
/// @param signatures The order signatures. /// @param signatures The order signatures.
/// @return orderInfos Info about the orders. /// @return orderInfos Info about the orders.
@ -836,11 +848,19 @@ contract NativeOrdersFeature is
actualFillableTakerTokenAmounts = new uint128[](orders.length); actualFillableTakerTokenAmounts = new uint128[](orders.length);
isSignatureValids = new bool[](orders.length); isSignatureValids = new bool[](orders.length);
for (uint256 i = 0; i < orders.length; ++i) { for (uint256 i = 0; i < orders.length; ++i) {
( try
orderInfos[i], this.getRfqOrderRelevantState(orders[i], signatures[i])
actualFillableTakerTokenAmounts[i], returns (
isSignatureValids[i] LibNativeOrder.OrderInfo memory orderInfo,
) = getRfqOrderRelevantState(orders[i], signatures[i]); uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
)
{
orderInfos[i] = orderInfo;
actualFillableTakerTokenAmounts[i] = actualFillableTakerTokenAmount;
isSignatureValids[i] = isSignatureValid;
}
catch {}
} }
} }

View File

@ -103,8 +103,8 @@ library LibNativeOrder {
// "uint128 makerAmount,", // "uint128 makerAmount,",
// "uint128 takerAmount,", // "uint128 takerAmount,",
// "address maker,", // "address maker,",
// "address txOrigin,",
// "address taker,", // "address taker,",
// "address txOrigin,",
// "bytes32 pool,", // "bytes32 pool,",
// "uint64 expiry,", // "uint64 expiry,",
// "uint256 salt" // "uint256 salt"

View File

@ -1,4 +1,11 @@
import { blockchainTests, constants, describe, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils'; import {
blockchainTests,
constants,
describe,
expect,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { import {
LimitOrder, LimitOrder,
LimitOrderFields, LimitOrderFields,
@ -18,7 +25,7 @@ import { getRandomLimitOrder, getRandomRfqOrder } from '../utils/orders';
import { TestMintableERC20TokenContract, TestRfqOriginRegistrationContract } from '../wrappers'; import { TestMintableERC20TokenContract, TestRfqOriginRegistrationContract } from '../wrappers';
blockchainTests.resets('NativeOrdersFeature', env => { blockchainTests.resets('NativeOrdersFeature', env => {
const { NULL_ADDRESS, MAX_UINT256, ZERO_AMOUNT } = constants; const { NULL_ADDRESS, MAX_UINT256, NULL_BYTES32, ZERO_AMOUNT } = constants;
const GAS_PRICE = new BigNumber('123e9'); const GAS_PRICE = new BigNumber('123e9');
const PROTOCOL_FEE_MULTIPLIER = 1337e3; const PROTOCOL_FEE_MULTIPLIER = 1337e3;
const SINGLE_PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER); const SINGLE_PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER);
@ -1668,6 +1675,30 @@ blockchainTests.resets('NativeOrdersFeature', env => {
expect(isSignatureValids[i]).to.eq(true); expect(isSignatureValids[i]).to.eq(true);
} }
}); });
it('swallows reverts', async () => {
const orders = new Array(3).fill(0).map(() => getTestLimitOrder());
// The second order will revert because its maker token is not valid.
orders[1].makerToken = randomAddress();
await batchFundOrderMakerAsync(orders);
const [orderInfos, fillableTakerAmounts, isSignatureValids] = await zeroEx
.batchGetLimitOrderRelevantStates(
orders,
await Promise.all(orders.map(async o => o.getSignatureWithProviderAsync(env.provider))),
)
.callAsync();
expect(orderInfos).to.be.length(orders.length);
expect(fillableTakerAmounts).to.be.length(orders.length);
expect(isSignatureValids).to.be.length(orders.length);
for (let i = 0; i < orders.length; ++i) {
expect(orderInfos[i]).to.deep.eq({
orderHash: i === 1 ? NULL_BYTES32 : orders[i].getHash(),
status: i === 1 ? OrderStatus.Invalid : OrderStatus.Fillable,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmounts[i]).to.bignumber.eq(i === 1 ? ZERO_AMOUNT : orders[i].takerAmount);
expect(isSignatureValids[i]).to.eq(i !== 1);
}
});
}); });
describe('batchGetRfqOrderRelevantStates()', () => { describe('batchGetRfqOrderRelevantStates()', () => {