Update OrderValidationUtils to return fillableTakerAssetAmount and refactor tests

This commit is contained in:
Amir Bandeali 2019-06-03 10:42:46 -07:00
parent 0db56a781e
commit 613af6013a
2 changed files with 417 additions and 576 deletions

View File

@ -21,26 +21,17 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetData.sol"; import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetData.sol";
contract OrderValidationUtils is contract OrderValidationUtils is
LibAssetData LibAssetData,
LibMath
{ {
using LibBytes for bytes; using LibBytes for bytes;
struct TraderInfo {
uint256 makerBalance; // Maker's balance of makerAsset
uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy
uint256 takerBalance; // Taker's balance of takerAsset
uint256 takerAllowance; // Taker's allowance to corresponding AssetProxy
uint256 makerZrxBalance; // Maker's balance of ZRX
uint256 makerZrxAllowance; // Maker's allowance of ZRX to ERC20Proxy
uint256 takerZrxBalance; // Taker's balance of ZRX
uint256 takerZrxAllowance; // Taker's allowance of ZRX to ERC20Proxy
}
// solhint-disable var-name-mixedcase // solhint-disable var-name-mixedcase
IExchange internal EXCHANGE; IExchange internal EXCHANGE;
bytes internal ZRX_ASSET_DATA; bytes internal ZRX_ASSET_DATA;
@ -58,116 +49,142 @@ contract OrderValidationUtils is
/// @dev Fetches information for order and maker/taker of order. /// @dev Fetches information for order and maker/taker of order.
/// @param order The order structure. /// @param order The order structure.
/// @param signature Proof that order has been created by maker. /// @param signature Proof that order has been created by maker.
/// @param takerAddress Address that will be filling the order. /// @return OrderInfo, the remaining amount fillable by the taker, and validity of signature for given order.
/// @return OrderInfo, TraderInfo, and validity of signature for given order. function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature)
function getOrderAndTraderInfo(
LibOrder.Order memory order,
bytes memory signature,
address takerAddress
)
public public
view view
returns ( returns (
LibOrder.OrderInfo memory orderInfo, LibOrder.OrderInfo memory orderInfo,
TraderInfo memory traderInfo, uint256 fillableTakerAssetAmount,
bool isValidSignature bool isValidSignature
) )
{ {
// Get info specific to order
orderInfo = EXCHANGE.getOrderInfo(order); orderInfo = EXCHANGE.getOrderInfo(order);
// Validate the maker's signature
// If the signature does not need to be validated, `0x01` can be supplied for the signature to always return `false`.
address makerAddress = order.makerAddress;
isValidSignature = EXCHANGE.isValidSignature( isValidSignature = EXCHANGE.isValidSignature(
orderInfo.orderHash, orderInfo.orderHash,
order.makerAddress, makerAddress,
signature signature
); );
traderInfo = getTraderInfo(order, takerAddress);
return (orderInfo, traderInfo, isValidSignature); // Get the transferable amount of the `makerAsset`
uint256 transferableMakerAssetAmount = getTransferableAssetAmount(order.makerAssetData, makerAddress);
// Assign to stack variables to reduce redundant mloads
uint256 takerAssetAmount = order.takerAssetAmount;
uint256 makerFee = order.makerFee;
// Get the amount of `takerAsset` that is purchasable given the transferability of `makerAsset` and `makerFeeAsset`
uint256 purchasableTakerAssetAmount;
if (order.makerAssetData.equals(ZRX_ASSET_DATA)) {
// If `makerAsset` equals `makerFeeAsset`, the % that can be filled is
// transferableMakerAssetAmount / (makerAssetAmount + makerFee)
purchasableTakerAssetAmount = getPartialAmountFloor(
transferableMakerAssetAmount,
safeAdd(order.makerAssetAmount, makerFee),
takerAssetAmount
);
} else {
// Get the transferable amount of the `makerFeeAsset`
uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(ZRX_ASSET_DATA, makerAddress);
// If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of
// (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee)
// If `makerFee` is 0, we default to using `transferableMakerAssetAmount`
purchasableTakerAssetAmount = makerFee == 0
? getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
)
: min256(
getPartialAmountFloor(
transferableMakerAssetAmount,
order.makerAssetAmount,
takerAssetAmount
),
getPartialAmountFloor(
transferableMakerFeeAssetAmount,
makerFee,
takerAssetAmount
)
);
}
// `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `purchasableTakerAssetAmount`
fillableTakerAssetAmount = min256(
safeSub(takerAssetAmount, orderInfo.orderTakerAssetFilledAmount),
purchasableTakerAssetAmount
);
return (orderInfo, fillableTakerAssetAmount, isValidSignature);
} }
/// @dev Fetches information for all passed in orders and the makers/takers of each order. /// @dev Fetches information for all passed in orders and the makers/takers of each order.
/// @param orders Array of order specifications. /// @param orders Array of order specifications.
/// @param signatures Proofs that orders have been created by makers. /// @param signatures Proofs that orders have been created by makers.
/// @param takerAddresses Array of taker addresses corresponding to each order. /// @return Arrays of OrderInfo, fillable takerAssetAmounts, and validity of signatures that correspond to each order.
/// @return Arrays of OrderInfo, TraderInfo, and validity of signatures that correspond to each order. function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures)
function getOrdersAndTradersInfo(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
address[] memory takerAddresses
)
public public
view view
returns ( returns (
LibOrder.OrderInfo[] memory ordersInfo, LibOrder.OrderInfo[] memory ordersInfo,
TraderInfo[] memory tradersInfo, uint256[] memory fillableTakerAssetAmounts,
bool[] memory isValidSignature bool[] memory isValidSignature
) )
{ {
ordersInfo = EXCHANGE.getOrdersInfo(orders);
tradersInfo = getTradersInfo(orders, takerAddresses);
uint256 length = orders.length; uint256 length = orders.length;
ordersInfo = new LibOrder.OrderInfo[](length);
fillableTakerAssetAmounts = new uint256[](length);
isValidSignature = new bool[](length); isValidSignature = new bool[](length);
for (uint256 i = 0; i != length; i++) { for (uint256 i = 0; i != length; i++) {
isValidSignature[i] = EXCHANGE.isValidSignature( (ordersInfo[i], fillableTakerAssetAmounts[i], isValidSignature[i]) = getOrderRelevantState(
ordersInfo[i].orderHash, orders[i],
orders[i].makerAddress,
signatures[i] signatures[i]
); );
} }
return (ordersInfo, tradersInfo, isValidSignature); return (ordersInfo, fillableTakerAssetAmounts, isValidSignature);
} }
/// @dev Fetches balance and allowances for maker and taker of order. /// @dev Gets the address of the AssetProxy that corresponds to the given assetData.
/// @param order The order structure. /// @param assetData Description of tokens, per the AssetProxy contract specification.
/// @param takerAddress Address that will be filling the order. /// @return Address of the AssetProxy contract.
/// @return Balances and allowances of maker and taker of order. function getAssetProxyAddress(bytes memory assetData)
function getTraderInfo(LibOrder.Order memory order, address takerAddress)
public public
view view
returns (TraderInfo memory traderInfo) returns (address assetProxyAddress)
{ {
bytes4 makerAssetProxyId = order.makerAssetData.readBytes4(0); if (assetData.equals(ZRX_ASSET_DATA)) {
bytes4 takerAssetProxyId = order.takerAssetData.readBytes4(0); return ERC20_PROXY_ADDRESS;
(traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(
order.makerAddress,
EXCHANGE.getAssetProxy(makerAssetProxyId),
order.makerAssetData
);
(traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(
takerAddress,
EXCHANGE.getAssetProxy(takerAssetProxyId),
order.takerAssetData
);
bytes memory zrxAssetData = ZRX_ASSET_DATA;
address erc20ProxyAddress = ERC20_PROXY_ADDRESS;
(traderInfo.makerZrxBalance, traderInfo.makerZrxAllowance) = getBalanceAndAllowance(
order.makerAddress,
erc20ProxyAddress,
zrxAssetData
);
(traderInfo.takerZrxBalance, traderInfo.takerZrxAllowance) = getBalanceAndAllowance(
takerAddress,
erc20ProxyAddress,
zrxAssetData
);
return traderInfo;
}
/// @dev Fetches balances and allowances of maker and taker for each provided order.
/// @param orders Array of order specifications.
/// @param takerAddresses Array of taker addresses corresponding to each order.
/// @return Array of balances and allowances for maker and taker of each order.
function getTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)
public
view
returns (TraderInfo[] memory)
{
uint256 ordersLength = orders.length;
TraderInfo[] memory tradersInfo = new TraderInfo[](ordersLength);
for (uint256 i = 0; i != ordersLength; i++) {
tradersInfo[i] = getTraderInfo(orders[i], takerAddresses[i]);
} }
return tradersInfo; bytes4 assetProxyId = assetData.readBytes4(0);
assetProxyAddress = EXCHANGE.getAssetProxy(assetProxyId);
return assetProxyAddress;
}
/// @dev Gets the amount of an asset transferable by the owner.
/// @param assetData Description of tokens, per the AssetProxy contract specification.
/// @param ownerAddress Address of the owner of the asset.
/// @return The amount of the asset tranferable by the owner.
function getTransferableAssetAmount(bytes memory assetData, address ownerAddress)
public
view
returns (uint256 transferableAssetAmount)
{
uint256 assetBalance = getBalance(ownerAddress, assetData);
address assetProxyAddress = getAssetProxyAddress(assetData);
uint256 assetAllowance = getAllowance(
ownerAddress,
assetProxyAddress,
assetData
);
transferableAssetAmount = min256(assetBalance, assetAllowance);
return transferableAssetAmount;
} }
} }

View File

@ -26,12 +26,15 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('DevUtils', () => { describe('DevUtils', () => {
let makerAddress: string; let makerAddress: string;
let owner: string;
let takerAddress: string; let takerAddress: string;
let owner: string;
let erc20AssetData: string; let erc20AssetData: string;
let erc20AssetData2: string;
let erc721AssetData: string; let erc721AssetData: string;
let zrxAssetData: string;
let erc20Token: DummyERC20TokenContract; let erc20Token: DummyERC20TokenContract;
let erc20Token2: DummyERC20TokenContract;
let zrxToken: DummyERC20TokenContract; let zrxToken: DummyERC20TokenContract;
let erc721Token: DummyERC721TokenContract; let erc721Token: DummyERC721TokenContract;
let exchange: ExchangeContract; let exchange: ExchangeContract;
@ -40,12 +43,9 @@ describe('DevUtils', () => {
let erc721Proxy: ERC721ProxyContract; let erc721Proxy: ERC721ProxyContract;
let signedOrder: SignedOrder; let signedOrder: SignedOrder;
let signedOrder2: SignedOrder;
let orderFactory: OrderFactory; let orderFactory: OrderFactory;
const tokenId = new BigNumber(123456789); const tokenId = new BigNumber(123456789);
const tokenId2 = new BigNumber(987654321);
const ERC721_BALANCE = new BigNumber(1);
before(async () => { before(async () => {
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
@ -61,8 +61,8 @@ describe('DevUtils', () => {
const erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); const erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
const numDummyErc20ToDeploy = 2; const numDummyErc20ToDeploy = 3;
[erc20Token, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( [erc20Token, zrxToken, erc20Token2] = await erc20Wrapper.deployDummyTokensAsync(
numDummyErc20ToDeploy, numDummyErc20ToDeploy,
constants.DUMMY_TOKEN_DECIMALS, constants.DUMMY_TOKEN_DECIMALS,
); );
@ -71,7 +71,7 @@ describe('DevUtils', () => {
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
erc721Proxy = await erc721Wrapper.deployProxyAsync(); erc721Proxy = await erc721Wrapper.deployProxyAsync();
const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
exchange = await ExchangeContract.deployFrom0xArtifactAsync( exchange = await ExchangeContract.deployFrom0xArtifactAsync(
artifacts.Exchange, artifacts.Exchange,
provider, provider,
@ -81,6 +81,8 @@ describe('DevUtils', () => {
const exchangeWrapper = new ExchangeWrapper(exchange, provider); const exchangeWrapper = new ExchangeWrapper(exchange, provider);
await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner });
await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner });
devUtils = await DevUtilsContract.deployFrom0xArtifactAsync( devUtils = await DevUtilsContract.deployFrom0xArtifactAsync(
artifacts.DevUtils, artifacts.DevUtils,
@ -91,6 +93,7 @@ describe('DevUtils', () => {
); );
erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address); erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20Token2.address);
erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId); erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId);
const defaultOrderParams = { const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS, ...constants.STATIC_ORDER_PARAMS,
@ -98,7 +101,7 @@ describe('DevUtils', () => {
makerAddress, makerAddress,
feeRecipientAddress: constants.NULL_ADDRESS, feeRecipientAddress: constants.NULL_ADDRESS,
makerAssetData: erc20AssetData, makerAssetData: erc20AssetData,
takerAssetData: erc721AssetData, takerAssetData: erc20AssetData2,
}; };
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams); orderFactory = new OrderFactory(privateKey, defaultOrderParams);
@ -111,133 +114,21 @@ describe('DevUtils', () => {
await blockchainLifecycle.revertAsync(); await blockchainLifecycle.revertAsync();
}); });
describe('getBalanceAndAllowance', () => { describe('getAssetProxyAddress', () => {
describe('getERC721TokenOwner', async () => { it('should return the address of registered proxies', async () => {
it('should return the null address when tokenId is not owned', async () => { const erc20ProxyAddress = await devUtils.getAssetProxyAddress.callAsync(erc20AssetData);
const tokenOwner = await devUtils.getERC721TokenOwner.callAsync(makerAddress, tokenId); const erc721ProxyAddress = await devUtils.getAssetProxyAddress.callAsync(erc721AssetData);
expect(tokenOwner).to.be.equal(constants.NULL_ADDRESS); expect(erc20ProxyAddress).to.equal(erc20Proxy.address);
}); expect(erc721ProxyAddress).to.equal(erc721Proxy.address);
it('should return the owner address when tokenId is owned', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.mint.sendTransactionAsync(makerAddress, tokenId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const tokenOwner = await devUtils.getERC721TokenOwner.callAsync(erc721Token.address, tokenId);
expect(tokenOwner).to.be.equal(makerAddress);
});
}); });
describe('ERC20 assetData', () => { it('should return the null address if the assetProxy does not exist', async () => {
it('should return the correct balances and allowances when both values are 0', async () => { const invalidAssetData = '0x01020304';
const [newBalance, newAllowance] = await devUtils.getBalanceAndAllowance.callAsync( const assetProxyAddress = await devUtils.getAssetProxyAddress.callAsync(invalidAssetData);
makerAddress, expect(assetProxyAddress).to.equal(constants.NULL_ADDRESS);
erc20Proxy.address,
erc20AssetData,
);
expect(constants.ZERO_AMOUNT).to.be.bignumber.equal(newBalance);
expect(constants.ZERO_AMOUNT).to.be.bignumber.equal(newAllowance);
});
it('should return the correct balance and allowance when both values are non-zero', async () => {
const balance = new BigNumber(123);
const allowance = new BigNumber(456);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(makerAddress, balance),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [newBalance, newAllowance] = await devUtils.getBalanceAndAllowance.callAsync(
makerAddress,
erc20Proxy.address,
erc20AssetData,
);
expect(balance).to.be.bignumber.equal(newBalance);
expect(allowance).to.be.bignumber.equal(newAllowance);
});
});
describe('ERC721 assetData', () => {
it('should return a balance of 0 when the tokenId is not owned by target', async () => {
const [newBalance] = await devUtils.getBalanceAndAllowance.callAsync(
makerAddress,
erc721Proxy.address,
erc721AssetData,
);
expect(newBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return an allowance of 0 when no approval is set', async () => {
const [, newAllowance] = await devUtils.getBalanceAndAllowance.callAsync(
makerAddress,
erc721Proxy.address,
erc721AssetData,
);
expect(newAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return a balance of 1 when the tokenId is owned by target', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.mint.sendTransactionAsync(makerAddress, tokenId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [newBalance] = await devUtils.getBalanceAndAllowance.callAsync(
makerAddress,
erc721Proxy.address,
erc721AssetData,
);
expect(newBalance).to.be.bignumber.equal(ERC721_BALANCE);
});
it('should return an allowance of MAX_UINT256 when ERC721Proxy is approved for all', async () => {
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [, newAllowance] = await devUtils.getBalanceAndAllowance.callAsync(
makerAddress,
erc721Proxy.address,
erc721AssetData,
);
expect(newAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
});
it('should return an allowance of 1 when ERC721Proxy is approved for specific tokenId', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.mint.sendTransactionAsync(makerAddress, tokenId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.approve.sendTransactionAsync(erc721Proxy.address, tokenId, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [, newAllowance] = await devUtils.getBalanceAndAllowance.callAsync(
makerAddress,
erc721Proxy.address,
erc721AssetData,
);
expect(newAllowance).to.be.bignumber.equal(new BigNumber(1));
});
}); });
}); });
describe('getBatchBalancesAndAllowances', () => { describe('getTransferableAssetAmount', () => {
it('should return the correct balances and allowances when all values are 0', async () => { it('should return the balance when balance < allowance', async () => {
const [
[erc20Balance, erc721Balance],
[erc20Allowance, erc721Allowance],
] = await devUtils.getBatchBalancesAndAllowances.callAsync(
makerAddress,
[erc20Proxy.address, erc721Proxy.address],
[erc20AssetData, erc721AssetData],
);
expect(erc20Balance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(erc721Balance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(erc20Allowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(erc721Allowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return the correct balances and allowances when balances and allowances are non-zero', async () => {
const balance = new BigNumber(123); const balance = new BigNumber(123);
const allowance = new BigNumber(456); const allowance = new BigNumber(456);
await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.awaitTransactionSuccessAsync(
@ -250,403 +141,336 @@ describe('DevUtils', () => {
}), }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync(
erc20AssetData,
makerAddress,
);
expect(transferableAmount).to.bignumber.equal(balance);
});
it('should return the allowance when allowance < balance', async () => {
const balance = new BigNumber(456);
const allowance = new BigNumber(123);
await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.mint.sendTransactionAsync(makerAddress, tokenId), await erc20Token.setBalance.sendTransactionAsync(makerAddress, balance),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, { await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
from: makerAddress, from: makerAddress,
}), }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const [ const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync(
[erc20Balance, erc721Balance], erc20AssetData,
[erc20Allowance, erc721Allowance],
] = await devUtils.getBatchBalancesAndAllowances.callAsync(
makerAddress, makerAddress,
[erc20Proxy.address, erc721Proxy.address],
[erc20AssetData, erc721AssetData],
); );
expect(erc20Balance).to.be.bignumber.equal(balance); expect(transferableAmount).to.bignumber.equal(allowance);
expect(erc721Balance).to.be.bignumber.equal(ERC721_BALANCE);
expect(erc20Allowance).to.be.bignumber.equal(allowance);
expect(erc721Allowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
}); });
}); });
describe('getTraderInfo', () => { describe('getOrderRelevantState', () => {
beforeEach(async () => { beforeEach(async () => {
signedOrder = await orderFactory.newSignedOrderAsync(); signedOrder = await orderFactory.newSignedOrderAsync();
}); });
it('should return the correct info when no balances or allowances are set', async () => { it('should return the correct orderInfo when the order is valid', async () => {
const traderInfo = await devUtils.getTraderInfo.callAsync(signedOrder, takerAddress); const [orderInfo] = await devUtils.getOrderRelevantState.callAsync(signedOrder, signedOrder.signature);
expect(traderInfo.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); expect(orderInfo.orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder));
expect(traderInfo.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT); expect(orderInfo.orderStatus).to.equal(OrderStatus.Fillable);
expect(traderInfo.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); expect(orderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
}); });
it('should return the correct info when balances and allowances are set', async () => { it('should return isValidSignature=true when the signature is valid', async () => {
const makerBalance = new BigNumber(123); const [, , isValidSignature] = await devUtils.getOrderRelevantState.callAsync(
const makerAllowance = new BigNumber(456);
const makerZrxBalance = new BigNumber(789);
const takerZrxAllowance = new BigNumber(987);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(makerAddress, makerBalance),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, makerAllowance, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(makerAddress, makerZrxBalance),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, takerZrxAllowance, {
from: takerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.mint.sendTransactionAsync(takerAddress, tokenId),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
from: takerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const traderInfo = await devUtils.getTraderInfo.callAsync(signedOrder, takerAddress);
expect(traderInfo.makerBalance).to.be.bignumber.equal(makerBalance);
expect(traderInfo.makerAllowance).to.be.bignumber.equal(makerAllowance);
expect(traderInfo.takerBalance).to.be.bignumber.equal(ERC721_BALANCE);
expect(traderInfo.takerAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
expect(traderInfo.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
expect(traderInfo.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
});
});
describe('getTradersInfo', () => {
beforeEach(async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
signedOrder2 = await orderFactory.newSignedOrderAsync({
takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId2),
});
});
it('should return the correct info when no balances or allowances have been set', async () => {
const orders = [signedOrder, signedOrder2];
const takers = [takerAddress, takerAddress];
const [traderInfo1, traderInfo2] = await devUtils.getTradersInfo.callAsync(orders, takers);
expect(traderInfo1.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return the correct info when balances and allowances are set', async () => {
const makerBalance = new BigNumber(123);
const makerAllowance = new BigNumber(456);
const makerZrxBalance = new BigNumber(789);
const takerZrxAllowance = new BigNumber(987);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(makerAddress, makerBalance),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, makerAllowance, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(makerAddress, makerZrxBalance),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, takerZrxAllowance, {
from: takerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
from: takerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.mint.sendTransactionAsync(takerAddress, tokenId2),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const orders = [signedOrder, signedOrder2];
const takers = [takerAddress, takerAddress];
const [traderInfo1, traderInfo2] = await devUtils.getTradersInfo.callAsync(orders, takers);
expect(traderInfo1.makerBalance).to.be.bignumber.equal(makerBalance);
expect(traderInfo1.makerAllowance).to.be.bignumber.equal(makerAllowance);
expect(traderInfo1.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
expect(traderInfo1.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
expect(traderInfo1.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
expect(traderInfo2.makerBalance).to.be.bignumber.equal(makerBalance);
expect(traderInfo2.makerAllowance).to.be.bignumber.equal(makerAllowance);
expect(traderInfo2.takerBalance).to.be.bignumber.equal(ERC721_BALANCE);
expect(traderInfo2.takerAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
expect(traderInfo2.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
expect(traderInfo2.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
});
});
describe('getOrderAndTraderInfo', () => {
beforeEach(async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
});
it('should return the correct info when no balances/allowances are set and the signature is invalid', async () => {
const invalidSignature = '0x01';
const [orderInfo, traderInfo, isValidSignature] = await devUtils.getOrderAndTraderInfo.callAsync(
signedOrder,
invalidSignature,
takerAddress,
);
const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.Fillable);
expect(orderInfo.orderHash).to.be.equal(expectedOrderHash);
expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(isValidSignature).to.equal(false);
});
it('should return the correct info when balances/allowances are set and the signature is valid', async () => {
const makerBalance = new BigNumber(123);
const makerAllowance = new BigNumber(456);
const makerZrxBalance = new BigNumber(789);
const takerZrxAllowance = new BigNumber(987);
const txData = undefined;
await erc20Token.setBalance.awaitTransactionSuccessAsync(
makerAddress,
makerBalance,
txData,
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc20Token.approve.awaitTransactionSuccessAsync(
erc20Proxy.address,
makerAllowance,
{
from: makerAddress,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
await zrxToken.setBalance.awaitTransactionSuccessAsync(
makerAddress,
makerZrxBalance,
txData,
constants.AWAIT_TRANSACTION_MINED_MS,
);
await zrxToken.approve.awaitTransactionSuccessAsync(
erc20Proxy.address,
takerZrxAllowance,
{
from: takerAddress,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
await erc721Token.mint.awaitTransactionSuccessAsync(
takerAddress,
tokenId,
{
from: takerAddress,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
const isApproved = true;
await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync(
erc721Proxy.address,
isApproved,
{
from: takerAddress,
},
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [orderInfo, traderInfo, isValidSignature] = await devUtils.getOrderAndTraderInfo.callAsync(
signedOrder, signedOrder,
signedOrder.signature, signedOrder.signature,
takerAddress,
); );
const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.Fillable);
expect(orderInfo.orderHash).to.be.equal(expectedOrderHash);
expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.makerBalance).to.be.bignumber.equal(makerBalance);
expect(traderInfo.makerAllowance).to.be.bignumber.equal(makerAllowance);
expect(traderInfo.takerBalance).to.be.bignumber.equal(ERC721_BALANCE);
expect(traderInfo.takerAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
expect(traderInfo.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
expect(traderInfo.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
expect(isValidSignature).to.equal(true); expect(isValidSignature).to.equal(true);
}); });
}); it('should return isValidSignature=false when the signature is invalid', async () => {
describe('getOrdersAndTradersInfo', () => {
beforeEach(async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
signedOrder2 = await orderFactory.newSignedOrderAsync({
takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId2),
});
});
it('should return the correct info when no balances or allowances have been set and the signatures are invalid', async () => {
const invalidSignature = '0x01'; const invalidSignature = '0x01';
const orders = [signedOrder, signedOrder2]; const [, , isValidSignature] = await devUtils.getOrderRelevantState.callAsync(
const signatures = [invalidSignature, invalidSignature]; signedOrder,
const takers = [takerAddress, takerAddress]; invalidSignature,
const [ );
[orderInfo1, orderInfo2], expect(isValidSignature).to.equal(false);
[traderInfo1, traderInfo2],
[isValidSignature1, isValidSignature2],
] = await devUtils.getOrdersAndTradersInfo.callAsync(orders, signatures, takers);
const expectedOrderHash1 = orderHashUtils.getOrderHashHex(signedOrder);
const expectedOrderHash2 = orderHashUtils.getOrderHashHex(signedOrder2);
expect(orderInfo1.orderStatus).to.be.equal(OrderStatus.Fillable);
expect(orderInfo1.orderHash).to.be.equal(expectedOrderHash1);
expect(orderInfo1.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(orderInfo2.orderStatus).to.be.equal(OrderStatus.Fillable);
expect(orderInfo2.orderHash).to.be.equal(expectedOrderHash2);
expect(orderInfo2.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(isValidSignature1).to.equal(false);
expect(isValidSignature2).to.equal(false);
}); });
it('should return the correct info when balances and allowances are set and the signatures are valid', async () => { it('should return a fillableTakerAssetAmount of 0 when no balances or allowances are insufficient', async () => {
const makerBalance = new BigNumber(123); const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
const makerAllowance = new BigNumber(456); signedOrder,
const makerZrxBalance = new BigNumber(789); signedOrder.signature,
const takerZrxAllowance = new BigNumber(987); );
const txData = undefined; expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
await erc20Token.setBalance.awaitTransactionSuccessAsync( });
makerAddress, it('should return a fillableTakerAssetAmount of 0 when fee balances/allowances are insufficient', async () => {
makerBalance, await web3Wrapper.awaitTransactionSuccessAsync(
txData, await erc20Token.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerAssetAmount),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
await erc20Token.approve.awaitTransactionSuccessAsync( await web3Wrapper.awaitTransactionSuccessAsync(
erc20Proxy.address, await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
makerAllowance,
{
from: makerAddress, from: makerAddress,
}, }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
await zrxToken.setBalance.awaitTransactionSuccessAsync( const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
makerAddress, signedOrder,
makerZrxBalance, signedOrder.signature,
txData, );
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return the correct fillableTakerAssetAmount when fee balances/allowances are partially sufficient', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerAssetAmount),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
await zrxToken.approve.awaitTransactionSuccessAsync( await web3Wrapper.awaitTransactionSuccessAsync(
erc20Proxy.address, await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
takerZrxAllowance, from: makerAddress,
{ }),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const divisor = 4;
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(
makerAddress,
signedOrder.makerFee.dividedToIntegerBy(divisor),
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerFee, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
signedOrder,
signedOrder.signature,
);
expect(fillableTakerAssetAmount).to.bignumber.equal(
signedOrder.takerAssetAmount.dividedToIntegerBy(divisor),
);
});
it('should return the correct fillableTakerAssetAmount when non-fee balances/allowances are partially sufficient', async () => {
const divisor = 4;
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(
makerAddress,
signedOrder.makerAssetAmount.dividedToIntegerBy(divisor),
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerFee),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerFee, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
signedOrder,
signedOrder.signature,
);
expect(fillableTakerAssetAmount).to.bignumber.equal(
signedOrder.takerAssetAmount.dividedToIntegerBy(divisor),
);
});
it('should return a fillableTakerAssetAmount of 0 when non-fee balances/allowances are insufficient', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerFee),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerFee, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
signedOrder,
signedOrder.signature,
);
expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return a fillableTakerAssetAmount equal to the takerAssetAmount when the order is unfilled and balances/allowances are sufficient', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerAssetAmount),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerFee),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerFee, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
signedOrder,
signedOrder.signature,
);
expect(fillableTakerAssetAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
});
it('should return the correct fillableTakerAssetAmount when balances/allowances are partially sufficient and makerAsset=makerFeeAsset', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData: zrxAssetData,
makerAssetAmount: new BigNumber(10),
takerAssetAmount: new BigNumber(20),
makerFee: new BigNumber(40),
});
const transferableMakerAssetAmount = new BigNumber(10);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(makerAddress, transferableMakerAssetAmount),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, transferableMakerAssetAmount, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const expectedFillableTakerAssetAmount = transferableMakerAssetAmount
.times(signedOrder.takerAssetAmount)
.dividedToIntegerBy(signedOrder.makerAssetAmount.plus(signedOrder.makerFee));
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
signedOrder,
signedOrder.signature,
);
expect(fillableTakerAssetAmount).to.bignumber.equal(expectedFillableTakerAssetAmount);
});
it('should return the correct fillabeTakerassetAmount when makerAsset balances/allowances are sufficient and there are no maker fees', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({ makerFee: constants.ZERO_AMOUNT });
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerAssetAmount),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
signedOrder,
signedOrder.signature,
);
expect(fillableTakerAssetAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
});
it('should return a fillableTakerAssetAmount when the remaining takerAssetAmount is less than the transferable amount', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerAssetAmount),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerFee),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerFee, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token2.setBalance.sendTransactionAsync(takerAddress, signedOrder.takerAssetAmount),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token2.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.takerAssetAmount, {
from: takerAddress, from: takerAddress,
}, }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const isApproved = true; await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync( await zrxToken.setBalance.sendTransactionAsync(takerAddress, signedOrder.takerFee),
erc721Proxy.address, constants.AWAIT_TRANSACTION_MINED_MS,
isApproved, );
{ await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.takerFee, {
from: takerAddress, from: takerAddress,
}, }),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
await erc721Token.mint.awaitTransactionSuccessAsync( const takerAssetFillAmount = signedOrder.takerAssetAmount.dividedToIntegerBy(4);
takerAddress, await exchange.fillOrder.awaitTransactionSuccessAsync(
tokenId2, signedOrder,
{ takerAssetFillAmount,
from: takerAddress, signedOrder.signature,
}, { from: takerAddress },
);
const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync(
signedOrder,
signedOrder.signature,
);
expect(fillableTakerAssetAmount).to.bignumber.equal(
signedOrder.takerAssetAmount.minus(takerAssetFillAmount),
);
});
});
describe('getOrderRelevantStates', async () => {
it('should return the correct information for multiple orders', async () => {
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Token.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerAssetAmount),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const orders = [signedOrder, signedOrder2]; await web3Wrapper.awaitTransactionSuccessAsync(
const takers = [takerAddress, takerAddress]; await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerFee),
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerFee, {
from: makerAddress,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
const signedOrder2 = await orderFactory.newSignedOrderAsync({ makerAssetData: erc721AssetData });
const invalidSignature = '0x01';
await exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder2, { from: makerAddress });
const [ const [
[orderInfo1, orderInfo2], ordersInfo,
[traderInfo1, traderInfo2], fillableTakerAssetAmounts,
[isValidSignature1, isValidSignature2], isValidSignature,
] = await devUtils.getOrdersAndTradersInfo.callAsync(orders, orders.map(order => order.signature), takers); ] = await devUtils.getOrderRelevantStates.callAsync(
const expectedOrderHash1 = orderHashUtils.getOrderHashHex(signedOrder); [signedOrder, signedOrder2],
const expectedOrderHash2 = orderHashUtils.getOrderHashHex(signedOrder2); [signedOrder.signature, invalidSignature],
expect(orderInfo1.orderStatus).to.be.equal(OrderStatus.Fillable); );
expect(orderInfo1.orderHash).to.be.equal(expectedOrderHash1); expect(ordersInfo[0].orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder));
expect(orderInfo1.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); expect(ordersInfo[1].orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder2));
expect(orderInfo2.orderStatus).to.be.equal(OrderStatus.Fillable); expect(ordersInfo[0].orderStatus).to.equal(OrderStatus.Fillable);
expect(orderInfo2.orderHash).to.be.equal(expectedOrderHash2); expect(ordersInfo[1].orderStatus).to.equal(OrderStatus.Cancelled);
expect(orderInfo2.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT); expect(ordersInfo[0].orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerBalance).to.be.bignumber.equal(makerBalance); expect(ordersInfo[1].orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.makerAllowance).to.be.bignumber.equal(makerAllowance); expect(fillableTakerAssetAmounts[0]).to.bignumber.equal(signedOrder.takerAssetAmount);
expect(traderInfo1.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); expect(fillableTakerAssetAmounts[1]).to.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); expect(isValidSignature[0]).to.equal(true);
expect(traderInfo1.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance); expect(isValidSignature[1]).to.equal(false);
expect(traderInfo1.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo1.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
expect(traderInfo2.makerBalance).to.be.bignumber.equal(makerBalance);
expect(traderInfo2.makerAllowance).to.be.bignumber.equal(makerAllowance);
expect(traderInfo2.takerBalance).to.be.bignumber.equal(ERC721_BALANCE);
expect(traderInfo2.takerAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
expect(traderInfo2.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
expect(traderInfo2.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
expect(traderInfo2.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
expect(isValidSignature1).to.equal(true);
expect(isValidSignature2).to.equal(true);
}); });
}); });
}); });