That time DevUtils wouldn't die (#97)

* `@0x/contracts-utils`: Allow for excess return data in `LibERC20TokenV06` compat* functions

* `@0x/contracts-zero-ex`: Add DevUtils-like functions to `NativeOrdersFeature`

* `@0x/contract-artifacts`: Update IZeroEx artifact

* `@0x/contract-wrappers`: Update IZeroExContract wrapper

* `@0x/contracts-zero-ex`: Address review feedback

* `@0x/contracts-utils`: Add `LibSafeMathV06.safeDowncastToUint128()`

* `@0x/contracts-zero-ex`: Fix changelog and add cancelled get relevant state tests

* `@0x/contracts-utils`: Fix changelog

Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
Lawrence Forman 2021-01-04 17:52:29 -05:00 committed by GitHub
parent 1249bf9ccc
commit 5083fab06a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1671 additions and 14 deletions

View File

@ -1,4 +1,13 @@
[ [
{
"version": "3.3.0",
"changes": [
{
"note": "Allow for excess return data in `LibERC20TokenV06` compat* functions",
"pr": 97
}
]
},
{ {
"timestamp": 1608692071, "timestamp": 1608692071,
"version": "3.2.14", "version": "3.2.14",

View File

@ -118,7 +118,7 @@ library LibERC20TokenV06 {
{ {
tokenDecimals = 18; tokenDecimals = 18;
(bool didSucceed, bytes memory resultData) = address(token).staticcall(DECIMALS_CALL_DATA); (bool didSucceed, bytes memory resultData) = address(token).staticcall(DECIMALS_CALL_DATA);
if (didSucceed && resultData.length == 32) { if (didSucceed && resultData.length >= 32) {
tokenDecimals = uint8(LibBytesV06.readUint256(resultData, 0)); tokenDecimals = uint8(LibBytesV06.readUint256(resultData, 0));
} }
} }
@ -141,7 +141,7 @@ library LibERC20TokenV06 {
spender spender
) )
); );
if (didSucceed && resultData.length == 32) { if (didSucceed && resultData.length >= 32) {
allowance_ = LibBytesV06.readUint256(resultData, 0); allowance_ = LibBytesV06.readUint256(resultData, 0);
} }
} }
@ -162,7 +162,7 @@ library LibERC20TokenV06 {
owner owner
) )
); );
if (didSucceed && resultData.length == 32) { if (didSucceed && resultData.length >= 32) {
balance = LibBytesV06.readUint256(resultData, 0); balance = LibBytesV06.readUint256(resultData, 0);
} }
} }
@ -180,7 +180,7 @@ library LibERC20TokenV06 {
if (resultData.length == 0) { if (resultData.length == 0) {
return true; return true;
} }
if (resultData.length == 32) { if (resultData.length >= 32) {
uint256 result = LibBytesV06.readUint256(resultData, 0); uint256 result = LibBytesV06.readUint256(resultData, 0);
if (result == 1) { if (result == 1) {
return true; return true;

View File

@ -1,4 +1,13 @@
[ [
{
"version": "4.7.0",
"changes": [
{
"note": "Add `LibSafeMathV06.safeDowncastToUint128()`",
"pr": 97
}
]
},
{ {
"timestamp": 1608692071, "timestamp": 1608692071,
"version": "4.6.5", "version": "4.6.5",

View File

@ -187,4 +187,18 @@ library LibSafeMathV06 {
{ {
return a < b ? a : b; return a < b ? a : b;
} }
function safeDowncastToUint128(uint256 a)
internal
pure
returns (uint128)
{
if (a > type(uint128).max) {
LibRichErrorsV06.rrevert(LibSafeMathRichErrorsV06.Uint256DowncastError(
LibSafeMathRichErrorsV06.DowncastErrorCodes.VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT128,
a
));
}
return uint128(a);
}
} }

View File

@ -39,7 +39,8 @@ library LibSafeMathRichErrorsV06 {
enum DowncastErrorCodes { enum DowncastErrorCodes {
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT32, VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT32,
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64, VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64,
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96 VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96,
VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT128
} }
// solhint-disable func-name-mixedcase // solhint-disable func-name-mixedcase

View File

@ -1,4 +1,13 @@
[ [
{
"version": "0.17.0",
"changes": [
{
"note": "Add DevUtils-like functions to `NativeOrdersFeature`",
"pr": 97
}
]
},
{ {
"version": "0.16.0", "version": "0.16.0",
"changes": [ "changes": [

View File

@ -346,4 +346,81 @@ interface INativeOrdersFeature {
view view
returns (uint32 multiplier); returns (uint32 multiplier);
/// @dev Get order info, fillable amount, and signature validity for a limit order.
/// Fillable amount is determined using balances and allowances of the maker.
/// @param order The limit order.
/// @param signature The order signature.
/// @return orderInfo Info about the order.
/// @return actualFillableTakerTokenAmount How much of the order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValid Whether the signature is valid.
function getLimitOrderRelevantState(
LibNativeOrder.LimitOrder calldata order,
LibSignature.Signature calldata signature
)
external
view
returns (
LibNativeOrder.OrderInfo memory orderInfo,
uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
);
/// @dev Get order info, fillable amount, and signature validity for an RFQ order.
/// Fillable amount is determined using balances and allowances of the maker.
/// @param order The RFQ order.
/// @param signature The order signature.
/// @return orderInfo Info about the order.
/// @return actualFillableTakerTokenAmount How much of the order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValid Whether the signature is valid.
function getRfqOrderRelevantState(
LibNativeOrder.RfqOrder calldata order,
LibSignature.Signature calldata signature
)
external
view
returns (
LibNativeOrder.OrderInfo memory orderInfo,
uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
);
/// @dev Batch version of `getLimitOrderRelevantState()`.
/// @param orders The limit orders.
/// @param signatures The order signatures.
/// @return orderInfos Info about the orders.
/// @return actualFillableTakerTokenAmounts How much of each order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValids Whether each signature is valid for the order.
function batchGetLimitOrderRelevantStates(
LibNativeOrder.LimitOrder[] calldata orders,
LibSignature.Signature[] calldata signatures
)
external
view
returns (
LibNativeOrder.OrderInfo[] memory orderInfos,
uint128[] memory actualFillableTakerTokenAmounts,
bool[] memory isSignatureValids
);
/// @dev Batch version of `getRfqOrderRelevantState()`.
/// @param orders The RFQ orders.
/// @param signatures The order signatures.
/// @return orderInfos Info about the orders.
/// @return actualFillableTakerTokenAmounts How much of each order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValids Whether each signature is valid for the order.
function batchGetRfqOrderRelevantStates(
LibNativeOrder.RfqOrder[] calldata orders,
LibSignature.Signature[] calldata signatures
)
external
view
returns (
LibNativeOrder.OrderInfo[] memory orderInfos,
uint128[] memory actualFillableTakerTokenAmounts,
bool[] memory isSignatureValids
);
} }

View File

@ -51,6 +51,7 @@ contract NativeOrdersFeature is
using LibSafeMathV06 for uint256; using LibSafeMathV06 for uint256;
using LibSafeMathV06 for uint128; using LibSafeMathV06 for uint128;
using LibRichErrorsV06 for bytes; using LibRichErrorsV06 for bytes;
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Params for `_settleOrder()`. /// @dev Params for `_settleOrder()`.
struct SettleOrderInfo { struct SettleOrderInfo {
@ -97,6 +98,15 @@ contract NativeOrdersFeature is
uint128 takerTokenFeeFilledAmount; uint128 takerTokenFeeFilledAmount;
} }
// @dev Params for `_getActualFillableTakerTokenAmount()`.
struct GetActualFillableTakerTokenAmountParams {
address maker;
IERC20TokenV06 makerToken;
uint128 orderMakerAmount;
uint128 orderTakerAmount;
LibNativeOrder.OrderInfo orderInfo;
}
/// @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.
@ -148,6 +158,10 @@ contract NativeOrdersFeature is
_registerFeatureFunction(this.getRfqOrderHash.selector); _registerFeatureFunction(this.getRfqOrderHash.selector);
_registerFeatureFunction(this.getProtocolFeeMultiplier.selector); _registerFeatureFunction(this.getProtocolFeeMultiplier.selector);
_registerFeatureFunction(this.registerAllowedRfqOrigins.selector); _registerFeatureFunction(this.registerAllowedRfqOrigins.selector);
_registerFeatureFunction(this.getLimitOrderRelevantState.selector);
_registerFeatureFunction(this.getRfqOrderRelevantState.selector);
_registerFeatureFunction(this.batchGetLimitOrderRelevantStates.selector);
_registerFeatureFunction(this.batchGetRfqOrderRelevantStates.selector);
return LibMigrate.MIGRATE_SUCCESS; return LibMigrate.MIGRATE_SUCCESS;
} }
@ -687,6 +701,148 @@ contract NativeOrdersFeature is
); );
} }
/// @dev Get order info, fillable amount, and signature validity for a limit order.
/// Fillable amount is determined using balances and allowances of the maker.
/// @param order The limit order.
/// @param signature The order signature.
/// @return orderInfo Info about the order.
/// @return actualFillableTakerTokenAmount How much of the order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValid Whether the signature is valid.
function getLimitOrderRelevantState(
LibNativeOrder.LimitOrder memory order,
LibSignature.Signature calldata signature
)
public
override
view
returns (
LibNativeOrder.OrderInfo memory orderInfo,
uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
)
{
orderInfo = getLimitOrderInfo(order);
actualFillableTakerTokenAmount = _getActualFillableTakerTokenAmount(
GetActualFillableTakerTokenAmountParams({
maker: order.maker,
makerToken: order.makerToken,
orderMakerAmount: order.makerAmount,
orderTakerAmount: order.takerAmount,
orderInfo: orderInfo
})
);
isSignatureValid = order.maker ==
LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
}
/// @dev Get order info, fillable amount, and signature validity for an RFQ order.
/// Fillable amount is determined using balances and allowances of the maker.
/// @param order The RFQ order.
/// @param signature The order signature.
/// @return orderInfo Info about the order.
/// @return actualFillableTakerTokenAmount How much of the order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValid Whether the signature is valid.
function getRfqOrderRelevantState(
LibNativeOrder.RfqOrder memory order,
LibSignature.Signature memory signature
)
public
override
view
returns (
LibNativeOrder.OrderInfo memory orderInfo,
uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
)
{
orderInfo = getRfqOrderInfo(order);
actualFillableTakerTokenAmount = _getActualFillableTakerTokenAmount(
GetActualFillableTakerTokenAmountParams({
maker: order.maker,
makerToken: order.makerToken,
orderMakerAmount: order.makerAmount,
orderTakerAmount: order.takerAmount,
orderInfo: orderInfo
})
);
isSignatureValid = order.maker ==
LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
}
/// @dev Batch version of `getLimitOrderRelevantState()`.
/// @param orders The limit orders.
/// @param signatures The order signatures.
/// @return orderInfos Info about the orders.
/// @return actualFillableTakerTokenAmounts How much of each order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValids Whether each signature is valid for the order.
function batchGetLimitOrderRelevantStates(
LibNativeOrder.LimitOrder[] calldata orders,
LibSignature.Signature[] calldata signatures
)
external
override
view
returns (
LibNativeOrder.OrderInfo[] memory orderInfos,
uint128[] memory actualFillableTakerTokenAmounts,
bool[] memory isSignatureValids
)
{
require(
orders.length == signatures.length,
"NativeOrdersFeature/MISMATCHED_ARRAY_LENGTHS"
);
orderInfos = new LibNativeOrder.OrderInfo[](orders.length);
actualFillableTakerTokenAmounts = new uint128[](orders.length);
isSignatureValids = new bool[](orders.length);
for (uint256 i = 0; i < orders.length; ++i) {
(
orderInfos[i],
actualFillableTakerTokenAmounts[i],
isSignatureValids[i]
) = getLimitOrderRelevantState(orders[i], signatures[i]);
}
}
/// @dev Batch version of `getRfqOrderRelevantState()`.
/// @param orders The RFQ orders.
/// @param signatures The order signatures.
/// @return orderInfos Info about the orders.
/// @return actualFillableTakerTokenAmounts How much of each order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValids Whether each signature is valid for the order.
function batchGetRfqOrderRelevantStates(
LibNativeOrder.RfqOrder[] calldata orders,
LibSignature.Signature[] calldata signatures
)
external
override
view
returns (
LibNativeOrder.OrderInfo[] memory orderInfos,
uint128[] memory actualFillableTakerTokenAmounts,
bool[] memory isSignatureValids
)
{
require(
orders.length == signatures.length,
"NativeOrdersFeature/MISMATCHED_ARRAY_LENGTHS"
);
orderInfos = new LibNativeOrder.OrderInfo[](orders.length);
actualFillableTakerTokenAmounts = new uint128[](orders.length);
isSignatureValids = new bool[](orders.length);
for (uint256 i = 0; i < orders.length; ++i) {
(
orderInfos[i],
actualFillableTakerTokenAmounts[i],
isSignatureValids[i]
) = getRfqOrderRelevantState(orders[i], signatures[i]);
}
}
/// @dev Get the protocol fee multiplier. This should be multiplied by the /// @dev Get the protocol fee multiplier. This should be multiplied by the
/// gas price to arrive at the required protocol fee to fill a native order. /// gas price to arrive at the required protocol fee to fill a native order.
/// @return multiplier The protocol fee multiplier. /// @return multiplier The protocol fee multiplier.
@ -751,6 +907,48 @@ contract NativeOrdersFeature is
orderInfo.status = LibNativeOrder.OrderStatus.FILLABLE; orderInfo.status = LibNativeOrder.OrderStatus.FILLABLE;
} }
/// @dev Calculate the actual fillable taker token amount of an order
/// based on maker allowance and balances.
function _getActualFillableTakerTokenAmount(
GetActualFillableTakerTokenAmountParams memory params
)
private
view
returns (uint128 actualFillableTakerTokenAmount)
{
if (params.orderMakerAmount == 0 || params.orderTakerAmount == 0) {
// Empty order.
return 0;
}
if (params.orderInfo.status != LibNativeOrder.OrderStatus.FILLABLE) {
// Not fillable.
return 0;
}
// Get the fillable maker amount based on the order quantities and
// previously filled amount
uint256 fillableMakerTokenAmount = LibMathV06.getPartialAmountFloor(
uint256(
params.orderTakerAmount
- params.orderInfo.takerTokenFilledAmount
),
uint256(params.orderTakerAmount),
uint256(params.orderMakerAmount)
);
// Clamp it to the amount of maker tokens we can spend on behalf of the
// maker.
fillableMakerTokenAmount = LibSafeMathV06.min256(
fillableMakerTokenAmount,
_getSpendableERC20BalanceOf(params.makerToken, params.maker)
);
// Convert to taker token amount.
return LibMathV06.getPartialAmountCeil(
fillableMakerTokenAmount,
uint256(params.orderMakerAmount),
uint256(params.orderTakerAmount)
).safeDowncastToUint128();
}
/// @dev Cancel a limit or RFQ order directly by its order hash. /// @dev Cancel a limit or RFQ order directly by its order hash.
/// @param orderHash The order's order hash. /// @param orderHash The order's order hash.
/// @param maker The order's maker. /// @param maker The order's maker.

View File

@ -1366,14 +1366,332 @@ blockchainTests.resets('NativeOrdersFeature', env => {
}); });
}); });
it.skip('RFQ gas benchmark', async () => { async function fundOrderMakerAsync(
const orders = [...new Array(2)].map(() => order: LimitOrder | RfqOrder,
getTestRfqOrder({ pool: '0x0000000000000000000000000000000000000000000000000000000000000000' }), balance: BigNumber = order.makerAmount,
); allowance: BigNumber = order.makerAmount,
// Fill one to warm up the fee pool. ): Promise<void> {
await fillRfqOrderAsync(orders[0]); await makerToken.burn(maker, await makerToken.balanceOf(maker).callAsync()).awaitTransactionSuccessAsync();
const receipt = await fillRfqOrderAsync(orders[1]); await makerToken.mint(maker, balance).awaitTransactionSuccessAsync();
// tslint:disable-next-line: no-console await makerToken.approve(zeroEx.address, allowance).awaitTransactionSuccessAsync({ from: maker });
console.log(receipt.gasUsed); }
function getFillableMakerTokenAmount(
order: LimitOrder | RfqOrder,
takerTokenFilledAmount: BigNumber = ZERO_AMOUNT,
): BigNumber {
return order.takerAmount
.minus(takerTokenFilledAmount)
.times(order.makerAmount)
.div(order.takerAmount)
.integerValue(BigNumber.ROUND_DOWN);
}
function getActualFillableTakerTokenAmount(
order: LimitOrder | RfqOrder,
makerBalance: BigNumber = order.makerAmount,
makerAllowance: BigNumber = order.makerAmount,
takerTokenFilledAmount: BigNumber = ZERO_AMOUNT,
): BigNumber {
const fillableMakerTokenAmount = getFillableMakerTokenAmount(order, takerTokenFilledAmount);
return BigNumber.min(fillableMakerTokenAmount, makerBalance, makerAllowance)
.times(order.takerAmount)
.div(order.makerAmount)
.integerValue(BigNumber.ROUND_UP);
}
function getRandomFraction(precision: number = 2): string {
return Math.random().toPrecision(precision);
}
describe('getLimitOrderRelevantState()', () => {
it('works with an empty order', async () => {
const order = getTestLimitOrder({
takerAmount: ZERO_AMOUNT,
});
await fundOrderMakerAsync(order);
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getLimitOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Filled,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmount).to.bignumber.eq(0);
expect(isSignatureValid).to.eq(true);
});
it('works with cancelled order', async () => {
const order = getTestLimitOrder();
await fundOrderMakerAsync(order);
await zeroEx.cancelLimitOrder(order).awaitTransactionSuccessAsync({ from: maker });
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getLimitOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Cancelled,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmount).to.bignumber.eq(0);
expect(isSignatureValid).to.eq(true);
});
it('works with a bad signature', async () => {
const order = getTestLimitOrder();
await fundOrderMakerAsync(order);
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getLimitOrderRelevantState(
order,
await order.clone({ maker: notMaker }).getSignatureWithProviderAsync(env.provider),
)
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Fillable,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmount).to.bignumber.eq(order.takerAmount);
expect(isSignatureValid).to.eq(false);
});
it('works with an unfilled order', async () => {
const order = getTestLimitOrder();
await fundOrderMakerAsync(order);
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getLimitOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Fillable,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmount).to.bignumber.eq(order.takerAmount);
expect(isSignatureValid).to.eq(true);
});
it('works with a fully filled order', async () => {
const order = getTestLimitOrder();
// Fully Fund maker and taker.
await fundOrderMakerAsync(order);
await takerToken
.mint(taker, order.takerAmount.plus(order.takerTokenFeeAmount))
.awaitTransactionSuccessAsync();
await fillLimitOrderAsync(order);
// Partially fill the order.
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getLimitOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Filled,
takerTokenFilledAmount: order.takerAmount,
});
expect(fillableTakerAmount).to.bignumber.eq(0);
expect(isSignatureValid).to.eq(true);
});
it('works with an under-funded, partially-filled order', async () => {
const order = getTestLimitOrder();
// Fully Fund maker and taker.
await fundOrderMakerAsync(order);
await takerToken
.mint(taker, order.takerAmount.plus(order.takerTokenFeeAmount))
.awaitTransactionSuccessAsync();
// Partially fill the order.
const fillAmount = order.takerAmount.times(getRandomFraction()).integerValue();
await fillLimitOrderAsync(order, { fillAmount });
// Reduce maker funds to be < remaining.
const remainingMakerAmount = getFillableMakerTokenAmount(order, fillAmount);
const balance = remainingMakerAmount.times(getRandomFraction()).integerValue();
const allowance = remainingMakerAmount.times(getRandomFraction()).integerValue();
await fundOrderMakerAsync(order, balance, allowance);
// Get order state.
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getLimitOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Fillable,
takerTokenFilledAmount: fillAmount,
});
expect(fillableTakerAmount).to.bignumber.eq(
getActualFillableTakerTokenAmount(order, balance, allowance, fillAmount),
);
expect(isSignatureValid).to.eq(true);
});
});
describe('getRfqOrderRelevantState()', () => {
it('works with an empty order', async () => {
const order = getTestRfqOrder({
takerAmount: ZERO_AMOUNT,
});
await fundOrderMakerAsync(order);
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getRfqOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Filled,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmount).to.bignumber.eq(0);
expect(isSignatureValid).to.eq(true);
});
it('works with cancelled order', async () => {
const order = getTestRfqOrder();
await fundOrderMakerAsync(order);
await zeroEx.cancelRfqOrder(order).awaitTransactionSuccessAsync({ from: maker });
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getRfqOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Cancelled,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmount).to.bignumber.eq(0);
expect(isSignatureValid).to.eq(true);
});
it('works with a bad signature', async () => {
const order = getTestRfqOrder();
await fundOrderMakerAsync(order);
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getRfqOrderRelevantState(
order,
await order.clone({ maker: notMaker }).getSignatureWithProviderAsync(env.provider),
)
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Fillable,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmount).to.bignumber.eq(order.takerAmount);
expect(isSignatureValid).to.eq(false);
});
it('works with an unfilled order', async () => {
const order = getTestRfqOrder();
await fundOrderMakerAsync(order);
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getRfqOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Fillable,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmount).to.bignumber.eq(order.takerAmount);
expect(isSignatureValid).to.eq(true);
});
it('works with a fully filled order', async () => {
const order = getTestRfqOrder();
// Fully Fund maker and taker.
await fundOrderMakerAsync(order);
await takerToken.mint(taker, order.takerAmount);
await fillRfqOrderAsync(order);
// Partially fill the order.
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getRfqOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Filled,
takerTokenFilledAmount: order.takerAmount,
});
expect(fillableTakerAmount).to.bignumber.eq(0);
expect(isSignatureValid).to.eq(true);
});
it('works with an under-funded, partially-filled order', async () => {
const order = getTestRfqOrder();
// Fully Fund maker and taker.
await fundOrderMakerAsync(order);
await takerToken.mint(taker, order.takerAmount).awaitTransactionSuccessAsync();
// Partially fill the order.
const fillAmount = order.takerAmount.times(getRandomFraction()).integerValue();
await fillRfqOrderAsync(order, fillAmount);
// Reduce maker funds to be < remaining.
const remainingMakerAmount = getFillableMakerTokenAmount(order, fillAmount);
const balance = remainingMakerAmount.times(getRandomFraction()).integerValue();
const allowance = remainingMakerAmount.times(getRandomFraction()).integerValue();
await fundOrderMakerAsync(order, balance, allowance);
// Get order state.
const [orderInfo, fillableTakerAmount, isSignatureValid] = await zeroEx
.getRfqOrderRelevantState(order, await order.getSignatureWithProviderAsync(env.provider))
.callAsync();
expect(orderInfo).to.deep.eq({
orderHash: order.getHash(),
status: OrderStatus.Fillable,
takerTokenFilledAmount: fillAmount,
});
expect(fillableTakerAmount).to.bignumber.eq(
getActualFillableTakerTokenAmount(order, balance, allowance, fillAmount),
);
expect(isSignatureValid).to.eq(true);
});
});
async function batchFundOrderMakerAsync(orders: Array<LimitOrder | RfqOrder>): Promise<void> {
await makerToken.burn(maker, await makerToken.balanceOf(maker).callAsync()).awaitTransactionSuccessAsync();
const balance = BigNumber.sum(...orders.map(o => o.makerAmount));
await makerToken.mint(maker, balance).awaitTransactionSuccessAsync();
await makerToken.approve(zeroEx.address, balance).awaitTransactionSuccessAsync({ from: maker });
}
describe('batchGetLimitOrderRelevantStates()', () => {
it('works with multiple orders', async () => {
const orders = new Array(3).fill(0).map(() => getTestLimitOrder());
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: orders[i].getHash(),
status: OrderStatus.Fillable,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmounts[i]).to.bignumber.eq(orders[i].takerAmount);
expect(isSignatureValids[i]).to.eq(true);
}
});
});
describe('batchGetRfqOrderRelevantStates()', () => {
it('works with multiple orders', async () => {
const orders = new Array(3).fill(0).map(() => getTestRfqOrder());
await batchFundOrderMakerAsync(orders);
const [orderInfos, fillableTakerAmounts, isSignatureValids] = await zeroEx
.batchGetRfqOrderRelevantStates(
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: orders[i].getHash(),
status: OrderStatus.Fillable,
takerTokenFilledAmount: ZERO_AMOUNT,
});
expect(fillableTakerAmounts[i]).to.bignumber.eq(orders[i].takerAmount);
expect(isSignatureValids[i]).to.eq(true);
}
});
}); });
}); });

View File

@ -1,4 +1,13 @@
[ [
{
"version": "3.11.0",
"changes": [
{
"note": "Update IZeroEx artifact",
"pr": 97
}
]
},
{ {
"version": "3.10.0", "version": "3.10.0",
"changes": [ "changes": [

View File

@ -457,6 +457,114 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "makerToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "takerToken", "type": "address" },
{ "internalType": "uint128", "name": "makerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerTokenFeeAmount", "type": "uint128" },
{ "internalType": "address", "name": "maker", "type": "address" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "address", "name": "sender", "type": "address" },
{ "internalType": "address", "name": "feeRecipient", "type": "address" },
{ "internalType": "bytes32", "name": "pool", "type": "bytes32" },
{ "internalType": "uint64", "name": "expiry", "type": "uint64" },
{ "internalType": "uint256", "name": "salt", "type": "uint256" }
],
"internalType": "struct LibNativeOrder.LimitOrder[]",
"name": "orders",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature[]",
"name": "signatures",
"type": "tuple[]"
}
],
"name": "batchGetLimitOrderRelevantStates",
"outputs": [
{
"components": [
{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" },
{ "internalType": "enum LibNativeOrder.OrderStatus", "name": "status", "type": "uint8" },
{ "internalType": "uint128", "name": "takerTokenFilledAmount", "type": "uint128" }
],
"internalType": "struct LibNativeOrder.OrderInfo[]",
"name": "orderInfos",
"type": "tuple[]"
},
{ "internalType": "uint128[]", "name": "actualFillableTakerTokenAmounts", "type": "uint128[]" },
{ "internalType": "bool[]", "name": "isSignatureValids", "type": "bool[]" }
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "makerToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "takerToken", "type": "address" },
{ "internalType": "uint128", "name": "makerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerAmount", "type": "uint128" },
{ "internalType": "address", "name": "maker", "type": "address" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "address", "name": "txOrigin", "type": "address" },
{ "internalType": "bytes32", "name": "pool", "type": "bytes32" },
{ "internalType": "uint64", "name": "expiry", "type": "uint64" },
{ "internalType": "uint256", "name": "salt", "type": "uint256" }
],
"internalType": "struct LibNativeOrder.RfqOrder[]",
"name": "orders",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature[]",
"name": "signatures",
"type": "tuple[]"
}
],
"name": "batchGetRfqOrderRelevantStates",
"outputs": [
{
"components": [
{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" },
{ "internalType": "enum LibNativeOrder.OrderStatus", "name": "status", "type": "uint8" },
{ "internalType": "uint128", "name": "takerTokenFilledAmount", "type": "uint128" }
],
"internalType": "struct LibNativeOrder.OrderInfo[]",
"name": "orderInfos",
"type": "tuple[]"
},
{ "internalType": "uint128[]", "name": "actualFillableTakerTokenAmounts", "type": "uint128[]" },
{ "internalType": "bool[]", "name": "isSignatureValids", "type": "bool[]" }
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
@ -834,6 +942,61 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "makerToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "takerToken", "type": "address" },
{ "internalType": "uint128", "name": "makerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerTokenFeeAmount", "type": "uint128" },
{ "internalType": "address", "name": "maker", "type": "address" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "address", "name": "sender", "type": "address" },
{ "internalType": "address", "name": "feeRecipient", "type": "address" },
{ "internalType": "bytes32", "name": "pool", "type": "bytes32" },
{ "internalType": "uint64", "name": "expiry", "type": "uint64" },
{ "internalType": "uint256", "name": "salt", "type": "uint256" }
],
"internalType": "struct LibNativeOrder.LimitOrder",
"name": "order",
"type": "tuple"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature",
"name": "signature",
"type": "tuple"
}
],
"name": "getLimitOrderRelevantState",
"outputs": [
{
"components": [
{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" },
{ "internalType": "enum LibNativeOrder.OrderStatus", "name": "status", "type": "uint8" },
{ "internalType": "uint128", "name": "takerTokenFilledAmount", "type": "uint128" }
],
"internalType": "struct LibNativeOrder.OrderInfo",
"name": "orderInfo",
"type": "tuple"
},
{ "internalType": "uint128", "name": "actualFillableTakerTokenAmount", "type": "uint128" },
{ "internalType": "bool", "name": "isSignatureValid", "type": "bool" }
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
@ -966,6 +1129,59 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "makerToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "takerToken", "type": "address" },
{ "internalType": "uint128", "name": "makerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerAmount", "type": "uint128" },
{ "internalType": "address", "name": "maker", "type": "address" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "address", "name": "txOrigin", "type": "address" },
{ "internalType": "bytes32", "name": "pool", "type": "bytes32" },
{ "internalType": "uint64", "name": "expiry", "type": "uint64" },
{ "internalType": "uint256", "name": "salt", "type": "uint256" }
],
"internalType": "struct LibNativeOrder.RfqOrder",
"name": "order",
"type": "tuple"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature",
"name": "signature",
"type": "tuple"
}
],
"name": "getRfqOrderRelevantState",
"outputs": [
{
"components": [
{ "internalType": "bytes32", "name": "orderHash", "type": "bytes32" },
{ "internalType": "enum LibNativeOrder.OrderStatus", "name": "status", "type": "uint8" },
{ "internalType": "uint128", "name": "takerTokenFilledAmount", "type": "uint128" }
],
"internalType": "struct LibNativeOrder.OrderInfo",
"name": "orderInfo",
"type": "tuple"
},
{ "internalType": "uint128", "name": "actualFillableTakerTokenAmount", "type": "uint128" },
{ "internalType": "bool", "name": "isSignatureValid", "type": "bool" }
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ "internalType": "bytes4", "name": "selector", "type": "bytes4" }, { "internalType": "bytes4", "name": "selector", "type": "bytes4" },
@ -1230,6 +1446,24 @@
}, },
"returns": { "returnResults": "The ABI-encoded results of the underlying calls." } "returns": { "returnResults": "The ABI-encoded results of the underlying calls." }
}, },
"batchGetLimitOrderRelevantStates((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256)[],(uint8,uint8,bytes32,bytes32)[])": {
"details": "Batch version of `getLimitOrderRelevantState()`.",
"params": { "orders": "The limit orders.", "signatures": "The order signatures." },
"returns": {
"actualFillableTakerTokenAmounts": "How much of each order is fillable based on maker funds, in taker tokens.",
"isSignatureValids": "Whether each signature is valid for the order.",
"orderInfos": "Info about the orders."
}
},
"batchGetRfqOrderRelevantStates((address,address,uint128,uint128,address,address,address,bytes32,uint64,uint256)[],(uint8,uint8,bytes32,bytes32)[])": {
"details": "Batch version of `getRfqOrderRelevantState()`.",
"params": { "orders": "The RFQ orders.", "signatures": "The order signatures." },
"returns": {
"actualFillableTakerTokenAmounts": "How much of each order is fillable based on maker funds, in taker tokens.",
"isSignatureValids": "Whether each signature is valid for the order.",
"orderInfos": "Info about the orders."
}
},
"cancelLimitOrder((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256))": { "cancelLimitOrder((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256))": {
"details": "Cancel a single limit order. The caller must be the maker. Silently succeeds if the order has already been cancelled.", "details": "Cancel a single limit order. The caller must be the maker. Silently succeeds if the order has already been cancelled.",
"params": { "order": "The limit order." } "params": { "order": "The limit order." }
@ -1326,6 +1560,15 @@
"params": { "order": "The limit order." }, "params": { "order": "The limit order." },
"returns": { "orderInfo": "Info about the order." } "returns": { "orderInfo": "Info about the order." }
}, },
"getLimitOrderRelevantState((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256),(uint8,uint8,bytes32,bytes32))": {
"details": "Get order info, fillable amount, and signature validity for a limit order. Fillable amount is determined using balances and allowances of the maker.",
"params": { "order": "The limit order.", "signature": "The order signature." },
"returns": {
"actualFillableTakerTokenAmount": "How much of the order is fillable based on maker funds, in taker tokens.",
"isSignatureValid": "Whether the signature is valid.",
"orderInfo": "Info about the order."
}
},
"getMetaTransactionExecutedBlock((address,address,uint256,uint256,uint256,uint256,bytes,uint256,address,uint256))": { "getMetaTransactionExecutedBlock((address,address,uint256,uint256,uint256,uint256,bytes,uint256,address,uint256))": {
"details": "Get the block at which a meta-transaction has been executed.", "details": "Get the block at which a meta-transaction has been executed.",
"params": { "mtx": "The meta-transaction." }, "params": { "mtx": "The meta-transaction." },
@ -1359,6 +1602,15 @@
"params": { "order": "The RFQ order." }, "params": { "order": "The RFQ order." },
"returns": { "orderInfo": "Info about the order." } "returns": { "orderInfo": "Info about the order." }
}, },
"getRfqOrderRelevantState((address,address,uint128,uint128,address,address,address,bytes32,uint64,uint256),(uint8,uint8,bytes32,bytes32))": {
"details": "Get order info, fillable amount, and signature validity for an RFQ order. Fillable amount is determined using balances and allowances of the maker.",
"params": { "order": "The RFQ order.", "signature": "The order signature." },
"returns": {
"actualFillableTakerTokenAmount": "How much of the order is fillable based on maker funds, in taker tokens.",
"isSignatureValid": "Whether the signature is valid.",
"orderInfo": "Info about the order."
}
},
"getRollbackEntryAtIndex(bytes4,uint256)": { "getRollbackEntryAtIndex(bytes4,uint256)": {
"details": "Retrieve an entry in the rollback history for a function.", "details": "Retrieve an entry in the rollback history for a function.",
"params": { "idx": "The index in the rollback history.", "selector": "The function selector." }, "params": { "idx": "The index in the rollback history.", "selector": "The function selector." },

View File

@ -1,4 +1,13 @@
[ [
{
"version": "13.12.0",
"changes": [
{
"note": "Update IZeroExContract wrapper",
"pr": 97
}
]
},
{ {
"timestamp": 1608692071, "timestamp": 1608692071,
"version": "13.11.2", "version": "13.11.2",

View File

@ -1229,6 +1229,220 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'payable', stateMutability: 'payable',
type: 'function', type: 'function',
}, },
{
inputs: [
{
name: 'orders',
type: 'tuple[]',
components: [
{
name: 'makerToken',
type: 'address',
},
{
name: 'takerToken',
type: 'address',
},
{
name: 'makerAmount',
type: 'uint128',
},
{
name: 'takerAmount',
type: 'uint128',
},
{
name: 'takerTokenFeeAmount',
type: 'uint128',
},
{
name: 'maker',
type: 'address',
},
{
name: 'taker',
type: 'address',
},
{
name: 'sender',
type: 'address',
},
{
name: 'feeRecipient',
type: 'address',
},
{
name: 'pool',
type: 'bytes32',
},
{
name: 'expiry',
type: 'uint64',
},
{
name: 'salt',
type: 'uint256',
},
],
},
{
name: 'signatures',
type: 'tuple[]',
components: [
{
name: 'signatureType',
type: 'uint8',
},
{
name: 'v',
type: 'uint8',
},
{
name: 'r',
type: 'bytes32',
},
{
name: 's',
type: 'bytes32',
},
],
},
],
name: 'batchGetLimitOrderRelevantStates',
outputs: [
{
name: 'orderInfos',
type: 'tuple[]',
components: [
{
name: 'orderHash',
type: 'bytes32',
},
{
name: 'status',
type: 'uint8',
},
{
name: 'takerTokenFilledAmount',
type: 'uint128',
},
],
},
{
name: 'actualFillableTakerTokenAmounts',
type: 'uint128[]',
},
{
name: 'isSignatureValids',
type: 'bool[]',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
name: 'orders',
type: 'tuple[]',
components: [
{
name: 'makerToken',
type: 'address',
},
{
name: 'takerToken',
type: 'address',
},
{
name: 'makerAmount',
type: 'uint128',
},
{
name: 'takerAmount',
type: 'uint128',
},
{
name: 'maker',
type: 'address',
},
{
name: 'taker',
type: 'address',
},
{
name: 'txOrigin',
type: 'address',
},
{
name: 'pool',
type: 'bytes32',
},
{
name: 'expiry',
type: 'uint64',
},
{
name: 'salt',
type: 'uint256',
},
],
},
{
name: 'signatures',
type: 'tuple[]',
components: [
{
name: 'signatureType',
type: 'uint8',
},
{
name: 'v',
type: 'uint8',
},
{
name: 'r',
type: 'bytes32',
},
{
name: 's',
type: 'bytes32',
},
],
},
],
name: 'batchGetRfqOrderRelevantStates',
outputs: [
{
name: 'orderInfos',
type: 'tuple[]',
components: [
{
name: 'orderHash',
type: 'bytes32',
},
{
name: 'status',
type: 'uint8',
},
{
name: 'takerTokenFilledAmount',
type: 'uint128',
},
],
},
{
name: 'actualFillableTakerTokenAmounts',
type: 'uint128[]',
},
{
name: 'isSignatureValids',
type: 'bool[]',
},
],
stateMutability: 'view',
type: 'function',
},
{ {
inputs: [ inputs: [
{ {
@ -2018,6 +2232,117 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'view', stateMutability: 'view',
type: 'function', type: 'function',
}, },
{
inputs: [
{
name: 'order',
type: 'tuple',
components: [
{
name: 'makerToken',
type: 'address',
},
{
name: 'takerToken',
type: 'address',
},
{
name: 'makerAmount',
type: 'uint128',
},
{
name: 'takerAmount',
type: 'uint128',
},
{
name: 'takerTokenFeeAmount',
type: 'uint128',
},
{
name: 'maker',
type: 'address',
},
{
name: 'taker',
type: 'address',
},
{
name: 'sender',
type: 'address',
},
{
name: 'feeRecipient',
type: 'address',
},
{
name: 'pool',
type: 'bytes32',
},
{
name: 'expiry',
type: 'uint64',
},
{
name: 'salt',
type: 'uint256',
},
],
},
{
name: 'signature',
type: 'tuple',
components: [
{
name: 'signatureType',
type: 'uint8',
},
{
name: 'v',
type: 'uint8',
},
{
name: 'r',
type: 'bytes32',
},
{
name: 's',
type: 'bytes32',
},
],
},
],
name: 'getLimitOrderRelevantState',
outputs: [
{
name: 'orderInfo',
type: 'tuple',
components: [
{
name: 'orderHash',
type: 'bytes32',
},
{
name: 'status',
type: 'uint8',
},
{
name: 'takerTokenFilledAmount',
type: 'uint128',
},
],
},
{
name: 'actualFillableTakerTokenAmount',
type: 'uint128',
},
{
name: 'isSignatureValid',
type: 'bool',
},
],
stateMutability: 'view',
type: 'function',
},
{ {
inputs: [ inputs: [
{ {
@ -2309,6 +2634,109 @@ export class IZeroExContract extends BaseContract {
stateMutability: 'view', stateMutability: 'view',
type: 'function', type: 'function',
}, },
{
inputs: [
{
name: 'order',
type: 'tuple',
components: [
{
name: 'makerToken',
type: 'address',
},
{
name: 'takerToken',
type: 'address',
},
{
name: 'makerAmount',
type: 'uint128',
},
{
name: 'takerAmount',
type: 'uint128',
},
{
name: 'maker',
type: 'address',
},
{
name: 'taker',
type: 'address',
},
{
name: 'txOrigin',
type: 'address',
},
{
name: 'pool',
type: 'bytes32',
},
{
name: 'expiry',
type: 'uint64',
},
{
name: 'salt',
type: 'uint256',
},
],
},
{
name: 'signature',
type: 'tuple',
components: [
{
name: 'signatureType',
type: 'uint8',
},
{
name: 'v',
type: 'uint8',
},
{
name: 'r',
type: 'bytes32',
},
{
name: 's',
type: 'bytes32',
},
],
},
],
name: 'getRfqOrderRelevantState',
outputs: [
{
name: 'orderInfo',
type: 'tuple',
components: [
{
name: 'orderHash',
type: 'bytes32',
},
{
name: 'status',
type: 'uint8',
},
{
name: 'takerTokenFilledAmount',
type: 'uint128',
},
],
},
{
name: 'actualFillableTakerTokenAmount',
type: 'uint128',
},
{
name: 'isSignatureValid',
type: 'bool',
},
],
stateMutability: 'view',
type: 'function',
},
{ {
inputs: [ inputs: [
{ {
@ -3429,6 +3857,178 @@ export class IZeroExContract extends BaseContract {
}, },
}; };
} }
/**
* Batch version of `getLimitOrderRelevantState()`.
* @param orders The limit orders.
* @param signatures The order signatures.
*/
public batchGetLimitOrderRelevantStates(
orders: Array<{
makerToken: string;
takerToken: string;
makerAmount: BigNumber;
takerAmount: BigNumber;
takerTokenFeeAmount: BigNumber;
maker: string;
taker: string;
sender: string;
feeRecipient: string;
pool: string;
expiry: BigNumber;
salt: BigNumber;
}>,
signatures: Array<{ signatureType: number | BigNumber; v: number | BigNumber; r: string; s: string }>,
): ContractTxFunctionObj<
[Array<{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }>, BigNumber[], boolean[]]
> {
const self = (this as any) as IZeroExContract;
assert.isArray('orders', orders);
assert.isArray('signatures', signatures);
const functionSignature =
'batchGetLimitOrderRelevantStates((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256)[],(uint8,uint8,bytes32,bytes32)[])';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<
[
Array<{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }>,
BigNumber[],
boolean[]
]
> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<
[
Array<{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }>,
BigNumber[],
boolean[]
]
>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [orders, signatures]);
},
};
}
/**
* Batch version of `getRfqOrderRelevantState()`.
* @param orders The RFQ orders.
* @param signatures The order signatures.
*/
public batchGetRfqOrderRelevantStates(
orders: Array<{
makerToken: string;
takerToken: string;
makerAmount: BigNumber;
takerAmount: BigNumber;
maker: string;
taker: string;
txOrigin: string;
pool: string;
expiry: BigNumber;
salt: BigNumber;
}>,
signatures: Array<{ signatureType: number | BigNumber; v: number | BigNumber; r: string; s: string }>,
): ContractTxFunctionObj<
[Array<{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }>, BigNumber[], boolean[]]
> {
const self = (this as any) as IZeroExContract;
assert.isArray('orders', orders);
assert.isArray('signatures', signatures);
const functionSignature =
'batchGetRfqOrderRelevantStates((address,address,uint128,uint128,address,address,address,bytes32,uint64,uint256)[],(uint8,uint8,bytes32,bytes32)[])';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<
[
Array<{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }>,
BigNumber[],
boolean[]
]
> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<
[
Array<{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }>,
BigNumber[],
boolean[]
]
>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [orders, signatures]);
},
};
}
/** /**
* Cancel a single limit order. The caller must be the maker. * Cancel a single limit order. The caller must be the maker.
* Silently succeeds if the order has already been cancelled. * Silently succeeds if the order has already been cancelled.
@ -4346,6 +4946,83 @@ export class IZeroExContract extends BaseContract {
}, },
}; };
} }
/**
* Get order info, fillable amount, and signature validity for a limit order.
* Fillable amount is determined using balances and allowances of the maker.
* @param order The limit order.
* @param signature The order signature.
*/
public getLimitOrderRelevantState(
order: {
makerToken: string;
takerToken: string;
makerAmount: BigNumber;
takerAmount: BigNumber;
takerTokenFeeAmount: BigNumber;
maker: string;
taker: string;
sender: string;
feeRecipient: string;
pool: string;
expiry: BigNumber;
salt: BigNumber;
},
signature: { signatureType: number | BigNumber; v: number | BigNumber; r: string; s: string },
): ContractTxFunctionObj<
[{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }, BigNumber, boolean]
> {
const self = (this as any) as IZeroExContract;
const functionSignature =
'getLimitOrderRelevantState((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256),(uint8,uint8,bytes32,bytes32))';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<[{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }, BigNumber, boolean]> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<
[{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }, BigNumber, boolean]
>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [order, signature]);
},
};
}
/** /**
* Get the block at which a meta-transaction has been executed. * Get the block at which a meta-transaction has been executed.
* @param mtx The meta-transaction. * @param mtx The meta-transaction.
@ -4755,6 +5432,81 @@ export class IZeroExContract extends BaseContract {
}, },
}; };
} }
/**
* Get order info, fillable amount, and signature validity for an RFQ order.
* Fillable amount is determined using balances and allowances of the maker.
* @param order The RFQ order.
* @param signature The order signature.
*/
public getRfqOrderRelevantState(
order: {
makerToken: string;
takerToken: string;
makerAmount: BigNumber;
takerAmount: BigNumber;
maker: string;
taker: string;
txOrigin: string;
pool: string;
expiry: BigNumber;
salt: BigNumber;
},
signature: { signatureType: number | BigNumber; v: number | BigNumber; r: string; s: string },
): ContractTxFunctionObj<
[{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }, BigNumber, boolean]
> {
const self = (this as any) as IZeroExContract;
const functionSignature =
'getRfqOrderRelevantState((address,address,uint128,uint128,address,address,address,bytes32,uint64,uint256),(uint8,uint8,bytes32,bytes32))';
return {
async sendTransactionAsync(
txData?: Partial<TxData> | undefined,
opts: SendTransactionOpts = { shouldValidate: true },
): Promise<string> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(
{ data: this.getABIEncodedTransactionData(), ...txData },
this.estimateGasAsync.bind(this),
);
if (opts.shouldValidate !== false) {
await this.callAsync(txDataWithDefaults);
}
return self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
},
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
opts: AwaitTransactionSuccessOpts = { shouldValidate: true },
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
return self._promiseWithTransactionHash(this.sendTransactionAsync(txData, opts), opts);
},
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const txDataWithDefaults = await self._applyDefaultsToTxDataAsync({
data: this.getABIEncodedTransactionData(),
...txData,
});
return self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
},
async callAsync(
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<[{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }, BigNumber, boolean]> {
BaseContract._assertCallParams(callData, defaultBlock);
const rawCallResult = await self._performCallAsync(
{ data: this.getABIEncodedTransactionData(), ...callData },
defaultBlock,
);
const abiEncoder = self._lookupAbiEncoder(functionSignature);
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
return abiEncoder.strictDecodeReturnValue<
[{ orderHash: string; status: number; takerTokenFilledAmount: BigNumber }, BigNumber, boolean]
>(rawCallResult);
},
getABIEncodedTransactionData(): string {
return self._strictEncodeArguments(functionSignature, [order, signature]);
},
};
}
/** /**
* Retrieve an entry in the rollback history for a function. * Retrieve an entry in the rollback history for a function.
* @param selector The function selector. * @param selector The function selector.