From 613af6013a3bc19341f920c7c76f03d89c6d1590 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Jun 2019 10:42:46 -0700 Subject: [PATCH] Update OrderValidationUtils to return fillableTakerAssetAmount and refactor tests --- .../src/DevUtils/OrderValidationUtils.sol | 189 ++-- contracts/extensions/test/dev_utils.ts | 804 +++++++----------- 2 files changed, 417 insertions(+), 576 deletions(-) diff --git a/contracts/extensions/contracts/src/DevUtils/OrderValidationUtils.sol b/contracts/extensions/contracts/src/DevUtils/OrderValidationUtils.sol index 4678a266c3..8882b9f4f3 100644 --- a/contracts/extensions/contracts/src/DevUtils/OrderValidationUtils.sol +++ b/contracts/extensions/contracts/src/DevUtils/OrderValidationUtils.sol @@ -21,26 +21,17 @@ pragma experimental ABIEncoderV2; 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/LibMath.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetData.sol"; contract OrderValidationUtils is - LibAssetData + LibAssetData, + LibMath { 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 IExchange internal EXCHANGE; bytes internal ZRX_ASSET_DATA; @@ -58,116 +49,142 @@ contract OrderValidationUtils is /// @dev Fetches information for order and maker/taker of order. /// @param order The order structure. /// @param signature Proof that order has been created by maker. - /// @param takerAddress Address that will be filling the order. - /// @return OrderInfo, TraderInfo, and validity of signature for given order. - function getOrderAndTraderInfo( - LibOrder.Order memory order, - bytes memory signature, - address takerAddress - ) + /// @return OrderInfo, the remaining amount fillable by the taker, and validity of signature for given order. + function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature) public view returns ( LibOrder.OrderInfo memory orderInfo, - TraderInfo memory traderInfo, + uint256 fillableTakerAssetAmount, bool isValidSignature ) { + // Get info specific to 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( orderInfo.orderHash, - order.makerAddress, + makerAddress, 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. /// @param orders Array of order specifications. /// @param signatures Proofs that orders have been created by makers. - /// @param takerAddresses Array of taker addresses corresponding to each order. - /// @return Arrays of OrderInfo, TraderInfo, and validity of signatures that correspond to each order. - function getOrdersAndTradersInfo( - LibOrder.Order[] memory orders, - bytes[] memory signatures, - address[] memory takerAddresses - ) + /// @return Arrays of OrderInfo, fillable takerAssetAmounts, and validity of signatures that correspond to each order. + function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures) public view returns ( LibOrder.OrderInfo[] memory ordersInfo, - TraderInfo[] memory tradersInfo, + uint256[] memory fillableTakerAssetAmounts, bool[] memory isValidSignature ) { - ordersInfo = EXCHANGE.getOrdersInfo(orders); - tradersInfo = getTradersInfo(orders, takerAddresses); - uint256 length = orders.length; + ordersInfo = new LibOrder.OrderInfo[](length); + fillableTakerAssetAmounts = new uint256[](length); isValidSignature = new bool[](length); + for (uint256 i = 0; i != length; i++) { - isValidSignature[i] = EXCHANGE.isValidSignature( - ordersInfo[i].orderHash, - orders[i].makerAddress, + (ordersInfo[i], fillableTakerAssetAmounts[i], isValidSignature[i]) = getOrderRelevantState( + orders[i], signatures[i] ); } - return (ordersInfo, tradersInfo, isValidSignature); + return (ordersInfo, fillableTakerAssetAmounts, isValidSignature); } - /// @dev Fetches balance and allowances for maker and taker of order. - /// @param order The order structure. - /// @param takerAddress Address that will be filling the order. - /// @return Balances and allowances of maker and taker of order. - function getTraderInfo(LibOrder.Order memory order, address takerAddress) + /// @dev Gets the address of the AssetProxy that corresponds to the given assetData. + /// @param assetData Description of tokens, per the AssetProxy contract specification. + /// @return Address of the AssetProxy contract. + function getAssetProxyAddress(bytes memory assetData) public view - returns (TraderInfo memory traderInfo) + returns (address assetProxyAddress) { - bytes4 makerAssetProxyId = order.makerAssetData.readBytes4(0); - bytes4 takerAssetProxyId = order.takerAssetData.readBytes4(0); - - (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]); + if (assetData.equals(ZRX_ASSET_DATA)) { + return ERC20_PROXY_ADDRESS; } - 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; } } diff --git a/contracts/extensions/test/dev_utils.ts b/contracts/extensions/test/dev_utils.ts index 3d2624030f..555002d3f0 100644 --- a/contracts/extensions/test/dev_utils.ts +++ b/contracts/extensions/test/dev_utils.ts @@ -26,12 +26,15 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); describe('DevUtils', () => { let makerAddress: string; - let owner: string; let takerAddress: string; + let owner: string; let erc20AssetData: string; + let erc20AssetData2: string; let erc721AssetData: string; + let zrxAssetData: string; let erc20Token: DummyERC20TokenContract; + let erc20Token2: DummyERC20TokenContract; let zrxToken: DummyERC20TokenContract; let erc721Token: DummyERC721TokenContract; let exchange: ExchangeContract; @@ -40,12 +43,9 @@ describe('DevUtils', () => { let erc721Proxy: ERC721ProxyContract; let signedOrder: SignedOrder; - let signedOrder2: SignedOrder; let orderFactory: OrderFactory; const tokenId = new BigNumber(123456789); - const tokenId2 = new BigNumber(987654321); - const ERC721_BALANCE = new BigNumber(1); before(async () => { await blockchainLifecycle.startAsync(); @@ -61,8 +61,8 @@ describe('DevUtils', () => { const erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - const numDummyErc20ToDeploy = 2; - [erc20Token, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + const numDummyErc20ToDeploy = 3; + [erc20Token, zrxToken, erc20Token2] = await erc20Wrapper.deployDummyTokensAsync( numDummyErc20ToDeploy, constants.DUMMY_TOKEN_DECIMALS, ); @@ -71,7 +71,7 @@ describe('DevUtils', () => { [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); erc721Proxy = await erc721Wrapper.deployProxyAsync(); - const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); + zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); exchange = await ExchangeContract.deployFrom0xArtifactAsync( artifacts.Exchange, provider, @@ -81,6 +81,8 @@ describe('DevUtils', () => { const exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.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( artifacts.DevUtils, @@ -91,6 +93,7 @@ describe('DevUtils', () => { ); erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address); + erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20Token2.address); erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId); const defaultOrderParams = { ...constants.STATIC_ORDER_PARAMS, @@ -98,7 +101,7 @@ describe('DevUtils', () => { makerAddress, feeRecipientAddress: constants.NULL_ADDRESS, makerAssetData: erc20AssetData, - takerAssetData: erc721AssetData, + takerAssetData: erc20AssetData2, }; const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; orderFactory = new OrderFactory(privateKey, defaultOrderParams); @@ -111,133 +114,21 @@ describe('DevUtils', () => { await blockchainLifecycle.revertAsync(); }); - describe('getBalanceAndAllowance', () => { - describe('getERC721TokenOwner', async () => { - it('should return the null address when tokenId is not owned', async () => { - const tokenOwner = await devUtils.getERC721TokenOwner.callAsync(makerAddress, tokenId); - expect(tokenOwner).to.be.equal(constants.NULL_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('getAssetProxyAddress', () => { + it('should return the address of registered proxies', async () => { + const erc20ProxyAddress = await devUtils.getAssetProxyAddress.callAsync(erc20AssetData); + const erc721ProxyAddress = await devUtils.getAssetProxyAddress.callAsync(erc721AssetData); + expect(erc20ProxyAddress).to.equal(erc20Proxy.address); + expect(erc721ProxyAddress).to.equal(erc721Proxy.address); }); - describe('ERC20 assetData', () => { - it('should return the correct balances and allowances when both values are 0', async () => { - const [newBalance, newAllowance] = await devUtils.getBalanceAndAllowance.callAsync( - makerAddress, - 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)); - }); + it('should return the null address if the assetProxy does not exist', async () => { + const invalidAssetData = '0x01020304'; + const assetProxyAddress = await devUtils.getAssetProxyAddress.callAsync(invalidAssetData); + expect(assetProxyAddress).to.equal(constants.NULL_ADDRESS); }); }); - describe('getBatchBalancesAndAllowances', () => { - it('should return the correct balances and allowances when all values are 0', 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 () => { + describe('getTransferableAssetAmount', () => { + it('should return the balance when balance < allowance', async () => { const balance = new BigNumber(123); const allowance = new BigNumber(456); await web3Wrapper.awaitTransactionSuccessAsync( @@ -250,403 +141,336 @@ describe('DevUtils', () => { }), 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 erc721Token.mint.sendTransactionAsync(makerAddress, tokenId), + await erc20Token.setBalance.sendTransactionAsync(makerAddress, balance), constants.AWAIT_TRANSACTION_MINED_MS, ); - const isApproved = true; await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, { + await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, { from: makerAddress, }), constants.AWAIT_TRANSACTION_MINED_MS, ); - const [ - [erc20Balance, erc721Balance], - [erc20Allowance, erc721Allowance], - ] = await devUtils.getBatchBalancesAndAllowances.callAsync( + const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync( + erc20AssetData, makerAddress, - [erc20Proxy.address, erc721Proxy.address], - [erc20AssetData, erc721AssetData], ); - expect(erc20Balance).to.be.bignumber.equal(balance); - 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); + expect(transferableAmount).to.bignumber.equal(allowance); }); }); - describe('getTraderInfo', () => { + describe('getOrderRelevantState', () => { beforeEach(async () => { signedOrder = await orderFactory.newSignedOrderAsync(); }); - it('should return the correct info when no balances or allowances are set', async () => { - const traderInfo = await devUtils.getTraderInfo.callAsync(signedOrder, takerAddress); - 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); + it('should return the correct orderInfo when the order is valid', async () => { + const [orderInfo] = await devUtils.getOrderRelevantState.callAsync(signedOrder, signedOrder.signature); + expect(orderInfo.orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder)); + expect(orderInfo.orderStatus).to.equal(OrderStatus.Fillable); + expect(orderInfo.orderTakerAssetFilledAmount).to.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, - ); - 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( + it('should return isValidSignature=true when the signature is valid', async () => { + const [, , isValidSignature] = await devUtils.getOrderRelevantState.callAsync( signedOrder, 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); }); - }); - 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 () => { + it('should return isValidSignature=false when the signature is invalid', async () => { const invalidSignature = '0x01'; - const orders = [signedOrder, signedOrder2]; - const signatures = [invalidSignature, invalidSignature]; - const takers = [takerAddress, takerAddress]; - const [ - [orderInfo1, orderInfo2], - [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); + const [, , isValidSignature] = await devUtils.getOrderRelevantState.callAsync( + signedOrder, + invalidSignature, + ); + expect(isValidSignature).to.equal(false); }); - it('should return the correct info when balances and allowances are set and the signatures are 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, + it('should return a fillableTakerAssetAmount of 0 when no balances or allowances are insufficient', async () => { + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( + signedOrder, + signedOrder.signature, + ); + expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('should return a fillableTakerAssetAmount of 0 when fee balances/allowances are insufficient', async () => { + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Token.setBalance.sendTransactionAsync(makerAddress, signedOrder.makerAssetAmount), constants.AWAIT_TRANSACTION_MINED_MS, ); - await erc20Token.approve.awaitTransactionSuccessAsync( - erc20Proxy.address, - makerAllowance, - { + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { from: makerAddress, - }, + }), constants.AWAIT_TRANSACTION_MINED_MS, ); - await zrxToken.setBalance.awaitTransactionSuccessAsync( - makerAddress, - makerZrxBalance, - txData, + const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( + signedOrder, + signedOrder.signature, + ); + 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, ); - await zrxToken.approve.awaitTransactionSuccessAsync( - erc20Proxy.address, - takerZrxAllowance, - { + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + 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, - }, + }), constants.AWAIT_TRANSACTION_MINED_MS, ); - const isApproved = true; - await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync( - erc721Proxy.address, - isApproved, - { + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.setBalance.sendTransactionAsync(takerAddress, signedOrder.takerFee), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.takerFee, { from: takerAddress, - }, + }), constants.AWAIT_TRANSACTION_MINED_MS, ); - await erc721Token.mint.awaitTransactionSuccessAsync( - takerAddress, - tokenId2, - { - from: takerAddress, - }, + const takerAssetFillAmount = signedOrder.takerAssetAmount.dividedToIntegerBy(4); + await exchange.fillOrder.awaitTransactionSuccessAsync( + signedOrder, + takerAssetFillAmount, + 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, ); - const orders = [signedOrder, signedOrder2]; - const takers = [takerAddress, takerAddress]; + 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 signedOrder2 = await orderFactory.newSignedOrderAsync({ makerAssetData: erc721AssetData }); + const invalidSignature = '0x01'; + await exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder2, { from: makerAddress }); const [ - [orderInfo1, orderInfo2], - [traderInfo1, traderInfo2], - [isValidSignature1, isValidSignature2], - ] = await devUtils.getOrdersAndTradersInfo.callAsync(orders, orders.map(order => order.signature), 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(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); - expect(isValidSignature1).to.equal(true); - expect(isValidSignature2).to.equal(true); + ordersInfo, + fillableTakerAssetAmounts, + isValidSignature, + ] = await devUtils.getOrderRelevantStates.callAsync( + [signedOrder, signedOrder2], + [signedOrder.signature, invalidSignature], + ); + expect(ordersInfo[0].orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder)); + expect(ordersInfo[1].orderHash).to.equal(orderHashUtils.getOrderHashHex(signedOrder2)); + expect(ordersInfo[0].orderStatus).to.equal(OrderStatus.Fillable); + expect(ordersInfo[1].orderStatus).to.equal(OrderStatus.Cancelled); + expect(ordersInfo[0].orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT); + expect(ordersInfo[1].orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT); + expect(fillableTakerAssetAmounts[0]).to.bignumber.equal(signedOrder.takerAssetAmount); + expect(fillableTakerAssetAmounts[1]).to.bignumber.equal(constants.ZERO_AMOUNT); + expect(isValidSignature[0]).to.equal(true); + expect(isValidSignature[1]).to.equal(false); }); }); });