From 0db56a781e01143dca223704119f6acead3d2535 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Jun 2019 10:35:15 -0700 Subject: [PATCH 01/15] Cleanup LibAssetData tests --- contracts/asset-proxy/test/lib_asset_data.ts | 177 ++++++++----------- 1 file changed, 78 insertions(+), 99 deletions(-) diff --git a/contracts/asset-proxy/test/lib_asset_data.ts b/contracts/asset-proxy/test/lib_asset_data.ts index d744ba3769..29f18a9159 100644 --- a/contracts/asset-proxy/test/lib_asset_data.ts +++ b/contracts/asset-proxy/test/lib_asset_data.ts @@ -6,10 +6,9 @@ import { artifacts as erc1155Artifacts, ERC1155MintableContract, ERC1155TransferSingleEventArgs, - IERC1155MintableContract, } from '@0x/contracts-erc1155'; -import { artifacts as erc20Artifacts, DummyERC20TokenContract, IERC20TokenContract } from '@0x/contracts-erc20'; -import { artifacts as erc721Artifacts, DummyERC721TokenContract, IERC721TokenContract } from '@0x/contracts-erc721'; +import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; +import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721'; import { chaiSetup, constants, LogDecoder, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { AssetProxyId } from '@0x/types'; @@ -59,14 +58,15 @@ describe('LibAssetData', () => { let approvedSpenderAddress: string; let anotherApprovedSpenderAddress: string; - let erc20TokenAddress: string; + let erc20Token: DummyERC20TokenContract; + let erc721Token: DummyERC721TokenContract; + let erc1155Token: ERC1155MintableContract; + const erc20TokenTotalSupply = new BigNumber(1); - let erc721TokenAddress: string; const firstERC721TokenId = new BigNumber(1); const numberOfERC721Tokens = 10; - let erc1155MintableAddress: string; let erc1155TokenId: BigNumber; before(async () => { @@ -84,7 +84,7 @@ describe('LibAssetData', () => { anotherApprovedSpenderAddress, ] = await web3Wrapper.getAvailableAddressesAsync(); - erc20TokenAddress = (await DummyERC20TokenContract.deployFrom0xArtifactAsync( + erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync( erc20Artifacts.DummyERC20Token, provider, txDefaults, @@ -92,77 +92,47 @@ describe('LibAssetData', () => { 'DUM', new BigNumber(1), erc20TokenTotalSupply, - )).address; + ); - const erc721TokenContract = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( erc721Artifacts.DummyERC721Token, provider, txDefaults, 'Dummy', 'DUM', ); - erc721TokenAddress = erc721TokenContract.address; // mint `numberOfERC721Tokens` tokens const transactionMinedPromises = []; for (let i = 0; i < numberOfERC721Tokens; i++) { transactionMinedPromises.push( - web3Wrapper.awaitTransactionSuccessAsync( - await erc721TokenContract.mint.sendTransactionAsync( - tokenOwnerAddress, - firstERC721TokenId.plus(i - 1), - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ), + erc721Token.mint.awaitTransactionSuccessAsync(tokenOwnerAddress, firstERC721TokenId.plus(i - 1)), ); } await Promise.all(transactionMinedPromises); - const erc1155MintableContract = await ERC1155MintableContract.deployFrom0xArtifactAsync( + erc1155Token = await ERC1155MintableContract.deployFrom0xArtifactAsync( erc1155Artifacts.ERC1155Mintable, provider, txDefaults, ); - erc1155MintableAddress = erc1155MintableContract.address; // Somewhat re-inventing the wheel here, but the prior art currently // exists only as an unexported test util in the erc1155 package // (Erc1155Wrapper.mintFungibleTokensAsync() in erc1155/test/utils/). // This is concise enough to justify duplication, but it sure is ugly. // tslint:disable-next-line no-unnecessary-type-assertion - erc1155TokenId = ((await new LogDecoder(web3Wrapper, erc1155Artifacts).getTxWithDecodedLogsAsync( - await erc1155MintableContract.create.sendTransactionAsync('uri:Dummy', /*isNonFungible:*/ false), - )).logs[0] as LogWithDecodedArgs).args.id; - await web3Wrapper.awaitTransactionSuccessAsync( - await erc1155MintableContract.mintFungible.sendTransactionAsync( - erc1155TokenId, - [tokenOwnerAddress], - [new BigNumber(1)], - ), - constants.AWAIT_TRANSACTION_MINED_MS, + const logDecoder = new LogDecoder(web3Wrapper, erc1155Artifacts); + const transactionReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await erc1155Token.create.sendTransactionAsync('uri:Dummy', /*isNonFungible:*/ false), + ); + + erc1155TokenId = (transactionReceipt.logs[0] as LogWithDecodedArgs).args.id; + await erc1155Token.mintFungible.awaitTransactionSuccessAsync( + erc1155TokenId, + [tokenOwnerAddress], + [new BigNumber(1)], ); }); - async function setERC20AllowanceAsync(): Promise { - return web3Wrapper.awaitTransactionSuccessAsync( - await new IERC20TokenContract( - erc20Artifacts.IERC20Token.compilerOutput.abi, - erc20TokenAddress, - provider, - ).approve.sendTransactionAsync(approvedSpenderAddress, new BigNumber(1), { from: tokenOwnerAddress }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - - async function setERC721AllowanceAsync(): Promise { - return web3Wrapper.awaitTransactionSuccessAsync( - await new IERC721TokenContract( - erc721Artifacts.IERC721Token.compilerOutput.abi, - erc721TokenAddress, - provider, - ).approve.sendTransactionAsync(approvedSpenderAddress, new BigNumber(1), { from: tokenOwnerAddress }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - } - after(async () => { await blockchainLifecycle.revertAsync(); }); @@ -243,7 +213,7 @@ describe('LibAssetData', () => { expect( await libAssetData.getBalance.callAsync( tokenOwnerAddress, - await libAssetData.encodeERC20AssetData.callAsync(erc20TokenAddress), + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), ), ).to.bignumber.equal(erc20TokenTotalSupply); }); @@ -252,7 +222,7 @@ describe('LibAssetData', () => { expect( await libAssetData.getBalance.callAsync( tokenOwnerAddress, - await libAssetData.encodeERC721AssetData.callAsync(erc721TokenAddress, firstERC721TokenId), + await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ), ).to.bignumber.equal(1); }); @@ -262,7 +232,7 @@ describe('LibAssetData', () => { await libAssetData.getBalance.callAsync( tokenOwnerAddress, await libAssetData.encodeERC1155AssetData.callAsync( - erc1155MintableAddress, + erc1155Token.address, [erc1155TokenId], [new BigNumber(1)], // token values '0x', // callback data @@ -278,8 +248,8 @@ describe('LibAssetData', () => { await libAssetData.encodeMultiAssetData.callAsync( [new BigNumber(1), new BigNumber(1)], [ - await libAssetData.encodeERC20AssetData.callAsync(erc20TokenAddress), - await libAssetData.encodeERC721AssetData.callAsync(erc721TokenAddress, firstERC721TokenId), + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), + await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ], ), ), @@ -287,62 +257,55 @@ describe('LibAssetData', () => { }); it('should query ERC20 allowances by asset data', async () => { - await setERC20AllowanceAsync(); + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + from: tokenOwnerAddress, + }); expect( await libAssetData.getAllowance.callAsync( tokenOwnerAddress, approvedSpenderAddress, - await libAssetData.encodeERC20AssetData.callAsync(erc20TokenAddress), + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), ), - ).to.bignumber.equal(1); + ).to.bignumber.equal(allowance); }); it('should query ERC721 approval by asset data', async () => { - await setERC721AllowanceAsync(); + await erc721Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, firstERC721TokenId, { + from: tokenOwnerAddress, + }); expect( await libAssetData.getAllowance.callAsync( tokenOwnerAddress, approvedSpenderAddress, - await libAssetData.encodeERC721AssetData.callAsync(erc721TokenAddress, firstERC721TokenId), + await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ), ).to.bignumber.equal(1); }); it('should query ERC721 approvalForAll by assetData', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await new IERC721TokenContract( - erc721Artifacts.IERC721Token.compilerOutput.abi, - erc721TokenAddress, - provider, - ).setApprovalForAll.sendTransactionAsync(anotherApprovedSpenderAddress, true, { - from: tokenOwnerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync(anotherApprovedSpenderAddress, true, { + from: tokenOwnerAddress, + }); expect( await libAssetData.getAllowance.callAsync( tokenOwnerAddress, anotherApprovedSpenderAddress, - await libAssetData.encodeERC721AssetData.callAsync(erc721TokenAddress, firstERC721TokenId), + await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ), ).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); }); it('should query ERC1155 allowances by asset data', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await new IERC1155MintableContract( - erc1155Artifacts.IERC1155Mintable.compilerOutput.abi, - erc1155MintableAddress, - provider, - ).setApprovalForAll.sendTransactionAsync(approvedSpenderAddress, true, { from: tokenOwnerAddress }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await erc1155Token.setApprovalForAll.awaitTransactionSuccessAsync(approvedSpenderAddress, true, { + from: tokenOwnerAddress, + }); expect( await libAssetData.getAllowance.callAsync( tokenOwnerAddress, approvedSpenderAddress, await libAssetData.encodeERC1155AssetData.callAsync( - erc1155MintableAddress, + erc1155Token.address, [erc1155TokenId], [new BigNumber(1)], '0x', @@ -352,8 +315,13 @@ describe('LibAssetData', () => { }); it('should query multi-asset allowances by asset data', async () => { - await setERC20AllowanceAsync(); - await setERC721AllowanceAsync(); + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + from: tokenOwnerAddress, + }); + await erc721Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, firstERC721TokenId, { + from: tokenOwnerAddress, + }); expect( await libAssetData.getAllowance.callAsync( tokenOwnerAddress, @@ -361,8 +329,8 @@ describe('LibAssetData', () => { await libAssetData.encodeMultiAssetData.callAsync( [new BigNumber(1), new BigNumber(1)], [ - await libAssetData.encodeERC20AssetData.callAsync(erc20TokenAddress), - await libAssetData.encodeERC721AssetData.callAsync(erc721TokenAddress, firstERC721TokenId), + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), + await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ], ), ), @@ -373,22 +341,27 @@ describe('LibAssetData', () => { it('should query balances for a batch of asset data strings', async () => { expect( await libAssetData.getBatchBalances.callAsync(tokenOwnerAddress, [ - await libAssetData.encodeERC20AssetData.callAsync(erc20TokenAddress), - await libAssetData.encodeERC721AssetData.callAsync(erc721TokenAddress, firstERC721TokenId), + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), + await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ]), ).to.deep.equal([new BigNumber(erc20TokenTotalSupply), new BigNumber(1)]); }); it('should query allowances for a batch of asset data strings', async () => { - await setERC20AllowanceAsync(); - await setERC721AllowanceAsync(); + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + from: tokenOwnerAddress, + }); + await erc721Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, firstERC721TokenId, { + from: tokenOwnerAddress, + }); expect( await libAssetData.getBatchAllowances.callAsync( tokenOwnerAddress, [approvedSpenderAddress, approvedSpenderAddress], [ - await libAssetData.encodeERC20AssetData.callAsync(erc20TokenAddress), - await libAssetData.encodeERC721AssetData.callAsync(erc721TokenAddress, firstERC721TokenId), + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), + await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ], ), ).to.deep.equal([new BigNumber(1), new BigNumber(1)]); @@ -398,35 +371,41 @@ describe('LibAssetData', () => { it('should return the null address when tokenId is not owned', async () => { const nonexistentTokenId = new BigNumber(1234567890); expect( - await libAssetData.getERC721TokenOwner.callAsync(erc721TokenAddress, nonexistentTokenId), + await libAssetData.getERC721TokenOwner.callAsync(erc721Token.address, nonexistentTokenId), ).to.be.equal(constants.NULL_ADDRESS); }); it('should return the owner address when tokenId is owned', async () => { expect( - await libAssetData.getERC721TokenOwner.callAsync(erc721TokenAddress, firstERC721TokenId), + await libAssetData.getERC721TokenOwner.callAsync(erc721Token.address, firstERC721TokenId), ).to.be.equal(tokenOwnerAddress); }); }); it('should query balance and allowance together, from asset data', async () => { - await setERC20AllowanceAsync(); + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + from: tokenOwnerAddress, + }); expect( await libAssetData.getBalanceAndAllowance.callAsync( tokenOwnerAddress, approvedSpenderAddress, - await libAssetData.encodeERC20AssetData.callAsync(erc20TokenAddress), + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), ), - ).to.deep.equal([new BigNumber(erc20TokenTotalSupply), new BigNumber(1)]); + ).to.deep.equal([new BigNumber(erc20TokenTotalSupply), allowance]); }); it('should query balances and allowances together, from an asset data array', async () => { - await setERC20AllowanceAsync(); + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + from: tokenOwnerAddress, + }); expect( await libAssetData.getBatchBalancesAndAllowances.callAsync( tokenOwnerAddress, [approvedSpenderAddress, approvedSpenderAddress], - [await libAssetData.encodeERC20AssetData.callAsync(erc20TokenAddress)], + [await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address)], ), - ).to.deep.equal([[new BigNumber(erc20TokenTotalSupply)], [new BigNumber(1)]]); + ).to.deep.equal([[new BigNumber(erc20TokenTotalSupply)], [allowance]]); }); }); From 613af6013a3bc19341f920c7c76f03d89c6d1590 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Jun 2019 10:42:46 -0700 Subject: [PATCH 02/15] 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); }); }); }); From 6b7cb13e9a416360975149cc9fc73e4d3591a8dc Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Jun 2019 15:49:33 -0700 Subject: [PATCH 03/15] Move DevUtils contract into its own package --- .circleci/config.yml | 2 + .gitignore | 2 + .prettierignore | 2 + README.md | 1 + contracts/dev-utils/CHANGELOG.json | 11 + contracts/dev-utils/CHANGELOG.md | 99 +++++ contracts/dev-utils/DEPLOYS.json | 1 + contracts/dev-utils/README.md | 73 ++++ contracts/dev-utils/compiler.json | 26 ++ .../contracts/src}/DevUtils.sol | 0 .../contracts/src}/OrderValidationUtils.sol | 0 contracts/dev-utils/package.json | 89 +++++ contracts/dev-utils/src/artifacts.ts | 9 + contracts/dev-utils/src/index.ts | 2 + contracts/dev-utils/src/wrappers.ts | 6 + .../test/dev_utils.ts | 4 +- contracts/dev-utils/test/global_hooks.ts | 19 + contracts/dev-utils/tsconfig.json | 7 + contracts/dev-utils/tslint.json | 6 + contracts/extensions/compiler.json | 1 - contracts/extensions/package.json | 2 +- contracts/extensions/src/artifacts.ts | 2 - contracts/extensions/src/wrappers.ts | 1 - contracts/extensions/tsconfig.json | 1 - package.json | 2 +- .../src/generated-wrappers/coordinator.ts | 368 +++++++++--------- tsconfig.json | 1 + 27 files changed, 544 insertions(+), 193 deletions(-) create mode 100644 contracts/dev-utils/CHANGELOG.json create mode 100644 contracts/dev-utils/CHANGELOG.md create mode 100644 contracts/dev-utils/DEPLOYS.json create mode 100644 contracts/dev-utils/README.md create mode 100644 contracts/dev-utils/compiler.json rename contracts/{extensions/contracts/src/DevUtils => dev-utils/contracts/src}/DevUtils.sol (100%) rename contracts/{extensions/contracts/src/DevUtils => dev-utils/contracts/src}/OrderValidationUtils.sol (100%) create mode 100644 contracts/dev-utils/package.json create mode 100644 contracts/dev-utils/src/artifacts.ts create mode 100644 contracts/dev-utils/src/index.ts create mode 100644 contracts/dev-utils/src/wrappers.ts rename contracts/{extensions => dev-utils}/test/dev_utils.ts (99%) create mode 100644 contracts/dev-utils/test/global_hooks.ts create mode 100644 contracts/dev-utils/tsconfig.json create mode 100644 contracts/dev-utils/tslint.json diff --git a/.circleci/config.yml b/.circleci/config.yml index 58e16383f8..9c1046dc0b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -58,6 +58,7 @@ jobs: - run: yarn wsrun test:circleci @0x/contracts-exchange - run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder - run: yarn wsrun test:circleci @0x/contracts-coordinator + - run: yarn wsrun test:circleci @0x/contracts-dev-utils test-contracts-geth: docker: - image: circleci/node:9-browsers @@ -80,6 +81,7 @@ jobs: - run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange - run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-exchange-forwarder - run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-coordinator + - run: TEST_PROVIDER=geth yarn wsrun test:circleci @0x/contracts-dev-utils test-publish: resource_class: medium+ docker: diff --git a/.gitignore b/.gitignore index 203d8ad811..78d357c298 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ contracts/erc721/generated-artifacts/ contracts/erc1155/generated-artifacts/ contracts/extensions/generated-artifacts/ contracts/exchange-forwarder/generated-artifacts/ +contracts/dev-utils/generated-artifacts/ packages/sol-tracing-utils/test/fixtures/artifacts/ packages/metacoin/artifacts/ @@ -110,6 +111,7 @@ contracts/erc721/generated-wrappers/ contracts/erc1155/generated-wrappers/ contracts/extensions/generated-wrappers/ contracts/exchange-forwarder/generated-wrappers/ +contracts/dev-utils/generated-wrappers/ packages/metacoin/src/contract_wrappers # solc-bin in sol-compiler diff --git a/.prettierignore b/.prettierignore index 6614aec2a7..2c92af5de2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -22,6 +22,8 @@ lib /contracts/extensions/generated-artifacts /contracts/exchange-forwarder/generated-wrappers /contracts/exchange-forwarder/generated-artifacts +/contracts/dev-utils/generated-wrappers +/contracts/dev-utils/generated-artifacts /packages/abi-gen-wrappers/src/generated-wrappers /packages/contract-artifacts/artifacts /python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts diff --git a/README.md b/README.md index f6299bf43e..5c080b63c2 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ These packages are all under development. See [/contracts/README.md](/contracts/ | [`@0x/contracts-test-utils`](/contracts/test-utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-test-utils.svg)](https://www.npmjs.com/package/@0x/contracts-test-utils) | Typescript/Javascript shared utilities used for testing contracts | | [`@0x/contracts-utils`](/contracts/utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-utils.svg)](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts | | [`@0x/contracts-coordinator`](/contracts/coordinator) | [![npm](https://img.shields.io/npm/v/@0x/contracts-coordinator.svg)](https://www.npmjs.com/package/@0x/contracts-coordinator) | A contract that allows users to execute 0x transactions with permission from a Coordinator | +| [`@0x/contracts-dev-utils`](/contracts/dev-utils) | [![npm](https://img.shields.io/npm/v/@0x/contracts-dev-utils.svg)](https://www.npmjs.com/package/@0x/contracts-dev-utils) | A contract contains utility functions for developers (such as validating many orders using a single eth_call) | ### Typescript/Javascript Packages diff --git a/contracts/dev-utils/CHANGELOG.json b/contracts/dev-utils/CHANGELOG.json new file mode 100644 index 0000000000..a3ea7fc26f --- /dev/null +++ b/contracts/dev-utils/CHANGELOG.json @@ -0,0 +1,11 @@ +[ + { + "version": "0.0.1", + "changes": [ + { + "note": "Create dev-utils package", + "pr": TODO + } + ] + } +] diff --git a/contracts/dev-utils/CHANGELOG.md b/contracts/dev-utils/CHANGELOG.md new file mode 100644 index 0000000000..2eb59be5c7 --- /dev/null +++ b/contracts/dev-utils/CHANGELOG.md @@ -0,0 +1,99 @@ + + +CHANGELOG + +## v3.1.5 - _May 24, 2019_ + + * Dependencies updated + +## v3.1.4 - _May 15, 2019_ + + * Dependencies updated + +## v3.1.3 - _May 14, 2019_ + + * Dependencies updated + +## v3.1.2 - _May 10, 2019_ + + * Dependencies updated + +## v3.1.1 - _April 11, 2019_ + + * Dependencies updated + +## v3.1.0 - _March 21, 2019_ + + * Run Web3ProviderEngine without excess block polling (#1695) + +## v3.0.0 - _March 20, 2019_ + + * Do not reexport external dependencies (#1682) + * Upgrade contracts to Solidity 0.5.5 (#1682) + +## v2.0.8 - _March 1, 2019_ + + * Dependencies updated + +## v2.0.7 - _February 27, 2019_ + + * Dependencies updated + +## v2.0.6 - _February 26, 2019_ + + * Dependencies updated + +## v2.0.5 - _February 25, 2019_ + + * Dependencies updated + +## v2.0.4 - _February 9, 2019_ + + * Dependencies updated + +## v2.0.3 - _February 7, 2019_ + + * Dependencies updated + +## v2.0.2 - _February 7, 2019_ + + * Fake publish to enable pinning + +## v2.0.1 - _February 6, 2019_ + + * Dependencies updated + +## v2.0.0 - _February 5, 2019_ + + * Upgrade the bignumber.js to v8.0.2 (#1517) + * Move Forwarder contract into new package (#1539) + * Nest extensions under src directory (#1539) + +## v1.2.3 - _January 17, 2019_ + + * Dependencies updated + +## v1.2.2 - _January 15, 2019_ + + * Dependencies updated + +## v1.2.1 - _January 11, 2019_ + + * Dependencies updated + +## v1.2.0 - _January 9, 2019_ + + * Added Dutch Auction Wrapper (#1465) + +## v1.1.0 - _Invalid date_ + + * Added Balance Threshold Filter (#1383) + * Add OrderMatcher (#1117) + * Add OrderValidator (#1464) + +## v1.0.2 - _December 13, 2018_ + + * Dependencies updated diff --git a/contracts/dev-utils/DEPLOYS.json b/contracts/dev-utils/DEPLOYS.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/contracts/dev-utils/DEPLOYS.json @@ -0,0 +1 @@ +[] diff --git a/contracts/dev-utils/README.md b/contracts/dev-utils/README.md new file mode 100644 index 0000000000..8c62bce579 --- /dev/null +++ b/contracts/dev-utils/README.md @@ -0,0 +1,73 @@ +## Dev-Utils + +This package implements various utilities for developers. For example, the `DevUtils` contract can query batches of balances or allowances given some `assetData`, can validate batches of orders, and can decode 0x-specific calldata. Addresses of the deployed contracts can be found in the 0x [wiki](https://0xproject.com/wiki#Deployed-Addresses) or the [DEPLOYS](./DEPLOYS.json) file within this package. + +## Installation + +**Install** + +```bash +npm install @0x/contracts-dev-utils --save +``` + +## Bug bounty + +A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0xproject.com/wiki#Bug-Bounty). + +## Contributing + +We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. + +For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install Dependencies + +If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: + +```bash +yarn config set workspaces-experimental true +``` + +Then install dependencies + +```bash +yarn install +``` + +### Build + +To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: + +```bash +PKG=@0x/contracts-extensions yarn build +``` + +Or continuously rebuild on change: + +```bash +PKG=@0x/contracts-extensions yarn watch +``` + +### Clean + +```bash +yarn clean +``` + +### Lint + +```bash +yarn lint +``` + +### Run Tests + +```bash +yarn test +``` + +#### Testing options + +Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md). diff --git a/contracts/dev-utils/compiler.json b/contracts/dev-utils/compiler.json new file mode 100644 index 0000000000..2157d0a193 --- /dev/null +++ b/contracts/dev-utils/compiler.json @@ -0,0 +1,26 @@ +{ + "artifactsDir": "./generated-artifacts", + "contractsDir": "./contracts", + "useDockerisedSolc": false, + "isOfflineMode": false, + "compilerSettings": { + "evmVersion": "constantinople", + "optimizer": { + "enabled": true, + "runs": 1000000, + "details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true } + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap" + ] + } + } + }, + "contracts": ["src/DevUtils.sol"] +} diff --git a/contracts/extensions/contracts/src/DevUtils/DevUtils.sol b/contracts/dev-utils/contracts/src/DevUtils.sol similarity index 100% rename from contracts/extensions/contracts/src/DevUtils/DevUtils.sol rename to contracts/dev-utils/contracts/src/DevUtils.sol diff --git a/contracts/extensions/contracts/src/DevUtils/OrderValidationUtils.sol b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol similarity index 100% rename from contracts/extensions/contracts/src/DevUtils/OrderValidationUtils.sol rename to contracts/dev-utils/contracts/src/OrderValidationUtils.sol diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json new file mode 100644 index 0000000000..d12ddb5281 --- /dev/null +++ b/contracts/dev-utils/package.json @@ -0,0 +1,89 @@ +{ + "name": "@0x/contracts-dev-utils", + "version": "0.0.1", + "engines": { + "node": ">=6.12" + }, + "description": "Smart contract extensions of 0x protocol", + "main": "lib/src/index.js", + "directories": { + "test": "test" + }, + "scripts": { + "build": "yarn pre_build && tsc -b", + "build:ci": "yarn build", + "pre_build": "run-s compile generate_contract_wrappers", + "test": "yarn run_mocha", + "rebuild_and_test": "run-s build test", + "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", + "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", + "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", + "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", + "compile": "sol-compiler", + "watch": "sol-compiler -w", + "clean": "shx rm -rf lib generated-artifacts generated-wrappers", + "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", + "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", + "fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", + "coverage:report:text": "istanbul report text", + "coverage:report:html": "istanbul report html && open coverage/index.html", + "profiler:report:html": "istanbul report html && open coverage/index.html", + "coverage:report:lcov": "istanbul report lcov", + "test:circleci": "yarn test", + "contracts:gen": "contracts-gen", + "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" + }, + "config": { + "abis": "./generated-artifacts/@(DevUtils).json", + "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." + }, + "repository": { + "type": "git", + "url": "https://github.com/0xProject/0x-monorepo.git" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/0xProject/0x-monorepo/issues" + }, + "homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md", + "devDependencies": { + "@0x/abi-gen": "^2.0.10", + "@0x/contract-wrappers": "^9.1.4", + "@0x/contracts-gen": "^1.0.9", + "@0x/contracts-test-utils": "^3.1.7", + "@0x/dev-utils": "^2.2.3", + "@0x/sol-compiler": "^3.1.8", + "@0x/tslint-config": "^3.0.1", + "@types/lodash": "4.14.104", + "@types/node": "*", + "chai": "^4.0.1", + "chai-as-promised": "^7.1.0", + "chai-bignumber": "^3.0.0", + "dirty-chai": "^2.0.1", + "make-promises-safe": "^1.1.0", + "mocha": "^4.1.0", + "npm-run-all": "^4.1.2", + "shx": "^0.2.2", + "solhint": "^1.4.1", + "tslint": "5.11.0", + "typescript": "3.0.1" + }, + "dependencies": { + "@0x/base-contract": "^5.1.0", + "@0x/contracts-asset-proxy": "^2.1.5", + "@0x/contracts-erc20": "^2.2.5", + "@0x/contracts-erc721": "^2.1.6", + "@0x/contracts-exchange": "^2.1.5", + "@0x/contracts-exchange-libs": "^2.1.6", + "@0x/contracts-utils": "^3.1.6", + "@0x/order-utils": "^8.1.1", + "@0x/types": "^2.2.2", + "@0x/typescript-typings": "^4.2.2", + "@0x/utils": "^4.3.3", + "@0x/web3-wrapper": "^6.0.6", + "ethereum-types": "^2.1.2" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/contracts/dev-utils/src/artifacts.ts b/contracts/dev-utils/src/artifacts.ts new file mode 100644 index 0000000000..148184dd70 --- /dev/null +++ b/contracts/dev-utils/src/artifacts.ts @@ -0,0 +1,9 @@ +/* + * ----------------------------------------------------------------------------- + * Warning: This file is auto-generated by contracts-gen. Don't edit manually. + * ----------------------------------------------------------------------------- + */ +import { ContractArtifact } from 'ethereum-types'; + +import * as DevUtils from '../generated-artifacts/DevUtils.json'; +export const artifacts = { DevUtils: DevUtils as ContractArtifact }; diff --git a/contracts/dev-utils/src/index.ts b/contracts/dev-utils/src/index.ts new file mode 100644 index 0000000000..d55f08ea2d --- /dev/null +++ b/contracts/dev-utils/src/index.ts @@ -0,0 +1,2 @@ +export * from './artifacts'; +export * from './wrappers'; diff --git a/contracts/dev-utils/src/wrappers.ts b/contracts/dev-utils/src/wrappers.ts new file mode 100644 index 0000000000..ca8ce678dc --- /dev/null +++ b/contracts/dev-utils/src/wrappers.ts @@ -0,0 +1,6 @@ +/* + * ----------------------------------------------------------------------------- + * Warning: This file is auto-generated by contracts-gen. Don't edit manually. + * ----------------------------------------------------------------------------- + */ +export * from '../generated-wrappers/dev_utils'; diff --git a/contracts/extensions/test/dev_utils.ts b/contracts/dev-utils/test/dev_utils.ts similarity index 99% rename from contracts/extensions/test/dev_utils.ts rename to contracts/dev-utils/test/dev_utils.ts index 555002d3f0..7d8ac0cfe0 100644 --- a/contracts/extensions/test/dev_utils.ts +++ b/contracts/dev-utils/test/dev_utils.ts @@ -1,7 +1,7 @@ import { ERC20ProxyContract, ERC20Wrapper, ERC721ProxyContract, ERC721Wrapper } from '@0x/contracts-asset-proxy'; import { DummyERC20TokenContract } from '@0x/contracts-erc20'; import { DummyERC721TokenContract } from '@0x/contracts-erc721'; -import { ExchangeContract, ExchangeWrapper } from '@0x/contracts-exchange'; +import { artifacts as exchangeArtifacts, ExchangeContract, ExchangeWrapper } from '@0x/contracts-exchange'; import { chaiSetup, constants, @@ -73,7 +73,7 @@ describe('DevUtils', () => { zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, + exchangeArtifacts.Exchange, provider, txDefaults, zrxAssetData, diff --git a/contracts/dev-utils/test/global_hooks.ts b/contracts/dev-utils/test/global_hooks.ts new file mode 100644 index 0000000000..2ca47d433b --- /dev/null +++ b/contracts/dev-utils/test/global_hooks.ts @@ -0,0 +1,19 @@ +import { env, EnvVars } from '@0x/dev-utils'; + +import { coverage, profiler, provider } from '@0x/contracts-test-utils'; +import { providerUtils } from '@0x/utils'; + +before('start web3 provider', () => { + providerUtils.startProviderEngine(provider); +}); +after('generate coverage report', async () => { + if (env.parseBoolean(EnvVars.SolidityCoverage)) { + const coverageSubprovider = coverage.getCoverageSubproviderSingleton(); + await coverageSubprovider.writeCoverageAsync(); + } + if (env.parseBoolean(EnvVars.SolidityProfiler)) { + const profilerSubprovider = profiler.getProfilerSubproviderSingleton(); + await profilerSubprovider.writeProfilerOutputAsync(); + } + provider.stop(); +}); diff --git a/contracts/dev-utils/tsconfig.json b/contracts/dev-utils/tsconfig.json new file mode 100644 index 0000000000..b428ecd4d9 --- /dev/null +++ b/contracts/dev-utils/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, + "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], + "files": ["generated-artifacts/DevUtils.json"], + "exclude": ["./deploy/solc/solc_bin"] +} diff --git a/contracts/dev-utils/tslint.json b/contracts/dev-utils/tslint.json new file mode 100644 index 0000000000..1bb3ac2a22 --- /dev/null +++ b/contracts/dev-utils/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": ["@0x/tslint-config"], + "rules": { + "custom-no-magic-numbers": false + } +} diff --git a/contracts/extensions/compiler.json b/contracts/extensions/compiler.json index 7d513dc38e..a58e45e4fe 100644 --- a/contracts/extensions/compiler.json +++ b/contracts/extensions/compiler.json @@ -27,7 +27,6 @@ "@0x/contracts-exchange/contracts/examples/ExchangeWrapper.sol", "@0x/contracts-exchange/contracts/src/Exchange.sol", "src/BalanceThresholdFilter/BalanceThresholdFilter.sol", - "src/DevUtils/DevUtils.sol", "src/DutchAuction/DutchAuction.sol", "src/OrderMatcher/OrderMatcher.sol" ] diff --git a/contracts/extensions/package.json b/contracts/extensions/package.json index eb89070979..6aa7f2a705 100644 --- a/contracts/extensions/package.json +++ b/contracts/extensions/package.json @@ -34,7 +34,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "./generated-artifacts/@(BalanceThresholdFilter|DevUtils|DutchAuction|Exchange|ExchangeWrapper|OrderMatcher|WETH9).json", + "abis": "./generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Exchange|ExchangeWrapper|OrderMatcher|WETH9).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/extensions/src/artifacts.ts b/contracts/extensions/src/artifacts.ts index 49a56d9e48..2a286d5e1d 100644 --- a/contracts/extensions/src/artifacts.ts +++ b/contracts/extensions/src/artifacts.ts @@ -6,7 +6,6 @@ import { ContractArtifact } from 'ethereum-types'; import * as BalanceThresholdFilter from '../generated-artifacts/BalanceThresholdFilter.json'; -import * as DevUtils from '../generated-artifacts/DevUtils.json'; import * as DutchAuction from '../generated-artifacts/DutchAuction.json'; import * as Exchange from '../generated-artifacts/Exchange.json'; import * as ExchangeWrapper from '../generated-artifacts/ExchangeWrapper.json'; @@ -19,5 +18,4 @@ export const artifacts = { BalanceThresholdFilter: BalanceThresholdFilter as ContractArtifact, DutchAuction: DutchAuction as ContractArtifact, OrderMatcher: OrderMatcher as ContractArtifact, - DevUtils: DevUtils as ContractArtifact, }; diff --git a/contracts/extensions/src/wrappers.ts b/contracts/extensions/src/wrappers.ts index 9a227280ee..26ae645ceb 100644 --- a/contracts/extensions/src/wrappers.ts +++ b/contracts/extensions/src/wrappers.ts @@ -4,7 +4,6 @@ * ----------------------------------------------------------------------------- */ export * from '../generated-wrappers/balance_threshold_filter'; -export * from '../generated-wrappers/dev_utils'; export * from '../generated-wrappers/dutch_auction'; export * from '../generated-wrappers/exchange'; export * from '../generated-wrappers/exchange_wrapper'; diff --git a/contracts/extensions/tsconfig.json b/contracts/extensions/tsconfig.json index 1246d38e1d..73784c22b3 100644 --- a/contracts/extensions/tsconfig.json +++ b/contracts/extensions/tsconfig.json @@ -4,7 +4,6 @@ "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "files": [ "generated-artifacts/BalanceThresholdFilter.json", - "generated-artifacts/DevUtils.json", "generated-artifacts/DutchAuction.json", "generated-artifacts/Exchange.json", "generated-artifacts/ExchangeWrapper.json", diff --git a/package.json b/package.json index a14f286adf..edf1b474fb 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "lint": "wsrun lint $PKG --fast-exit --parallel --exclude-missing" }, "config": { - "contractsPackages": "@0x/contracts-asset-proxy @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-extensions @0x/contracts-multisig @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-coordinator", + "contractsPackages": "@0x/contracts-asset-proxy @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-extensions @0x/contracts-multisig @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-coordinator @0x/contracts-dev-utils", "mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic", "packagesWithDocPages": "0x.js connect json-schemas subproviders web3-wrapper contract-wrappers order-utils order-watcher sol-compiler sol-coverage sol-profiler sol-trace ethereum-types asset-buyer migrations", "ignoreDependencyVersions": "@types/styled-components @types/node", diff --git a/packages/abi-gen-wrappers/src/generated-wrappers/coordinator.ts b/packages/abi-gen-wrappers/src/generated-wrappers/coordinator.ts index c45701b4a9..eba1e902d9 100644 --- a/packages/abi-gen-wrappers/src/generated-wrappers/coordinator.ts +++ b/packages/abi-gen-wrappers/src/generated-wrappers/coordinator.ts @@ -23,7 +23,6 @@ import { assert } from '@0x/assert'; import * as ethers from 'ethers'; // tslint:enable:no-unused-variable - /* istanbul ignore next */ // tslint:disable:no-parameter-reassignment // tslint:disable-next-line:class-name @@ -34,8 +33,7 @@ export class CoordinatorContract extends BaseContract { signature: string, callData: Partial = {}, defaultBlock?: BlockParam, - ): Promise { + ): Promise { assert.isString('hash', hash); assert.isString('signature', signature); assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ @@ -46,10 +44,8 @@ export class CoordinatorContract extends BaseContract { if (defaultBlock !== undefined) { assert.isBlockParam('defaultBlock', defaultBlock); } - const self = this as any as CoordinatorContract; - const encodedData = self._strictEncodeArguments('getSignerAddress(bytes32,bytes)', [hash, - signature - ]); + const self = (this as any) as CoordinatorContract; + const encodedData = self._strictEncodeArguments('getSignerAddress(bytes32,bytes)', [hash, signature]); const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( { to: self.address, @@ -62,20 +58,17 @@ export class CoordinatorContract extends BaseContract { BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); const abiEncoder = self._lookupAbiEncoder('getSignerAddress(bytes32,bytes)'); // tslint:disable boolean-naming - const result = abiEncoder.strictDecodeReturnValue(rawCallResult); + const result = abiEncoder.strictDecodeReturnValue(rawCallResult); // tslint:enable boolean-naming return result; }, }; public getTransactionHash = { async callAsync( - transaction: {salt: BigNumber;signerAddress: string;data: string}, + transaction: { salt: BigNumber; signerAddress: string; data: string }, callData: Partial = {}, defaultBlock?: BlockParam, - ): Promise { - + ): Promise { assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ schemas.addressSchema, schemas.numberSchema, @@ -84,9 +77,10 @@ export class CoordinatorContract extends BaseContract { if (defaultBlock !== undefined) { assert.isBlockParam('defaultBlock', defaultBlock); } - const self = this as any as CoordinatorContract; - const encodedData = self._strictEncodeArguments('getTransactionHash((uint256,address,bytes))', [transaction - ]); + const self = (this as any) as CoordinatorContract; + const encodedData = self._strictEncodeArguments('getTransactionHash((uint256,address,bytes))', [ + transaction, + ]); const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( { to: self.address, @@ -99,20 +93,22 @@ export class CoordinatorContract extends BaseContract { BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); const abiEncoder = self._lookupAbiEncoder('getTransactionHash((uint256,address,bytes))'); // tslint:disable boolean-naming - const result = abiEncoder.strictDecodeReturnValue(rawCallResult); + const result = abiEncoder.strictDecodeReturnValue(rawCallResult); // tslint:enable boolean-naming return result; }, }; public getCoordinatorApprovalHash = { async callAsync( - approval: {txOrigin: string;transactionHash: string;transactionSignature: string;approvalExpirationTimeSeconds: BigNumber}, + approval: { + txOrigin: string; + transactionHash: string; + transactionSignature: string; + approvalExpirationTimeSeconds: BigNumber; + }, callData: Partial = {}, defaultBlock?: BlockParam, - ): Promise { - + ): Promise { assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ schemas.addressSchema, schemas.numberSchema, @@ -121,9 +117,11 @@ export class CoordinatorContract extends BaseContract { if (defaultBlock !== undefined) { assert.isBlockParam('defaultBlock', defaultBlock); } - const self = this as any as CoordinatorContract; - const encodedData = self._strictEncodeArguments('getCoordinatorApprovalHash((address,bytes32,bytes,uint256))', [approval - ]); + const self = (this as any) as CoordinatorContract; + const encodedData = self._strictEncodeArguments( + 'getCoordinatorApprovalHash((address,bytes32,bytes,uint256))', + [approval], + ); const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( { to: self.address, @@ -136,54 +134,50 @@ export class CoordinatorContract extends BaseContract { BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); const abiEncoder = self._lookupAbiEncoder('getCoordinatorApprovalHash((address,bytes32,bytes,uint256))'); // tslint:disable boolean-naming - const result = abiEncoder.strictDecodeReturnValue(rawCallResult); + const result = abiEncoder.strictDecodeReturnValue(rawCallResult); // tslint:enable boolean-naming return result; }, }; public executeTransaction = { async sendTransactionAsync( - transaction: {salt: BigNumber;signerAddress: string;data: string}, + transaction: { salt: BigNumber; signerAddress: string; data: string }, txOrigin: string, transactionSignature: string, approvalExpirationTimeSeconds: BigNumber[], approvalSignatures: string[], - txData?: Partial | undefined, + txData?: Partial | undefined, ): Promise { - - assert.isString('txOrigin', txOrigin); - assert.isString('transactionSignature', transactionSignature); - assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); - assert.isArray('approvalSignatures', approvalSignatures); - const self = this as any as CoordinatorContract; - const encodedData = self._strictEncodeArguments('executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', [transaction, - txOrigin, - transactionSignature, - approvalExpirationTimeSeconds, - approvalSignatures - ]); - const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( - { - to: self.address, - ...txData, - data: encodedData, - }, - self._web3Wrapper.getContractDefaults(), - self.executeTransaction.estimateGasAsync.bind( - self, - transaction, - txOrigin, - transactionSignature, - approvalExpirationTimeSeconds, - approvalSignatures - ), - ); - const txHash = await self._web3Wrapper.sendTransactionAsync(txDataWithDefaults); - return txHash; + assert.isString('txOrigin', txOrigin); + assert.isString('transactionSignature', transactionSignature); + assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); + assert.isArray('approvalSignatures', approvalSignatures); + const self = (this as any) as CoordinatorContract; + const encodedData = self._strictEncodeArguments( + 'executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', + [transaction, txOrigin, transactionSignature, approvalExpirationTimeSeconds, approvalSignatures], + ); + const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + { + to: self.address, + ...txData, + data: encodedData, + }, + self._web3Wrapper.getContractDefaults(), + self.executeTransaction.estimateGasAsync.bind( + self, + transaction, + txOrigin, + transactionSignature, + approvalExpirationTimeSeconds, + approvalSignatures, + ), + ); + const txHash = await self._web3Wrapper.sendTransactionAsync(txDataWithDefaults); + return txHash; }, awaitTransactionSuccessAsync( - transaction: {salt: BigNumber;signerAddress: string;data: string}, + transaction: { salt: BigNumber; signerAddress: string; data: string }, txOrigin: string, transactionSignature: string, approvalExpirationTimeSeconds: BigNumber[], @@ -192,93 +186,86 @@ export class CoordinatorContract extends BaseContract { pollingIntervalMs?: number, timeoutMs?: number, ): PromiseWithTransactionHash { - - assert.isString('txOrigin', txOrigin); - assert.isString('transactionSignature', transactionSignature); - assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); - assert.isArray('approvalSignatures', approvalSignatures); - const self = this as any as CoordinatorContract; - const txHashPromise = self.executeTransaction.sendTransactionAsync(transaction, - txOrigin, - transactionSignature, - approvalExpirationTimeSeconds, - approvalSignatures - , txData); - return new PromiseWithTransactionHash( - txHashPromise, - (async (): Promise => { - // When the transaction hash resolves, wait for it to be mined. - return self._web3Wrapper.awaitTransactionSuccessAsync( - await txHashPromise, - pollingIntervalMs, - timeoutMs, - ); - })(), - ); + assert.isString('txOrigin', txOrigin); + assert.isString('transactionSignature', transactionSignature); + assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); + assert.isArray('approvalSignatures', approvalSignatures); + const self = (this as any) as CoordinatorContract; + const txHashPromise = self.executeTransaction.sendTransactionAsync( + transaction, + txOrigin, + transactionSignature, + approvalExpirationTimeSeconds, + approvalSignatures, + txData, + ); + return new PromiseWithTransactionHash( + txHashPromise, + (async (): Promise => { + // When the transaction hash resolves, wait for it to be mined. + return self._web3Wrapper.awaitTransactionSuccessAsync( + await txHashPromise, + pollingIntervalMs, + timeoutMs, + ); + })(), + ); }, async estimateGasAsync( - transaction: {salt: BigNumber;signerAddress: string;data: string}, + transaction: { salt: BigNumber; signerAddress: string; data: string }, txOrigin: string, transactionSignature: string, approvalExpirationTimeSeconds: BigNumber[], approvalSignatures: string[], txData?: Partial | undefined, ): Promise { - - assert.isString('txOrigin', txOrigin); - assert.isString('transactionSignature', transactionSignature); - assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); - assert.isArray('approvalSignatures', approvalSignatures); - const self = this as any as CoordinatorContract; - const encodedData = self._strictEncodeArguments('executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', [transaction, - txOrigin, - transactionSignature, - approvalExpirationTimeSeconds, - approvalSignatures - ]); - const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( - { - to: self.address, - ...txData, - data: encodedData, - }, - self._web3Wrapper.getContractDefaults(), - ); - const gas = await self._web3Wrapper.estimateGasAsync(txDataWithDefaults); - return gas; + assert.isString('txOrigin', txOrigin); + assert.isString('transactionSignature', transactionSignature); + assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); + assert.isArray('approvalSignatures', approvalSignatures); + const self = (this as any) as CoordinatorContract; + const encodedData = self._strictEncodeArguments( + 'executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', + [transaction, txOrigin, transactionSignature, approvalExpirationTimeSeconds, approvalSignatures], + ); + const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( + { + to: self.address, + ...txData, + data: encodedData, + }, + self._web3Wrapper.getContractDefaults(), + ); + const gas = await self._web3Wrapper.estimateGasAsync(txDataWithDefaults); + return gas; }, getABIEncodedTransactionData( - transaction: {salt: BigNumber;signerAddress: string;data: string}, + transaction: { salt: BigNumber; signerAddress: string; data: string }, txOrigin: string, transactionSignature: string, approvalExpirationTimeSeconds: BigNumber[], approvalSignatures: string[], ): string { - - assert.isString('txOrigin', txOrigin); - assert.isString('transactionSignature', transactionSignature); - assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); - assert.isArray('approvalSignatures', approvalSignatures); - const self = this as any as CoordinatorContract; - const abiEncodedTransactionData = self._strictEncodeArguments('executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', [transaction, - txOrigin, - transactionSignature, - approvalExpirationTimeSeconds, - approvalSignatures - ]); - return abiEncodedTransactionData; + assert.isString('txOrigin', txOrigin); + assert.isString('transactionSignature', transactionSignature); + assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); + assert.isArray('approvalSignatures', approvalSignatures); + const self = (this as any) as CoordinatorContract; + const abiEncodedTransactionData = self._strictEncodeArguments( + 'executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', + [transaction, txOrigin, transactionSignature, approvalExpirationTimeSeconds, approvalSignatures], + ); + return abiEncodedTransactionData; }, async callAsync( - transaction: {salt: BigNumber;signerAddress: string;data: string}, + transaction: { salt: BigNumber; signerAddress: string; data: string }, txOrigin: string, transactionSignature: string, approvalExpirationTimeSeconds: BigNumber[], approvalSignatures: string[], callData: Partial = {}, defaultBlock?: BlockParam, - ): Promise { - + ): Promise { assert.isString('txOrigin', txOrigin); assert.isString('transactionSignature', transactionSignature); assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); @@ -291,13 +278,11 @@ export class CoordinatorContract extends BaseContract { if (defaultBlock !== undefined) { assert.isBlockParam('defaultBlock', defaultBlock); } - const self = this as any as CoordinatorContract; - const encodedData = self._strictEncodeArguments('executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', [transaction, - txOrigin, - transactionSignature, - approvalExpirationTimeSeconds, - approvalSignatures - ]); + const self = (this as any) as CoordinatorContract; + const encodedData = self._strictEncodeArguments( + 'executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', + [transaction, txOrigin, transactionSignature, approvalExpirationTimeSeconds, approvalSignatures], + ); const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( { to: self.address, @@ -308,20 +293,17 @@ export class CoordinatorContract extends BaseContract { ); const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock); BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); - const abiEncoder = self._lookupAbiEncoder('executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])'); + const abiEncoder = self._lookupAbiEncoder( + 'executeTransaction((uint256,address,bytes),address,bytes,uint256[],bytes[])', + ); // tslint:disable boolean-naming - const result = abiEncoder.strictDecodeReturnValue(rawCallResult); + const result = abiEncoder.strictDecodeReturnValue(rawCallResult); // tslint:enable boolean-naming return result; }, }; public EIP712_EXCHANGE_DOMAIN_HASH = { - async callAsync( - callData: Partial = {}, - defaultBlock?: BlockParam, - ): Promise { + async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ schemas.addressSchema, schemas.numberSchema, @@ -330,7 +312,7 @@ export class CoordinatorContract extends BaseContract { if (defaultBlock !== undefined) { assert.isBlockParam('defaultBlock', defaultBlock); } - const self = this as any as CoordinatorContract; + const self = (this as any) as CoordinatorContract; const encodedData = self._strictEncodeArguments('EIP712_EXCHANGE_DOMAIN_HASH()', []); const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( { @@ -344,24 +326,21 @@ export class CoordinatorContract extends BaseContract { BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); const abiEncoder = self._lookupAbiEncoder('EIP712_EXCHANGE_DOMAIN_HASH()'); // tslint:disable boolean-naming - const result = abiEncoder.strictDecodeReturnValue(rawCallResult); + const result = abiEncoder.strictDecodeReturnValue(rawCallResult); // tslint:enable boolean-naming return result; }, }; public assertValidCoordinatorApprovals = { async callAsync( - transaction: {salt: BigNumber;signerAddress: string;data: string}, + transaction: { salt: BigNumber; signerAddress: string; data: string }, txOrigin: string, transactionSignature: string, approvalExpirationTimeSeconds: BigNumber[], approvalSignatures: string[], callData: Partial = {}, defaultBlock?: BlockParam, - ): Promise { - + ): Promise { assert.isString('txOrigin', txOrigin); assert.isString('transactionSignature', transactionSignature); assert.isArray('approvalExpirationTimeSeconds', approvalExpirationTimeSeconds); @@ -374,13 +353,11 @@ export class CoordinatorContract extends BaseContract { if (defaultBlock !== undefined) { assert.isBlockParam('defaultBlock', defaultBlock); } - const self = this as any as CoordinatorContract; - const encodedData = self._strictEncodeArguments('assertValidCoordinatorApprovals((uint256,address,bytes),address,bytes,uint256[],bytes[])', [transaction, - txOrigin, - transactionSignature, - approvalExpirationTimeSeconds, - approvalSignatures - ]); + const self = (this as any) as CoordinatorContract; + const encodedData = self._strictEncodeArguments( + 'assertValidCoordinatorApprovals((uint256,address,bytes),address,bytes,uint256[],bytes[])', + [transaction, txOrigin, transactionSignature, approvalExpirationTimeSeconds, approvalSignatures], + ); const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( { to: self.address, @@ -391,10 +368,11 @@ export class CoordinatorContract extends BaseContract { ); const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock); BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); - const abiEncoder = self._lookupAbiEncoder('assertValidCoordinatorApprovals((uint256,address,bytes),address,bytes,uint256[],bytes[])'); + const abiEncoder = self._lookupAbiEncoder( + 'assertValidCoordinatorApprovals((uint256,address,bytes),address,bytes,uint256[],bytes[])', + ); // tslint:disable boolean-naming - const result = abiEncoder.strictDecodeReturnValue(rawCallResult); + const result = abiEncoder.strictDecodeReturnValue(rawCallResult); // tslint:enable boolean-naming return result; }, @@ -404,7 +382,21 @@ export class CoordinatorContract extends BaseContract { data: string, callData: Partial = {}, defaultBlock?: BlockParam, - ): Promise + ): Promise< + Array<{ + makerAddress: string; + takerAddress: string; + feeRecipientAddress: string; + senderAddress: string; + makerAssetAmount: BigNumber; + takerAssetAmount: BigNumber; + makerFee: BigNumber; + takerFee: BigNumber; + expirationTimeSeconds: BigNumber; + salt: BigNumber; + makerAssetData: string; + takerAssetData: string; + }> > { assert.isString('data', data); assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ @@ -415,9 +407,8 @@ export class CoordinatorContract extends BaseContract { if (defaultBlock !== undefined) { assert.isBlockParam('defaultBlock', defaultBlock); } - const self = this as any as CoordinatorContract; - const encodedData = self._strictEncodeArguments('decodeOrdersFromFillData(bytes)', [data - ]); + const self = (this as any) as CoordinatorContract; + const encodedData = self._strictEncodeArguments('decodeOrdersFromFillData(bytes)', [data]); const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( { to: self.address, @@ -430,18 +421,28 @@ export class CoordinatorContract extends BaseContract { BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); const abiEncoder = self._lookupAbiEncoder('decodeOrdersFromFillData(bytes)'); // tslint:disable boolean-naming - const result = abiEncoder.strictDecodeReturnValue - >(rawCallResult); + const result = abiEncoder.strictDecodeReturnValue< + Array<{ + makerAddress: string; + takerAddress: string; + feeRecipientAddress: string; + senderAddress: string; + makerAssetAmount: BigNumber; + takerAssetAmount: BigNumber; + makerFee: BigNumber; + takerFee: BigNumber; + expirationTimeSeconds: BigNumber; + salt: BigNumber; + makerAssetData: string; + takerAssetData: string; + }> + >(rawCallResult); // tslint:enable boolean-naming return result; }, }; public EIP712_COORDINATOR_DOMAIN_HASH = { - async callAsync( - callData: Partial = {}, - defaultBlock?: BlockParam, - ): Promise { + async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [ schemas.addressSchema, schemas.numberSchema, @@ -450,7 +451,7 @@ export class CoordinatorContract extends BaseContract { if (defaultBlock !== undefined) { assert.isBlockParam('defaultBlock', defaultBlock); } - const self = this as any as CoordinatorContract; + const self = (this as any) as CoordinatorContract; const encodedData = self._strictEncodeArguments('EIP712_COORDINATOR_DOMAIN_HASH()', []); const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( { @@ -464,8 +465,7 @@ export class CoordinatorContract extends BaseContract { BaseContract._throwIfRevertWithReasonCallResult(rawCallResult); const abiEncoder = self._lookupAbiEncoder('EIP712_COORDINATOR_DOMAIN_HASH()'); // tslint:disable boolean-naming - const result = abiEncoder.strictDecodeReturnValue(rawCallResult); + const result = abiEncoder.strictDecodeReturnValue(rawCallResult); // tslint:enable boolean-naming return result; }, @@ -474,7 +474,7 @@ export class CoordinatorContract extends BaseContract { artifact: ContractArtifact | SimpleContractArtifact, supportedProvider: SupportedProvider, txDefaults: Partial, - _exchange: string, + _exchange: string, ): Promise { assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [ schemas.addressSchema, @@ -487,15 +487,14 @@ export class CoordinatorContract extends BaseContract { const provider = providerUtils.standardizeOrThrow(supportedProvider); const bytecode = artifact.compilerOutput.evm.bytecode.object; const abi = artifact.compilerOutput.abi; - return CoordinatorContract.deployAsync(bytecode, abi, provider, txDefaults, _exchange -); + return CoordinatorContract.deployAsync(bytecode, abi, provider, txDefaults, _exchange); } public static async deployAsync( bytecode: string, abi: ContractAbi, supportedProvider: SupportedProvider, txDefaults: Partial, - _exchange: string, + _exchange: string, ): Promise { assert.isHexString('bytecode', bytecode); assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [ @@ -505,20 +504,17 @@ export class CoordinatorContract extends BaseContract { ]); const provider = providerUtils.standardizeOrThrow(supportedProvider); const constructorAbi = BaseContract._lookupConstructorAbi(abi); - [_exchange -] = BaseContract._formatABIDataItemList( + [_exchange] = BaseContract._formatABIDataItemList( constructorAbi.inputs, - [_exchange -], + [_exchange], BaseContract._bigNumberToString, ); const iface = new ethers.utils.Interface(abi); const deployInfo = iface.deployFunction; - const txData = deployInfo.encode(bytecode, [_exchange -]); + const txData = deployInfo.encode(bytecode, [_exchange]); const web3Wrapper = new Web3Wrapper(provider); const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync( - {data: txData}, + { data: txData }, txDefaults, web3Wrapper.estimateGasAsync.bind(web3Wrapper), ); @@ -526,9 +522,13 @@ export class CoordinatorContract extends BaseContract { logUtils.log(`transactionHash: ${txHash}`); const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash); logUtils.log(`Coordinator successfully deployed at ${txReceipt.contractAddress}`); - const contractInstance = new CoordinatorContract(abi, txReceipt.contractAddress as string, provider, txDefaults); - contractInstance.constructorArgs = [_exchange -]; + const contractInstance = new CoordinatorContract( + abi, + txReceipt.contractAddress as string, + provider, + txDefaults, + ); + contractInstance.constructorArgs = [_exchange]; return contractInstance; } constructor(abi: ContractAbi, address: string, supportedProvider: SupportedProvider, txDefaults?: Partial) { diff --git a/tsconfig.json b/tsconfig.json index bfa83503e8..3382161ab8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,6 +30,7 @@ { "path": "./contracts/multisig" }, { "path": "./contracts/test-utils" }, { "path": "./contracts/utils" }, + { "path": "./contracts/dev-utils" }, { "path": "./packages/0x.js" }, { "path": "./packages/abi-gen-wrappers" }, { "path": "./packages/abi-gen" }, From f68b8d82e0854af7d831b1ebfd2fa192af7300ad Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Jun 2019 17:48:26 -0700 Subject: [PATCH 04/15] Move LibTransactionDecoder to dev-utils package --- contracts/asset-proxy/test/lib_asset_data.ts | 7 +- contracts/dev-utils/compiler.json | 2 +- .../dev-utils/contracts/src/DevUtils.sol | 2 +- .../contracts/src/LibTransactionDecoder.sol | 0 contracts/dev-utils/package.json | 2 +- contracts/dev-utils/src/artifacts.ts | 6 +- contracts/dev-utils/src/wrappers.ts | 1 + .../dev-utils/test/lib_transaction_decoder.ts | 144 +++++++++++ ...dev_utils.ts => order_validation_utils.ts} | 5 +- contracts/dev-utils/tsconfig.json | 2 +- contracts/exchange-libs/compiler.json | 1 - contracts/exchange-libs/package.json | 2 +- contracts/exchange-libs/src/artifacts.ts | 2 - contracts/exchange-libs/src/wrappers.ts | 1 - .../test/lib_transaction_decoder.ts | 231 ------------------ contracts/exchange-libs/tsconfig.json | 1 - 16 files changed, 159 insertions(+), 250 deletions(-) rename contracts/{exchange-libs => dev-utils}/contracts/src/LibTransactionDecoder.sol (100%) create mode 100644 contracts/dev-utils/test/lib_transaction_decoder.ts rename contracts/dev-utils/test/{dev_utils.ts => order_validation_utils.ts} (99%) delete mode 100644 contracts/exchange-libs/test/lib_transaction_decoder.ts diff --git a/contracts/asset-proxy/test/lib_asset_data.ts b/contracts/asset-proxy/test/lib_asset_data.ts index 29f18a9159..e44ef85468 100644 --- a/contracts/asset-proxy/test/lib_asset_data.ts +++ b/contracts/asset-proxy/test/lib_asset_data.ts @@ -115,16 +115,13 @@ describe('LibAssetData', () => { provider, txDefaults, ); - // Somewhat re-inventing the wheel here, but the prior art currently - // exists only as an unexported test util in the erc1155 package - // (Erc1155Wrapper.mintFungibleTokensAsync() in erc1155/test/utils/). - // This is concise enough to justify duplication, but it sure is ugly. - // tslint:disable-next-line no-unnecessary-type-assertion + const logDecoder = new LogDecoder(web3Wrapper, erc1155Artifacts); const transactionReceipt = await logDecoder.getTxWithDecodedLogsAsync( await erc1155Token.create.sendTransactionAsync('uri:Dummy', /*isNonFungible:*/ false), ); + // tslint:disable-next-line no-unnecessary-type-assertion erc1155TokenId = (transactionReceipt.logs[0] as LogWithDecodedArgs).args.id; await erc1155Token.mintFungible.awaitTransactionSuccessAsync( erc1155TokenId, diff --git a/contracts/dev-utils/compiler.json b/contracts/dev-utils/compiler.json index 2157d0a193..1117c0be6b 100644 --- a/contracts/dev-utils/compiler.json +++ b/contracts/dev-utils/compiler.json @@ -22,5 +22,5 @@ } } }, - "contracts": ["src/DevUtils.sol"] + "contracts": ["src/DevUtils.sol", "src/LibTransactionDecoder.sol"] } diff --git a/contracts/dev-utils/contracts/src/DevUtils.sol b/contracts/dev-utils/contracts/src/DevUtils.sol index b3827051b5..16de2404cb 100644 --- a/contracts/dev-utils/contracts/src/DevUtils.sol +++ b/contracts/dev-utils/contracts/src/DevUtils.sol @@ -20,7 +20,7 @@ pragma solidity ^0.5.5; pragma experimental ABIEncoderV2; import "./OrderValidationUtils.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibTransactionDecoder.sol"; +import "./LibTransactionDecoder.sol"; // solhint-disable no-empty-blocks diff --git a/contracts/exchange-libs/contracts/src/LibTransactionDecoder.sol b/contracts/dev-utils/contracts/src/LibTransactionDecoder.sol similarity index 100% rename from contracts/exchange-libs/contracts/src/LibTransactionDecoder.sol rename to contracts/dev-utils/contracts/src/LibTransactionDecoder.sol diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index d12ddb5281..a7c6450bc7 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -34,7 +34,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "./generated-artifacts/@(DevUtils).json", + "abis": "./generated-artifacts/@(DevUtils|LibTransactionDecoder).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/dev-utils/src/artifacts.ts b/contracts/dev-utils/src/artifacts.ts index 148184dd70..229b8eb26b 100644 --- a/contracts/dev-utils/src/artifacts.ts +++ b/contracts/dev-utils/src/artifacts.ts @@ -6,4 +6,8 @@ import { ContractArtifact } from 'ethereum-types'; import * as DevUtils from '../generated-artifacts/DevUtils.json'; -export const artifacts = { DevUtils: DevUtils as ContractArtifact }; +import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json'; +export const artifacts = { + DevUtils: DevUtils as ContractArtifact, + LibTransactionDecoder: LibTransactionDecoder as ContractArtifact, +}; diff --git a/contracts/dev-utils/src/wrappers.ts b/contracts/dev-utils/src/wrappers.ts index ca8ce678dc..78bc0dcfce 100644 --- a/contracts/dev-utils/src/wrappers.ts +++ b/contracts/dev-utils/src/wrappers.ts @@ -4,3 +4,4 @@ * ----------------------------------------------------------------------------- */ export * from '../generated-wrappers/dev_utils'; +export * from '../generated-wrappers/lib_transaction_decoder'; diff --git a/contracts/dev-utils/test/lib_transaction_decoder.ts b/contracts/dev-utils/test/lib_transaction_decoder.ts new file mode 100644 index 0000000000..26f5cf42b5 --- /dev/null +++ b/contracts/dev-utils/test/lib_transaction_decoder.ts @@ -0,0 +1,144 @@ +import { artifacts as exchangeArtifacts, IExchangeContract } from '@0x/contracts-exchange'; +import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { BigNumber } from '@0x/utils'; +import * as chai from 'chai'; + +import { artifacts, LibTransactionDecoderContract } from '../src'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +const order = { + makerAddress: '0xe36ea790bc9d7ab70c55260c66d52b1eca985f84', + takerAddress: '0x0000000000000000000000000000000000000000', + feeRecipientAddress: '0x78dc5d2d739606d31509c31d654056a45185ecb6', + senderAddress: '0x6ecbe1db9ef729cbe972c83fb886247691fb6beb', + makerAssetAmount: new BigNumber('100000000000000000000'), + takerAssetAmount: new BigNumber('200000000000000000000'), + makerFee: new BigNumber('1000000000000000000'), + takerFee: new BigNumber('1000000000000000000'), + expirationTimeSeconds: new BigNumber('1552396423'), + salt: new BigNumber('66097384406870180066678463045003379626790660770396923976862707230261946348951'), + makerAssetData: '0xf47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064', + takerAssetData: '0xf47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e3', +}; +const takerAssetFillAmount = new BigNumber('100000000000000000000'); +const signature = + '0x1ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03'; + +describe('LibTransactionDecoder', () => { + let libTxDecoder: LibTransactionDecoderContract; + const exchangeInterface = new IExchangeContract( + exchangeArtifacts.Exchange.compilerOutput.abi, + constants.NULL_ADDRESS, + provider, + txDefaults, + ); + before(async () => { + await blockchainLifecycle.startAsync(); + libTxDecoder = await LibTransactionDecoderContract.deployFrom0xArtifactAsync( + artifacts.LibTransactionDecoder, + provider, + txDefaults, + ); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + + it('should decode an Exchange.batchCancelOrders() transaction', async () => { + const input = exchangeInterface.batchCancelOrders.getABIEncodedTransactionData([order, order]); + expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([ + 'batchCancelOrders', + [order, order], + [], + [], + ]); + }); + + for (const func of ['batchFillOrders', 'batchFillOrdersNoThrow', 'batchFillOrKillOrders']) { + const input = (exchangeInterface as any)[func].getABIEncodedTransactionData( + [order, order], + [takerAssetFillAmount, takerAssetFillAmount], + [signature, signature], + ); + it(`should decode an Exchange.${func}() transaction`, async () => { + expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([ + func, + [order, order], + [takerAssetFillAmount, takerAssetFillAmount], + [signature, signature], + ]); + }); + } + + it('should decode an Exchange.cancelOrder() transaction', async () => { + const input = exchangeInterface.cancelOrder.getABIEncodedTransactionData(order); + expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([ + 'cancelOrder', + [order], + [], + [], + ]); + }); + + for (const func of ['fillOrder', 'fillOrderNoThrow', 'fillOrKillOrder']) { + const input = (exchangeInterface as any)[func].getABIEncodedTransactionData( + order, + takerAssetFillAmount, + signature, + ); + it(`should decode an Exchange.${func}() transaction`, async () => { + expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([ + func, + [order], + [takerAssetFillAmount], + [signature], + ]); + }); + } + + for (const func of ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow']) { + const input = (exchangeInterface as any)[func].getABIEncodedTransactionData( + [order, order], + takerAssetFillAmount, + [signature, signature], + ); + it(`should decode an Exchange.${func}() transaction`, async () => { + expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([ + func, + [order, order], + [takerAssetFillAmount], + [signature, signature], + ]); + }); + } + + it('should decode an Exchange.matchOrders() transaction', async () => { + const complementaryOrder = { + ...order, + makerAddress: order.takerAddress, + takerAddress: order.makerAddress, + makerAssetData: order.takerAssetData, + takerAssetData: order.makerAssetData, + makerAssetAmount: order.takerAssetAmount, + takerAssetAmount: order.makerAssetAmount, + makerFee: order.takerFee, + takerFee: order.makerFee, + }; + const input = exchangeInterface.matchOrders.getABIEncodedTransactionData( + order, + complementaryOrder, + signature, + signature, + ); + expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(input)).to.deep.equal([ + 'matchOrders', + [order, complementaryOrder], + [order.takerAssetAmount, complementaryOrder.takerAssetAmount], + [signature, signature], + ]); + }); +}); diff --git a/contracts/dev-utils/test/dev_utils.ts b/contracts/dev-utils/test/order_validation_utils.ts similarity index 99% rename from contracts/dev-utils/test/dev_utils.ts rename to contracts/dev-utils/test/order_validation_utils.ts index 7d8ac0cfe0..bff78732c0 100644 --- a/contracts/dev-utils/test/dev_utils.ts +++ b/contracts/dev-utils/test/order_validation_utils.ts @@ -16,7 +16,6 @@ import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; -import * as _ from 'lodash'; import { artifacts, DevUtilsContract } from '../src'; @@ -24,7 +23,7 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -describe('DevUtils', () => { +describe('OrderValidationUtils', () => { let makerAddress: string; let takerAddress: string; let owner: string; @@ -56,7 +55,7 @@ describe('DevUtils', () => { before(async () => { const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, makerAddress, takerAddress] = _.slice(accounts, 0, 3)); + const usedAddresses = ([owner, makerAddress, takerAddress] = accounts.slice(0, 3)); const erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); diff --git a/contracts/dev-utils/tsconfig.json b/contracts/dev-utils/tsconfig.json index b428ecd4d9..eec1de9575 100644 --- a/contracts/dev-utils/tsconfig.json +++ b/contracts/dev-utils/tsconfig.json @@ -2,6 +2,6 @@ "extends": "../../tsconfig", "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], - "files": ["generated-artifacts/DevUtils.json"], + "files": ["generated-artifacts/DevUtils.json", "generated-artifacts/LibTransactionDecoder.json"], "exclude": ["./deploy/solc/solc_bin"] } diff --git a/contracts/exchange-libs/compiler.json b/contracts/exchange-libs/compiler.json index a7ba8cc2fc..7a985d3c12 100644 --- a/contracts/exchange-libs/compiler.json +++ b/contracts/exchange-libs/compiler.json @@ -30,7 +30,6 @@ "src/LibFillResults.sol", "src/LibMath.sol", "src/LibOrder.sol", - "src/LibTransactionDecoder.sol", "test/TestLibs.sol" ] } diff --git a/contracts/exchange-libs/package.json b/contracts/exchange-libs/package.json index b03cb2489e..ee7ed5332c 100644 --- a/contracts/exchange-libs/package.json +++ b/contracts/exchange-libs/package.json @@ -34,7 +34,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "./generated-artifacts/@(LibAbiEncoder|LibAssetProxyErrors|LibConstants|LibEIP712|LibFillResults|LibMath|LibOrder|LibTransactionDecoder|TestLibs).json", + "abis": "./generated-artifacts/@(LibAbiEncoder|LibAssetProxyErrors|LibConstants|LibEIP712|LibFillResults|LibMath|LibOrder|TestLibs).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/exchange-libs/src/artifacts.ts b/contracts/exchange-libs/src/artifacts.ts index 735802ab65..861d6f3d2d 100644 --- a/contracts/exchange-libs/src/artifacts.ts +++ b/contracts/exchange-libs/src/artifacts.ts @@ -12,7 +12,6 @@ import * as LibEIP712 from '../generated-artifacts/LibEIP712.json'; import * as LibFillResults from '../generated-artifacts/LibFillResults.json'; import * as LibMath from '../generated-artifacts/LibMath.json'; import * as LibOrder from '../generated-artifacts/LibOrder.json'; -import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json'; import * as TestLibs from '../generated-artifacts/TestLibs.json'; export const artifacts = { LibAbiEncoder: LibAbiEncoder as ContractArtifact, @@ -22,6 +21,5 @@ export const artifacts = { LibFillResults: LibFillResults as ContractArtifact, LibMath: LibMath as ContractArtifact, LibOrder: LibOrder as ContractArtifact, - LibTransactionDecoder: LibTransactionDecoder as ContractArtifact, TestLibs: TestLibs as ContractArtifact, }; diff --git a/contracts/exchange-libs/src/wrappers.ts b/contracts/exchange-libs/src/wrappers.ts index 0cede57f8c..05244d87b7 100644 --- a/contracts/exchange-libs/src/wrappers.ts +++ b/contracts/exchange-libs/src/wrappers.ts @@ -10,5 +10,4 @@ export * from '../generated-wrappers/lib_e_i_p712'; export * from '../generated-wrappers/lib_fill_results'; export * from '../generated-wrappers/lib_math'; export * from '../generated-wrappers/lib_order'; -export * from '../generated-wrappers/lib_transaction_decoder'; export * from '../generated-wrappers/test_libs'; diff --git a/contracts/exchange-libs/test/lib_transaction_decoder.ts b/contracts/exchange-libs/test/lib_transaction_decoder.ts deleted file mode 100644 index 36e6db3070..0000000000 --- a/contracts/exchange-libs/test/lib_transaction_decoder.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { chaiSetup, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { Order } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import * as chai from 'chai'; - -import { artifacts, LibTransactionDecoderContract } from '../src'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -type OrderWithoutExchange = Pick>; - -const INPUTS: { order: OrderWithoutExchange; takerAssetFillAmount: BigNumber; signature: string } = { - order: { - makerAddress: '0xe36ea790bc9d7ab70c55260c66d52b1eca985f84', - takerAddress: '0x0000000000000000000000000000000000000000', - feeRecipientAddress: '0x78dc5d2d739606d31509c31d654056a45185ecb6', - senderAddress: '0x6ecbe1db9ef729cbe972c83fb886247691fb6beb', - makerAssetAmount: new BigNumber('100000000000000000000'), - takerAssetAmount: new BigNumber('200000000000000000000'), - makerFee: new BigNumber('1000000000000000000'), - takerFee: new BigNumber('1000000000000000000'), - expirationTimeSeconds: new BigNumber('1552396423'), - salt: new BigNumber('66097384406870180066678463045003379626790660770396923976862707230261946348951'), - makerAssetData: '0xf47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064', - takerAssetData: '0xf47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e3', - }, - takerAssetFillAmount: new BigNumber('100000000000000000000'), - signature: - '0x1ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03', -}; - -const ENCODED_INPUTS: { [functionName: string]: string } = { - // It would be best to encode inputs as part of the test run, but that's - // not really possible in this case, because doing so would introduce a - // dependency on @0x/contracts-exchange, but of course that package already - // depends on @0x/contracts-exchange-libs (this package), so it would - // introduce a circular dependency. - // - // Values used in this object are declared along with the (commented out) - // code that generated them. Running that code depends on the following: - // - // import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange'; - // import { getContractAddressesForNetworkOrThrow, NetworkId } from '@0x/contract-addresses'; - // const exchangeContract = new ExchangeContract( - // exchangeArtifacts.Exchange.compilerOutput.abi, - // getContractAddressesForNetworkOrThrow(NetworkId.Ganache).exchange, - // provider, - // ); - // - batchCancelOrders: - // exchangeContract.batchCancelOrders.getABIEncodedTransactionData([ - // INPUTS.order, - // INPUTS.order, - // ]), - '0x4ac147820000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e300000000000000000000000000000000000000000000000000000000', - batchFillOrders: - // exchangeContract.batchFillOrders.getABIEncodedTransactionData( - // [INPUTS.order, INPUTS.order], - // [INPUTS.takerAssetFillAmount, INPUTS.takerAssetFillAmount], - // [INPUTS.signature, INPUTS.signature], - // ), - '0x297bb70b000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - batchFillOrdersNoThrow: - // exchangeContract.batchFillOrdersNoThrow.getABIEncodedTransactionData( - // [INPUTS.order, INPUTS.order], - // [INPUTS.takerAssetFillAmount, INPUTS.takerAssetFillAmount], - // [INPUTS.signature, INPUTS.signature], - // ), - '0x50dde190000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - batchFillOrKillOrders: - // exchangeContract.batchFillOrKillOrders.getABIEncodedTransactionData( - // [INPUTS.order, INPUTS.order], - // [INPUTS.takerAssetFillAmount, INPUTS.takerAssetFillAmount], - // [INPUTS.signature, INPUTS.signature], - // ), - '0x4d0ae546000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - cancelOrder: - // exchangeContract.cancelOrder.getABIEncodedTransactionData(INPUTS.order), - '0xd46b02c30000000000000000000000000000000000000000000000000000000000000020000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e300000000000000000000000000000000000000000000000000000000', - fillOrder: - // exchangeContract.fillOrder.getABIEncodedTransactionData( - // INPUTS.order, - // INPUTS.takerAssetFillAmount, - // INPUTS.signature, - // ), - '0xb4be83d500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - fillOrderNoThrow: - // exchangeContract.fillOrderNoThrow.getABIEncodedTransactionData( - // INPUTS.order, - // INPUTS.takerAssetFillAmount, - // INPUTS.signature, - // ), - '0x3e228bae00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - fillOrKillOrder: - // exchangeContract.fillOrKillOrder.getABIEncodedTransactionData( - // INPUTS.order, - // INPUTS.takerAssetFillAmount, - // INPUTS.signature, - // ), - '0x64a3bc1500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000000000000000002a0000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - marketBuyOrders: - // exchangeContract.marketBuyOrders.getABIEncodedTransactionData( - // [INPUTS.order, INPUTS.order], - // INPUTS.takerAssetFillAmount, - // [INPUTS.signature, INPUTS.signature], - // ), - '0xe5fa431b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - marketBuyOrdersNoThrow: - // exchangeContract.marketBuyOrdersNoThrow.getABIEncodedTransactionData( - // [INPUTS.order, INPUTS.order], - // INPUTS.takerAssetFillAmount, - // [INPUTS.signature, INPUTS.signature], - // ), - '0xa3e2038000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - marketSellOrders: - // exchangeContract.marketSellOrders.getABIEncodedTransactionData( - // [INPUTS.order, INPUTS.order], - // INPUTS.takerAssetFillAmount, - // [INPUTS.signature, INPUTS.signature], - // ), - '0x7e1d980800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - marketSellOrdersNoThrow: - // exchangeContract.marketSellOrdersNoThrow.getABIEncodedTransactionData( - // [INPUTS.order, INPUTS.order], - // INPUTS.takerAssetFillAmount, - // [INPUTS.signature, INPUTS.signature], - // ), - '0xdd1c7d1800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000e36ea790bc9d7ab70c55260c66d52b1eca985f84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb0000000000000000000000000000000000000000000000056bc75e2d6310000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', - matchOrders: - // exchangeContract.matchOrders.getABIEncodedTransactionData( - // INPUTS.order, - // makeComplementaryOrder(INPUTS.order), - // INPUTS.signature, - // INPUTS.signature, - // ), - '0x3c28d86100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078dc5d2d739606d31509c31d654056a45185ecb60000000000000000000000006ecbe1db9ef729cbe972c83fb886247691fb6beb00000000000000000000000000000000000000000000000ad78ebc5ac620000000000000000000000000000000000000000000000000000ad78ebc5ac62000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000005c87b0879221cb37dcf690e02b0f9aecf44fcaa5ed9ce99697e86743795fa132596ff597000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000024f47261b000000000000000000000000025b8fe1de9daf8ba351890744ff28cf7dfa8f5e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000421ce8e3c600d933423172b5021158a6be2e818613ff8e762d70ef490c752fd98a626a215f09f169668990414de75a53da221c294a3002f796d004827258b641876e03000000000000000000000000000000000000000000000000000000000000', -}; - -describe('LibTransactionDecoder', () => { - let libTxDecoder: LibTransactionDecoderContract; - - before(async () => { - await blockchainLifecycle.startAsync(); - libTxDecoder = await LibTransactionDecoderContract.deployFrom0xArtifactAsync( - artifacts.LibTransactionDecoder, - provider, - txDefaults, - ); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - - it('should decode an Exchange.batchCancelOrders() transaction', async () => { - expect( - await libTxDecoder.decodeZeroExTransactionData.callAsync(ENCODED_INPUTS.batchCancelOrders), - ).to.deep.equal(['batchCancelOrders', [INPUTS.order, INPUTS.order], [], []]); - }); - - for (const func of ['batchFillOrders', 'batchFillOrdersNoThrow', 'batchFillOrKillOrders']) { - it(`should decode an Exchange.${func}() transaction`, async () => { - expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(ENCODED_INPUTS[func])).to.deep.equal([ - func, - [INPUTS.order, INPUTS.order], - [INPUTS.takerAssetFillAmount, INPUTS.takerAssetFillAmount], - [INPUTS.signature, INPUTS.signature], - ]); - }); - } - - it('should decode an Exchange.cancelOrder() transaction', async () => { - expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(ENCODED_INPUTS.cancelOrder)).to.deep.equal([ - 'cancelOrder', - [INPUTS.order], - [], - [], - ]); - }); - - for (const func of ['fillOrder', 'fillOrderNoThrow', 'fillOrKillOrder']) { - it(`should decode an Exchange.${func}() transaction`, async () => { - expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(ENCODED_INPUTS[func])).to.deep.equal([ - func, - [INPUTS.order], - [INPUTS.takerAssetFillAmount], - [INPUTS.signature], - ]); - }); - } - - for (const func of ['marketBuyOrders', 'marketBuyOrdersNoThrow', 'marketSellOrders', 'marketSellOrdersNoThrow']) { - it(`should decode an Exchange.${func}() transaction`, async () => { - expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(ENCODED_INPUTS[func])).to.deep.equal([ - func, - [INPUTS.order, INPUTS.order], - [INPUTS.takerAssetFillAmount], - [INPUTS.signature, INPUTS.signature], - ]); - }); - } - - it('should decode an Exchange.matchOrders() transaction', async () => { - function makeComplementaryOrder(order: OrderWithoutExchange): OrderWithoutExchange { - const complementaryOrder = order; - - complementaryOrder.makerAddress = order.takerAddress; - complementaryOrder.takerAddress = order.makerAddress; - - complementaryOrder.makerAssetData = order.takerAssetData; - complementaryOrder.takerAssetData = order.makerAssetData; - - complementaryOrder.makerAssetAmount = order.takerAssetAmount; - complementaryOrder.takerAssetAmount = order.makerAssetAmount; - - complementaryOrder.makerFee = order.takerFee; - complementaryOrder.takerFee = order.makerFee; - - return complementaryOrder; - } - - expect(await libTxDecoder.decodeZeroExTransactionData.callAsync(ENCODED_INPUTS.matchOrders)).to.deep.equal([ - 'matchOrders', - [INPUTS.order, makeComplementaryOrder(INPUTS.order)], - [INPUTS.order.takerAssetAmount, makeComplementaryOrder(INPUTS.order).takerAssetAmount], - [INPUTS.signature, INPUTS.signature], - ]); - }); -}); diff --git a/contracts/exchange-libs/tsconfig.json b/contracts/exchange-libs/tsconfig.json index cf9bf35633..1b5d541758 100644 --- a/contracts/exchange-libs/tsconfig.json +++ b/contracts/exchange-libs/tsconfig.json @@ -10,7 +10,6 @@ "generated-artifacts/LibFillResults.json", "generated-artifacts/LibMath.json", "generated-artifacts/LibOrder.json", - "generated-artifacts/LibTransactionDecoder.json", "generated-artifacts/TestLibs.json" ], "exclude": ["./deploy/solc/solc_bin"] From 62f0a867a800fd306fe13327722ca48b4f288b73 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Jun 2019 17:53:53 -0700 Subject: [PATCH 05/15] Update CHANGELOGs --- contracts/dev-utils/CHANGELOG.json | 2 +- contracts/dev-utils/CHANGELOG.md | 99 -------------------------- contracts/exchange-libs/CHANGELOG.json | 9 +++ contracts/extensions/CHANGELOG.json | 8 +-- 4 files changed, 12 insertions(+), 106 deletions(-) delete mode 100644 contracts/dev-utils/CHANGELOG.md diff --git a/contracts/dev-utils/CHANGELOG.json b/contracts/dev-utils/CHANGELOG.json index a3ea7fc26f..4249f2d831 100644 --- a/contracts/dev-utils/CHANGELOG.json +++ b/contracts/dev-utils/CHANGELOG.json @@ -4,7 +4,7 @@ "changes": [ { "note": "Create dev-utils package", - "pr": TODO + "pr": 1848 } ] } diff --git a/contracts/dev-utils/CHANGELOG.md b/contracts/dev-utils/CHANGELOG.md deleted file mode 100644 index 2eb59be5c7..0000000000 --- a/contracts/dev-utils/CHANGELOG.md +++ /dev/null @@ -1,99 +0,0 @@ - - -CHANGELOG - -## v3.1.5 - _May 24, 2019_ - - * Dependencies updated - -## v3.1.4 - _May 15, 2019_ - - * Dependencies updated - -## v3.1.3 - _May 14, 2019_ - - * Dependencies updated - -## v3.1.2 - _May 10, 2019_ - - * Dependencies updated - -## v3.1.1 - _April 11, 2019_ - - * Dependencies updated - -## v3.1.0 - _March 21, 2019_ - - * Run Web3ProviderEngine without excess block polling (#1695) - -## v3.0.0 - _March 20, 2019_ - - * Do not reexport external dependencies (#1682) - * Upgrade contracts to Solidity 0.5.5 (#1682) - -## v2.0.8 - _March 1, 2019_ - - * Dependencies updated - -## v2.0.7 - _February 27, 2019_ - - * Dependencies updated - -## v2.0.6 - _February 26, 2019_ - - * Dependencies updated - -## v2.0.5 - _February 25, 2019_ - - * Dependencies updated - -## v2.0.4 - _February 9, 2019_ - - * Dependencies updated - -## v2.0.3 - _February 7, 2019_ - - * Dependencies updated - -## v2.0.2 - _February 7, 2019_ - - * Fake publish to enable pinning - -## v2.0.1 - _February 6, 2019_ - - * Dependencies updated - -## v2.0.0 - _February 5, 2019_ - - * Upgrade the bignumber.js to v8.0.2 (#1517) - * Move Forwarder contract into new package (#1539) - * Nest extensions under src directory (#1539) - -## v1.2.3 - _January 17, 2019_ - - * Dependencies updated - -## v1.2.2 - _January 15, 2019_ - - * Dependencies updated - -## v1.2.1 - _January 11, 2019_ - - * Dependencies updated - -## v1.2.0 - _January 9, 2019_ - - * Added Dutch Auction Wrapper (#1465) - -## v1.1.0 - _Invalid date_ - - * Added Balance Threshold Filter (#1383) - * Add OrderMatcher (#1117) - * Add OrderValidator (#1464) - -## v1.0.2 - _December 13, 2018_ - - * Dependencies updated diff --git a/contracts/exchange-libs/CHANGELOG.json b/contracts/exchange-libs/CHANGELOG.json index 98dd96f4b3..015ab8be7a 100644 --- a/contracts/exchange-libs/CHANGELOG.json +++ b/contracts/exchange-libs/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "3.0.0", + "changes": [ + { + "note": "Move `LibTransactionDecoder` to contracts/dev-utils package", + "pr": 1848 + } + ] + }, { "timestamp": 1558712885, "version": "2.1.6", diff --git a/contracts/extensions/CHANGELOG.json b/contracts/extensions/CHANGELOG.json index 26103fe705..160807d275 100644 --- a/contracts/extensions/CHANGELOG.json +++ b/contracts/extensions/CHANGELOG.json @@ -3,12 +3,8 @@ "version": "4.0.0", "changes": [ { - "note": "Rename `OrderValidator` to `OrderValidationUtils`", - "pr": 1835 - }, - { - "note": "Create `DevUtils` contract", - "pr": 1835 + "note": "Move `OrderValidator` to contracts/dev-utils package as `OrderValidationUtils`", + "pr": 1848 } ] }, From c9ecef4fc385fb6538a89fd6d0569401b4215bd3 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Jun 2019 18:04:26 -0700 Subject: [PATCH 06/15] Cleanup tests --- .../dev-utils/test/order_validation_utils.ts | 285 +++++++----------- 1 file changed, 101 insertions(+), 184 deletions(-) diff --git a/contracts/dev-utils/test/order_validation_utils.ts b/contracts/dev-utils/test/order_validation_utils.ts index bff78732c0..4e9a423f79 100644 --- a/contracts/dev-utils/test/order_validation_utils.ts +++ b/contracts/dev-utils/test/order_validation_utils.ts @@ -130,16 +130,10 @@ describe('OrderValidationUtils', () => { it('should return the balance when balance < allowance', 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, - ); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, balance); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { + from: makerAddress, + }); const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync( erc20AssetData, makerAddress, @@ -149,16 +143,10 @@ describe('OrderValidationUtils', () => { it('should return the allowance when allowance < balance', async () => { const balance = new BigNumber(456); const allowance = new BigNumber(123); - 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, - ); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, balance); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { + from: makerAddress, + }); const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync( erc20AssetData, makerAddress, @@ -199,16 +187,10 @@ describe('OrderValidationUtils', () => { 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 web3Wrapper.awaitTransactionSuccessAsync( - await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { - from: makerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( signedOrder, signedOrder.signature, @@ -216,30 +198,21 @@ describe('OrderValidationUtils', () => { 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 web3Wrapper.awaitTransactionSuccessAsync( - await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { - from: makerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); + + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); 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, + + await zrxToken.setBalance.awaitTransactionSuccessAsync( + makerAddress, + signedOrder.makerFee.dividedToIntegerBy(divisor), ); + + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( signedOrder, signedOrder.signature, @@ -250,29 +223,21 @@ describe('OrderValidationUtils', () => { }); 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, + + await erc20Token.setBalance.awaitTransactionSuccessAsync( + makerAddress, + signedOrder.makerAssetAmount.dividedToIntegerBy(divisor), ); + + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + + await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); + + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( signedOrder, signedOrder.signature, @@ -282,16 +247,11 @@ describe('OrderValidationUtils', () => { ); }); 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, - ); + await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); + + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( signedOrder, signedOrder.signature, @@ -299,26 +259,17 @@ describe('OrderValidationUtils', () => { 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, - ); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); + + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + + await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); + + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( signedOrder, signedOrder.signature, @@ -333,16 +284,12 @@ describe('OrderValidationUtils', () => { 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, - ); + + await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, transferableMakerAssetAmount); + + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, transferableMakerAssetAmount, { + from: makerAddress, + }); const expectedFillableTakerAssetAmount = transferableMakerAssetAmount .times(signedOrder.takerAssetAmount) .dividedToIntegerBy(signedOrder.makerAssetAmount.plus(signedOrder.makerFee)); @@ -354,16 +301,12 @@ describe('OrderValidationUtils', () => { }); 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, - ); + + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); + + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( signedOrder, signedOrder.signature, @@ -371,46 +314,29 @@ describe('OrderValidationUtils', () => { 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, - ); - 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 erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); + + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + + await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); + + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); + + await erc20Token2.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerAssetAmount); + + await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerAssetAmount, { + from: takerAddress, + }); + + await zrxToken.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerFee); + + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerFee, { + from: takerAddress, + }); const takerAssetFillAmount = signedOrder.takerAssetAmount.dividedToIntegerBy(4); await exchange.fillOrder.awaitTransactionSuccessAsync( signedOrder, @@ -429,26 +355,17 @@ describe('OrderValidationUtils', () => { }); 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, - ); - 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 erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); + + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + + await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); + + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); const signedOrder2 = await orderFactory.newSignedOrderAsync({ makerAssetData: erc721AssetData }); const invalidSignature = '0x01'; await exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder2, { from: makerAddress }); From 23df406ff0dec78236e7e4777e8223ddcfd17949 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Jun 2019 20:49:16 -0700 Subject: [PATCH 07/15] Fix package.json typos --- contracts/dev-utils/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index a7c6450bc7..3e18dd94f4 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -4,7 +4,7 @@ "engines": { "node": ">=6.12" }, - "description": "Smart contract extensions of 0x protocol", + "description": "0x protocol specific utility contracts", "main": "lib/src/index.js", "directories": { "test": "test" @@ -45,7 +45,7 @@ "bugs": { "url": "https://github.com/0xProject/0x-monorepo/issues" }, - "homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md", + "homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md", "devDependencies": { "@0x/abi-gen": "^2.0.10", "@0x/contract-wrappers": "^9.1.4", From 12afeb30aed5c6f31eaa817348d1598fa42056c1 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 5 Jun 2019 09:06:01 -0700 Subject: [PATCH 08/15] Move LibAssetData to DevUtils package --- contracts/asset-proxy/compiler.json | 3 +-- contracts/asset-proxy/package.json | 2 +- contracts/asset-proxy/src/artifacts.ts | 2 -- contracts/asset-proxy/src/wrappers.ts | 1 - contracts/asset-proxy/tsconfig.json | 1 - contracts/dev-utils/compiler.json | 2 +- .../src/libs => dev-utils/contracts/src}/LibAssetData.sol | 2 +- contracts/dev-utils/package.json | 2 +- contracts/dev-utils/src/artifacts.ts | 2 ++ contracts/dev-utils/src/wrappers.ts | 1 + contracts/{asset-proxy => dev-utils}/test/lib_asset_data.ts | 0 contracts/dev-utils/tsconfig.json | 6 +++++- 12 files changed, 13 insertions(+), 11 deletions(-) rename contracts/{asset-proxy/contracts/src/libs => dev-utils/contracts/src}/LibAssetData.sol (99%) rename contracts/{asset-proxy => dev-utils}/test/lib_asset_data.ts (100%) diff --git a/contracts/asset-proxy/compiler.json b/contracts/asset-proxy/compiler.json index ddb26ea0e3..c09706f307 100644 --- a/contracts/asset-proxy/compiler.json +++ b/contracts/asset-proxy/compiler.json @@ -30,7 +30,6 @@ "src/MultiAssetProxy.sol", "src/interfaces/IAssetData.sol", "src/interfaces/IAssetProxy.sol", - "src/interfaces/IAuthorizable.sol", - "src/libs/LibAssetData.sol" + "src/interfaces/IAuthorizable.sol" ] } diff --git a/contracts/asset-proxy/package.json b/contracts/asset-proxy/package.json index 78a10f0a49..2bdba923c3 100644 --- a/contracts/asset-proxy/package.json +++ b/contracts/asset-proxy/package.json @@ -34,7 +34,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|LibAssetData|MixinAuthorizable|MultiAssetProxy).json", + "abis": "./generated-artifacts/@(ERC1155Proxy|ERC20Proxy|ERC721Proxy|IAssetData|IAssetProxy|IAuthorizable|MixinAuthorizable|MultiAssetProxy).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/asset-proxy/src/artifacts.ts b/contracts/asset-proxy/src/artifacts.ts index 9c82fe54ff..7fc2b38cda 100644 --- a/contracts/asset-proxy/src/artifacts.ts +++ b/contracts/asset-proxy/src/artifacts.ts @@ -11,11 +11,9 @@ import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json'; import * as IAssetData from '../generated-artifacts/IAssetData.json'; import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json'; import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json'; -import * as LibAssetData from '../generated-artifacts/LibAssetData.json'; import * as MixinAuthorizable from '../generated-artifacts/MixinAuthorizable.json'; import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json'; export const artifacts = { - LibAssetData: LibAssetData as ContractArtifact, ERC1155Proxy: ERC1155Proxy as ContractArtifact, ERC20Proxy: ERC20Proxy as ContractArtifact, ERC721Proxy: ERC721Proxy as ContractArtifact, diff --git a/contracts/asset-proxy/src/wrappers.ts b/contracts/asset-proxy/src/wrappers.ts index 2b7bf8bbde..3412cb0266 100644 --- a/contracts/asset-proxy/src/wrappers.ts +++ b/contracts/asset-proxy/src/wrappers.ts @@ -9,6 +9,5 @@ export * from '../generated-wrappers/erc721_proxy'; export * from '../generated-wrappers/i_asset_data'; export * from '../generated-wrappers/i_asset_proxy'; export * from '../generated-wrappers/i_authorizable'; -export * from '../generated-wrappers/lib_asset_data'; export * from '../generated-wrappers/mixin_authorizable'; export * from '../generated-wrappers/multi_asset_proxy'; diff --git a/contracts/asset-proxy/tsconfig.json b/contracts/asset-proxy/tsconfig.json index 809a8ed0da..31358217c9 100644 --- a/contracts/asset-proxy/tsconfig.json +++ b/contracts/asset-proxy/tsconfig.json @@ -9,7 +9,6 @@ "generated-artifacts/IAssetData.json", "generated-artifacts/IAssetProxy.json", "generated-artifacts/IAuthorizable.json", - "generated-artifacts/LibAssetData.json", "generated-artifacts/MixinAuthorizable.json", "generated-artifacts/MultiAssetProxy.json" ], diff --git a/contracts/dev-utils/compiler.json b/contracts/dev-utils/compiler.json index 1117c0be6b..eccc11fef8 100644 --- a/contracts/dev-utils/compiler.json +++ b/contracts/dev-utils/compiler.json @@ -22,5 +22,5 @@ } } }, - "contracts": ["src/DevUtils.sol", "src/LibTransactionDecoder.sol"] + "contracts": ["src/DevUtils.sol", "src/LibAssetData.sol", "src/LibTransactionDecoder.sol"] } diff --git a/contracts/asset-proxy/contracts/src/libs/LibAssetData.sol b/contracts/dev-utils/contracts/src/LibAssetData.sol similarity index 99% rename from contracts/asset-proxy/contracts/src/libs/LibAssetData.sol rename to contracts/dev-utils/contracts/src/LibAssetData.sol index 0b223cde77..f871626a2d 100644 --- a/contracts/asset-proxy/contracts/src/libs/LibAssetData.sol +++ b/contracts/dev-utils/contracts/src/LibAssetData.sol @@ -23,7 +23,7 @@ import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol"; -import "./LibAssetProxyIds.sol"; +import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetProxyIds.sol"; contract LibAssetData is diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index 3e18dd94f4..51b99ffa07 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -34,7 +34,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "./generated-artifacts/@(DevUtils|LibTransactionDecoder).json", + "abis": "./generated-artifacts/@(DevUtils|LibAssetData|LibTransactionDecoder).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/dev-utils/src/artifacts.ts b/contracts/dev-utils/src/artifacts.ts index 229b8eb26b..022784e6dc 100644 --- a/contracts/dev-utils/src/artifacts.ts +++ b/contracts/dev-utils/src/artifacts.ts @@ -6,8 +6,10 @@ import { ContractArtifact } from 'ethereum-types'; import * as DevUtils from '../generated-artifacts/DevUtils.json'; +import * as LibAssetData from '../generated-artifacts/LibAssetData.json'; import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json'; export const artifacts = { DevUtils: DevUtils as ContractArtifact, LibTransactionDecoder: LibTransactionDecoder as ContractArtifact, + LibAssetData: LibAssetData as ContractArtifact, }; diff --git a/contracts/dev-utils/src/wrappers.ts b/contracts/dev-utils/src/wrappers.ts index 78bc0dcfce..e07c65436d 100644 --- a/contracts/dev-utils/src/wrappers.ts +++ b/contracts/dev-utils/src/wrappers.ts @@ -4,4 +4,5 @@ * ----------------------------------------------------------------------------- */ export * from '../generated-wrappers/dev_utils'; +export * from '../generated-wrappers/lib_asset_data'; export * from '../generated-wrappers/lib_transaction_decoder'; diff --git a/contracts/asset-proxy/test/lib_asset_data.ts b/contracts/dev-utils/test/lib_asset_data.ts similarity index 100% rename from contracts/asset-proxy/test/lib_asset_data.ts rename to contracts/dev-utils/test/lib_asset_data.ts diff --git a/contracts/dev-utils/tsconfig.json b/contracts/dev-utils/tsconfig.json index eec1de9575..7a4c9021ed 100644 --- a/contracts/dev-utils/tsconfig.json +++ b/contracts/dev-utils/tsconfig.json @@ -2,6 +2,10 @@ "extends": "../../tsconfig", "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], - "files": ["generated-artifacts/DevUtils.json", "generated-artifacts/LibTransactionDecoder.json"], + "files": [ + "generated-artifacts/DevUtils.json", + "generated-artifacts/LibAssetData.json", + "generated-artifacts/LibTransactionDecoder.json" + ], "exclude": ["./deploy/solc/solc_bin"] } From be6fce5a891b33a048c5918c0a5ac075bcc09de5 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 5 Jun 2019 10:41:25 -0700 Subject: [PATCH 09/15] Refactor contracts to only use allowances for AssetProxies (spender is no longer an input) --- .../dev-utils/contracts/src/LibAssetData.sol | 371 ++++++++---------- .../contracts/src/OrderValidationUtils.sol | 127 +++--- 2 files changed, 217 insertions(+), 281 deletions(-) diff --git a/contracts/dev-utils/contracts/src/LibAssetData.sol b/contracts/dev-utils/contracts/src/LibAssetData.sol index f871626a2d..637d69e151 100644 --- a/contracts/dev-utils/contracts/src/LibAssetData.sol +++ b/contracts/dev-utils/contracts/src/LibAssetData.sol @@ -24,6 +24,7 @@ import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol"; import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetProxyIds.sol"; +import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; contract LibAssetData is @@ -33,68 +34,74 @@ contract LibAssetData is using LibBytes for bytes; - /// @dev Returns the owner's balance of the token(s) specified in - /// assetData. When the asset data contains multiple tokens (eg in - /// ERC1155 or Multi-Asset), the return value indicates how many - /// complete "baskets" of those tokens are owned by owner. - /// @param owner Owner of the tokens specified by assetData. - /// @param assetData Description of tokens, per the AssetProxy contract - /// specification. - /// @return Number of tokens (or token baskets) held by owner. - function getBalance(address owner, bytes memory assetData) + // solhint-disable var-name-mixedcase + IExchange internal _EXCHANGE; + address internal _ERC20_PROXY_ADDRESS; + address internal _ERC721_PROXY_ADDRESS; + address internal _ERC1155_PROXY_ADDRESS; + // solhint-enable var-name-mixedcase + + constructor (address _exchange) + public + { + _EXCHANGE = IExchange(_exchange); + _ERC20_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC20_PROXY_ID); + _ERC721_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC721_PROXY_ID); + _ERC1155_PROXY_ADDRESS = _EXCHANGE.getAssetProxy(ERC1155_PROXY_ID); + } + + /// @dev Returns the owner's balance of the assets(s) specified in + /// assetData. When the asset data contains multiple assets (eg in + /// ERC1155 or Multi-Asset), the return value indicates how many + /// complete "baskets" of those assets are owned by owner. + /// @param ownerAddress Owner of the assets specified by assetData. + /// @param assetData Details of asset, encoded per the AssetProxy contract specification. + /// @return Number of assets (or asset baskets) held by owner. + function getBalance(address ownerAddress, bytes memory assetData) public view returns (uint256 balance) { - bytes4 proxyId = assetData.readBytes4(0); + bytes4 assetProxyId = assetData.readBytes4(0); - if (proxyId == ERC20_PROXY_ID) { + if (assetProxyId == ERC20_PROXY_ID) { address tokenAddress = assetData.readAddress(16); - balance = IERC20Token(tokenAddress).balanceOf(owner); - } else if (proxyId == ERC721_PROXY_ID) { + balance = IERC20Token(tokenAddress).balanceOf(ownerAddress); + } else if (assetProxyId == ERC721_PROXY_ID) { (, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData); - balance = getERC721TokenOwner(tokenAddress, tokenId) == owner ? 1 : 0; - } else if (proxyId == ERC1155_PROXY_ID) { - ( - , - address tokenAddress, - uint256[] memory tokenIds, - uint256[] memory tokenValues, - ) = decodeERC1155AssetData(assetData); - for (uint256 i = 0; i < tokenIds.length; i++) { - uint256 totalBalance = IERC1155(tokenAddress).balanceOf(owner, tokenIds[i]); + balance = getERC721TokenOwner(tokenAddress, tokenId) == ownerAddress ? 1 : 0; + } else if (assetProxyId == ERC1155_PROXY_ID) { + (, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData); + uint256 length = tokenIds.length; + for (uint256 i = 0; i != length; i++) { + uint256 totalBalance = IERC1155(tokenAddress).balanceOf(ownerAddress, tokenIds[i]); uint256 scaledBalance = totalBalance / tokenValues[i]; if (scaledBalance < balance || balance == 0) { balance = scaledBalance; } } - } else if (proxyId == MULTI_ASSET_PROXY_ID) { - ( - , - uint256[] memory assetAmounts, - bytes[] memory nestedAssetData - ) = decodeMultiAssetData(assetData); - for (uint256 i = 0; i < nestedAssetData.length; i++) { - uint256 totalBalance = getBalance(owner, nestedAssetData[i]); + } else if (assetProxyId == MULTI_ASSET_PROXY_ID) { + (, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData); + uint256 length = nestedAssetData.length; + for (uint256 i = 0; i != length; i++) { + uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]); uint256 scaledBalance = totalBalance / assetAmounts[i]; if (scaledBalance < balance || balance == 0) { balance = scaledBalance; } } - } else { - revert("UNSUPPORTED_PROXY_ID"); - } + } + // Balance will be 0 if assetProxyId is unknown return balance; } /// @dev Calls getBalance() for each element of assetData. - /// @param owner Owner of the tokens specified by assetData. - /// @param assetData Array of token descriptors, each encoded per the - /// AssetProxy contract specification. - /// @return Array of token balances from getBalance(), with each element - /// corresponding to the same-indexed element in the assetData input. - function getBatchBalances(address owner, bytes[] memory assetData) + /// @param ownerAddress Owner of the assets specified by assetData. + /// @param assetData Array of asset details, each encoded per the AssetProxy contract specification. + /// @return Array of asset balances from getBalance(), with each element + /// corresponding to the same-indexed element in the assetData input. + function getBatchBalances(address ownerAddress, bytes[] memory assetData) public view returns (uint256[] memory balances) @@ -102,83 +109,65 @@ contract LibAssetData is uint256 length = assetData.length; balances = new uint256[](length); for (uint256 i = 0; i != length; i++) { - balances[i] = getBalance(owner, assetData[i]); + balances[i] = getBalance(ownerAddress, assetData[i]); } return balances; } - /// @dev Returns the number of token(s) (described by assetData) that - /// spender is authorized to spend. When the asset data contains - /// multiple tokens (eg for Multi-Asset), the return value indicates - /// how many complete "baskets" of those tokens may be spent by spender. - /// @param owner Owner of the tokens specified by assetData. - /// @param spender Address whose authority to spend is in question. - /// @param assetData Description of tokens, per the AssetProxy contract - /// specification. - /// @return Number of tokens (or token baskets) that the spender is - /// authorized to spend. - function getAllowance( - address owner, - address spender, - bytes memory assetData - ) + /// @dev Returns the number of asset(s) (described by assetData) that + /// the corresponding AssetProxy contract is authorized to spend. When the asset data contains + /// multiple assets (eg for Multi-Asset), the return value indicates + /// how many complete "baskets" of those assets may be spent by all of the corresponding + /// AssetProxy contracts. + /// @param ownerAddress Owner of the assets specified by assetData. + /// @param assetData Details of asset, encoded per the AssetProxy contract specification. + /// @return Number of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend. + function getAssetProxyAllowance(address ownerAddress, bytes memory assetData) public view returns (uint256 allowance) { - bytes4 proxyId = assetData.readBytes4(0); + bytes4 assetProxyId = assetData.readBytes4(0); - if (proxyId == ERC20_PROXY_ID) { - address tokenAddress = assetData.readAddress(16); - allowance = IERC20Token(tokenAddress).allowance(owner, spender); - } else if (proxyId == ERC721_PROXY_ID) { - (, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData); - IERC721Token token = IERC721Token(tokenAddress); - if (token.isApprovedForAll(owner, spender)) { - allowance = _MAX_UINT256; - } else if (token.getApproved(tokenId) == spender) { - allowance = 1; - } - } else if (proxyId == ERC1155_PROXY_ID) { - (, address tokenAddress, , , ) = decodeERC1155AssetData(assetData); - allowance = IERC1155(tokenAddress).isApprovedForAll(owner, spender) ? _MAX_UINT256 : 0; - } else if (proxyId == MULTI_ASSET_PROXY_ID) { - ( - , - uint256[] memory amounts, - bytes[] memory nestedAssetData - ) = decodeMultiAssetData(assetData); - for (uint256 i = 0; i < nestedAssetData.length; i++) { - uint256 totalAllowance = getAllowance( - owner, - spender, - nestedAssetData[i] - ); + if (assetProxyId == MULTI_ASSET_PROXY_ID) { + (, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData); + uint256 length = nestedAssetData.length; + for (uint256 i = 0; i != length; i++) { + uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]); uint256 scaledAllowance = totalAllowance / amounts[i]; if (scaledAllowance < allowance || allowance == 0) { allowance = scaledAllowance; } } - } else { - revert("UNSUPPORTED_PROXY_ID"); } + if (assetProxyId == ERC20_PROXY_ID) { + address tokenAddress = assetData.readAddress(16); + allowance = IERC20Token(tokenAddress).allowance(ownerAddress, _ERC20_PROXY_ADDRESS); + } else if (assetProxyId == ERC721_PROXY_ID) { + (, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData); + IERC721Token token = IERC721Token(tokenAddress); + address assetProxyAddress = _ERC721_PROXY_ADDRESS; + if (token.isApprovedForAll(ownerAddress, assetProxyAddress)) { + allowance = _MAX_UINT256; + } else if (token.getApproved(tokenId) == assetProxyAddress) { + allowance = 1; + } + } else if (assetProxyId == ERC1155_PROXY_ID) { + (, address tokenAddress, , , ) = decodeERC1155AssetData(assetData); + allowance = IERC1155(tokenAddress).isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS) ? _MAX_UINT256 : 0; + } + + // Allowance will be 0 if the assetProxyId is unknown return allowance; } - /// @dev Calls getAllowance() for each element of assetData. - /// @param owner Owner of the tokens specified by assetData. - /// @param spenders Array of addresses whose authority to spend is in question for each corresponding assetData. - /// @param assetData Description of tokens, per the AssetProxy contract - /// specification. - /// @return An array of token allowances from getAllowance(), with each - /// element corresponding to the same-indexed element in the assetData - /// input. - function getBatchAllowances( - address owner, - address[] memory spenders, - bytes[] memory assetData - ) + /// @dev Calls getAssetProxyAllowance() for each element of assetData. + /// @param ownerAddress Owner of the assets specified by assetData. + /// @param assetData Array of asset details, each encoded per the AssetProxy contract specification. + /// @return An array of asset allowances from getAllowance(), with each + /// element corresponding to the same-indexed element in the assetData input. + function getBatchAssetProxyAllowances(address ownerAddress, bytes[] memory assetData) public view returns (uint256[] memory allowances) @@ -186,63 +175,44 @@ contract LibAssetData is uint256 length = assetData.length; allowances = new uint256[](length); for (uint256 i = 0; i != length; i++) { - allowances[i] = getAllowance(owner, spenders[i], assetData[i]); + allowances[i] = getAssetProxyAllowance(ownerAddress, assetData[i]); } return allowances; } /// @dev Calls getBalance() and getAllowance() for assetData. - /// @param owner Owner of the tokens specified by assetData. - /// @param spender Address whose authority to spend is in question. - /// @param assetData Description of tokens, per the AssetProxy contract - /// specification. - /// @return Number of tokens (or token baskets) held by owner, and number - /// of tokens (or token baskets) that the spender is authorized to - /// spend. - function getBalanceAndAllowance( - address owner, - address spender, - bytes memory assetData - ) + /// @param ownerAddress Owner of the assets specified by assetData. + /// @param assetData Details of asset, encoded per the AssetProxy contract specification. + /// @return Number of assets (or asset baskets) held by owner, and number + /// of assets (or asset baskets) that the corresponding AssetProxy is authorized to spend. + function getBalanceAndAssetProxyAllowance(address ownerAddress, bytes memory assetData) public view returns (uint256 balance, uint256 allowance) { - balance = getBalance(owner, assetData); - allowance = getAllowance(owner, spender, assetData); + balance = getBalance(ownerAddress, assetData); + allowance = getAssetProxyAllowance(ownerAddress, assetData); return (balance, allowance); } - /// @dev Calls getBatchBalances() and getBatchAllowances() for each element - /// of assetData. - /// @param owner Owner of the tokens specified by assetData. - /// @param spenders Array of addresses whose authority to spend is in question for each corresponding assetData. - /// @param assetData Description of tokens, per the AssetProxy contract - /// specification. - /// @return An array of token balances from getBalance(), and an array of - /// token allowances from getAllowance(), with each element - /// corresponding to the same-indexed element in the assetData input. - function getBatchBalancesAndAllowances( - address owner, - address[] memory spenders, - bytes[] memory assetData - ) + /// @dev Calls getBatchBalances() and getBatchAllowances() for each element of assetData. + /// @param ownerAddress Owner of the assets specified by assetData. + /// @param assetData Array of asset details, each encoded per the AssetProxy contract specification. + /// @return An array of asset balances from getBalance(), and an array of + /// asset allowances from getAllowance(), with each element + /// corresponding to the same-indexed element in the assetData input. + function getBatchBalancesAndAssetProxyAllowances(address ownerAddress, bytes[] memory assetData) public view - returns ( - uint256[] memory balances, - uint256[] memory allowances - ) + returns (uint256[] memory balances, uint256[] memory allowances) { - balances = getBatchBalances(owner, assetData); - allowances = getBatchAllowances(owner, spenders, assetData); + balances = getBatchBalances(ownerAddress, assetData); + allowances = getBatchAssetProxyAllowances(ownerAddress, assetData); return (balances, allowances); } - /// @dev Encode ERC-20 asset data into the format described in the - /// AssetProxy contract specification. - /// @param tokenAddress The address of the ERC-20 contract hosting the - /// token to be traded. + /// @dev Encode ERC-20 asset data into the format described in the AssetProxy contract specification. + /// @param tokenAddress The address of the ERC-20 contract hosting the asset to be traded. /// @return AssetProxy-compliant data describing the asset. function encodeERC20AssetData(address tokenAddress) public @@ -253,41 +223,34 @@ contract LibAssetData is return assetData; } - /// @dev Decode ERC-20 asset data from the format described in the - /// AssetProxy contract specification. - /// @param assetData AssetProxy-compliant asset data describing an ERC-20 - /// asset. - /// @return The ERC-20 AssetProxy identifier, and the address of the ERC-20 - /// contract hosting this asset. + /// @dev Decode ERC-20 asset data from the format described in the AssetProxy contract specification. + /// @param assetData AssetProxy-compliant asset data describing an ERC-20 asset. + /// @return The ERC-20 AssetProxy identifier, and the address of the ERC-20 + /// contract hosting this asset. function decodeERC20AssetData(bytes memory assetData) public pure returns ( - bytes4 proxyId, + bytes4 assetProxyId, address tokenAddress ) { - proxyId = assetData.readBytes4(0); + assetProxyId = assetData.readBytes4(0); require( - proxyId == ERC20_PROXY_ID, + assetProxyId == ERC20_PROXY_ID, "WRONG_PROXY_ID" ); tokenAddress = assetData.readAddress(16); - return (proxyId, tokenAddress); + return (assetProxyId, tokenAddress); } - /// @dev Encode ERC-721 asset data into the format described in the - /// AssetProxy specification. - /// @param tokenAddress The address of the ERC-721 contract hosting the - /// token to be traded. - /// @param tokenId The identifier of the specific token to be traded. + /// @dev Encode ERC-721 asset data into the format described in the AssetProxy specification. + /// @param tokenAddress The address of the ERC-721 contract hosting the asset to be traded. + /// @param tokenId The identifier of the specific asset to be traded. /// @return AssetProxy-compliant asset data describing the asset. - function encodeERC721AssetData( - address tokenAddress, - uint256 tokenId - ) + function encodeERC721AssetData(address tokenAddress, uint256 tokenId) public pure returns (bytes memory assetData) @@ -300,41 +263,37 @@ contract LibAssetData is return assetData; } - /// @dev Decode ERC-721 asset data from the format described in the - /// AssetProxy contract specification. - /// @param assetData AssetProxy-compliant asset data describing an ERC-721 - /// asset. + /// @dev Decode ERC-721 asset data from the format described in the AssetProxy contract specification. + /// @param assetData AssetProxy-compliant asset data describing an ERC-721 asset. /// @return The ERC-721 AssetProxy identifier, the address of the ERC-721 - /// contract hosting this asset, and the identifier of the specific - /// token to be traded. + /// contract hosting this asset, and the identifier of the specific + /// asset to be traded. function decodeERC721AssetData(bytes memory assetData) public pure returns ( - bytes4 proxyId, + bytes4 assetProxyId, address tokenAddress, uint256 tokenId ) { - proxyId = assetData.readBytes4(0); + assetProxyId = assetData.readBytes4(0); require( - proxyId == ERC721_PROXY_ID, + assetProxyId == ERC721_PROXY_ID, "WRONG_PROXY_ID" ); tokenAddress = assetData.readAddress(16); tokenId = assetData.readUint256(36); - return (proxyId, tokenAddress, tokenId); + return (assetProxyId, tokenAddress, tokenId); } - /// @dev Encode ERC-1155 asset data into the format described in the - /// AssetProxy contract specification. - /// @param tokenAddress The address of the ERC-1155 contract hosting the - /// token(s) to be traded. - /// @param tokenIds The identifiers of the specific tokens to be traded. - /// @param tokenValues The amounts of each token to be traded. - /// @param callbackData ... + /// @dev Encode ERC-1155 asset data into the format described in the AssetProxy contract specification. + /// @param tokenAddress The address of the ERC-1155 contract hosting the asset(s) to be traded. + /// @param tokenIds The identifiers of the specific assets to be traded. + /// @param tokenValues The amounts of each asset to be traded. + /// @param callbackData Data to be passed to receiving contracts when a transfer is performed. /// @return AssetProxy-compliant asset data describing the set of assets. function encodeERC1155AssetData( address tokenAddress, @@ -356,32 +315,30 @@ contract LibAssetData is return assetData; } - /// @dev Decode ERC-1155 asset data from the format described in the - /// AssetProxy contract specification. - /// @param assetData AssetProxy-compliant asset data describing an ERC-1155 - /// set of assets. + /// @dev Decode ERC-1155 asset data from the format described in the AssetProxy contract specification. + /// @param assetData AssetProxy-compliant asset data describing an ERC-1155 set of assets. /// @return The ERC-1155 AssetProxy identifier, the address of the ERC-1155 - /// contract hosting the assets, an array of the identifiers of the - /// tokens to be traded, an array of token amounts to be traded, and - /// callback data. Each element of the arrays corresponds to the - /// same-indexed element of the other array. Return values specified as - /// `memory` are returned as pointers to locations within the memory of - /// the input parameter `assetData`. + /// contract hosting the assets, an array of the identifiers of the + /// assets to be traded, an array of asset amounts to be traded, and + /// callback data. Each element of the arrays corresponds to the + /// same-indexed element of the other array. Return values specified as + /// `memory` are returned as pointers to locations within the memory of + /// the input parameter `assetData`. function decodeERC1155AssetData(bytes memory assetData) public pure returns ( - bytes4 proxyId, + bytes4 assetProxyId, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues, bytes memory callbackData ) { - proxyId = assetData.readBytes4(0); + assetProxyId = assetData.readBytes4(0); require( - proxyId == ERC1155_PROXY_ID, + assetProxyId == ERC1155_PROXY_ID, "WRONG_PROXY_ID" ); @@ -399,7 +356,7 @@ contract LibAssetData is } return ( - proxyId, + assetProxyId, tokenAddress, tokenIds, tokenValues, @@ -407,16 +364,11 @@ contract LibAssetData is ); } - /// @dev Encode data for multiple assets, per the AssetProxy contract - /// specification. + /// @dev Encode data for multiple assets, per the AssetProxy contract specification. /// @param amounts The amounts of each asset to be traded. - /// @param nestedAssetData AssetProxy-compliant data describing each asset - /// to be traded. + /// @param nestedAssetData AssetProxy-compliant data describing each asset to be traded. /// @return AssetProxy-compliant data describing the set of assets. - function encodeMultiAssetData( - uint256[] memory amounts, - bytes[] memory nestedAssetData - ) + function encodeMultiAssetData(uint256[] memory amounts, bytes[] memory nestedAssetData) public pure returns (bytes memory assetData) @@ -429,28 +381,25 @@ contract LibAssetData is return assetData; } - /// @dev Decode multi-asset data from the format described in the - /// AssetProxy contract specification. - /// @param assetData AssetProxy-compliant data describing a multi-asset - /// basket. + /// @dev Decode multi-asset data from the format described in the AssetProxy contract specification. + /// @param assetData AssetProxy-compliant data describing a multi-asset basket. /// @return The Multi-Asset AssetProxy identifier, an array of the amounts - /// of the assets to be traded, and an array of the - /// AssetProxy-compliant data describing each asset to be traded. Each - /// element of the arrays corresponds to the same-indexed element of - /// the other array. + /// of the assets to be traded, and an array of the + /// AssetProxy-compliant data describing each asset to be traded. Each + /// element of the arrays corresponds to the same-indexed element of the other array. function decodeMultiAssetData(bytes memory assetData) public pure returns ( - bytes4 proxyId, + bytes4 assetProxyId, uint256[] memory amounts, bytes[] memory nestedAssetData ) { - proxyId = assetData.readBytes4(0); + assetProxyId = assetData.readBytes4(0); require( - proxyId == MULTI_ASSET_PROXY_ID, + assetProxyId == MULTI_ASSET_PROXY_ID, "WRONG_PROXY_ID" ); @@ -462,14 +411,14 @@ contract LibAssetData is // solhint-enable indent } - /// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token. - /// @param tokenAddress Address of ERC721 token. + /// @dev Calls `asset.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned asset. + /// @param tokenAddress Address of ERC721 asset. /// @param tokenId The identifier for the specific NFT. /// @return Owner of tokenId or null address if unowned. function getERC721TokenOwner(address tokenAddress, uint256 tokenId) public view - returns (address owner) + returns (address ownerAddress) { bytes memory ownerOfCalldata = abi.encodeWithSelector( 0x6352211e, @@ -478,7 +427,7 @@ contract LibAssetData is (bool success, bytes memory returnData) = tokenAddress.staticcall(ownerOfCalldata); - owner = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0); - return owner; + ownerAddress = (success && returnData.length == 32) ? returnData.readAddress(12) : address(0); + return ownerAddress; } } diff --git a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol index 8882b9f4f3..6bcf6bae7a 100644 --- a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol +++ b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol @@ -23,7 +23,7 @@ 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"; +import "./LibAssetData.sol"; contract OrderValidationUtils is @@ -33,23 +33,26 @@ contract OrderValidationUtils is using LibBytes for bytes; // solhint-disable var-name-mixedcase - IExchange internal EXCHANGE; - bytes internal ZRX_ASSET_DATA; - address internal ERC20_PROXY_ADDRESS; + bytes internal _ZRX_ASSET_DATA; // solhint-enable var-name-mixedcase constructor (address _exchange, bytes memory _zrxAssetData) public + LibAssetData(_exchange) { - EXCHANGE = IExchange(_exchange); - ZRX_ASSET_DATA = _zrxAssetData; - ERC20_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC20_PROXY_ID); + _ZRX_ASSET_DATA = _zrxAssetData; } - /// @dev Fetches information for order and maker/taker of order. + /// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable. /// @param order The order structure. - /// @param signature Proof that order has been created by maker. - /// @return OrderInfo, the remaining amount fillable by the taker, and validity of signature for given order. + /// @param signature Signature provided by maker that proves the order's authenticity. + /// `0x01` can always be provided if the signature does not need to be validated. + /// @return orderInfo The hash, status, and `takerAssetAmount` already filled for the given order. + /// @return fillableTakerAssetAmount The amount of the order's `takerAssetAmount` that is fillable given all on-chain state. + /// If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled" + /// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final + /// amount of each asset that can be filled. + /// @return isValidSignature The validity of the provided signature. function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature) public view @@ -60,74 +63,80 @@ contract OrderValidationUtils is ) { // 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, makerAddress, signature ); // Get the transferable amount of the `makerAsset` - uint256 transferableMakerAssetAmount = getTransferableAssetAmount(order.makerAssetData, makerAddress); + uint256 transferableMakerAssetAmount = getTransferableAssetAmount(makerAddress, order.makerAssetData); - // Assign to stack variables to reduce redundant mloads + // Assign to stack variables to reduce redundant mloads/sloads uint256 takerAssetAmount = order.takerAssetAmount; uint256 makerFee = order.makerFee; + bytes memory zrxAssetData = _ZRX_ASSET_DATA; - // Get the amount of `takerAsset` that is purchasable given the transferability of `makerAsset` and `makerFeeAsset` - uint256 purchasableTakerAssetAmount; - if (order.makerAssetData.equals(ZRX_ASSET_DATA)) { + // Get the amount of `takerAsset` that is transferable to maker given the transferability of `makerAsset` and `makerFeeAsset` + // and the total amounts specified in the order + uint256 transferableTakerAssetAmount; + if (order.makerAssetData.equals(zrxAssetData)) { // If `makerAsset` equals `makerFeeAsset`, the % that can be filled is // transferableMakerAssetAmount / (makerAssetAmount + makerFee) - purchasableTakerAssetAmount = getPartialAmountFloor( + transferableTakerAssetAmount = getPartialAmountFloor( transferableMakerAssetAmount, safeAdd(order.makerAssetAmount, makerFee), takerAssetAmount ); } else { // Get the transferable amount of the `makerFeeAsset` - uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(ZRX_ASSET_DATA, makerAddress); + uint256 transferableMakerFeeAssetAmount = getTransferableAssetAmount(makerAddress, zrxAssetData); - // 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( + // If `makerFee` is 0, the % that can be filled is (transferableMakerAssetAmount / makerAssetAmount) + if (makerFee == 0) { + transferableTakerAssetAmount = getPartialAmountFloor( transferableMakerAssetAmount, order.makerAssetAmount, takerAssetAmount - ) - : min256( - getPartialAmountFloor( - transferableMakerAssetAmount, - order.makerAssetAmount, - takerAssetAmount - ), - getPartialAmountFloor( - transferableMakerFeeAssetAmount, - makerFee, - takerAssetAmount - ) ); + + // If `makerAsset` does not equal `makerFeeAsset`, the % that can be filled is the lower of + // (transferableMakerAssetAmount / makerAssetAmount) and (transferableMakerAssetFeeAmount / makerFee) + } else { + uint256 transferableMakerToTakerAmount = getPartialAmountFloor( + transferableMakerAssetAmount, + order.makerAssetAmount, + takerAssetAmount + ); + uint256 transferableMakerFeeToTakerAmount = getPartialAmountFloor( + transferableMakerFeeAssetAmount, + makerFee, + takerAssetAmount + ); + transferableTakerAssetAmount = min256(transferableMakerToTakerAmount, transferableMakerFeeToTakerAmount); + } } - - // `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `purchasableTakerAssetAmount` + + // `fillableTakerAssetAmount` is the lower of the order's remaining `takerAssetAmount` and the `transferableTakerAssetAmount` fillableTakerAssetAmount = min256( safeSub(takerAssetAmount, orderInfo.orderTakerAssetFilledAmount), - purchasableTakerAssetAmount + transferableTakerAssetAmount ); 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. - /// @return Arrays of OrderInfo, fillable takerAssetAmounts, and validity of signatures that correspond to each order. + /// @dev Fetches all order-relevant information needed to validate if the supplied orders are fillable. + /// @param orders Array of order structures. + /// @param signatures Array of signatures provided by makers that prove the authenticity of the orders. + /// `0x01` can always be provided if a signature does not need to be validated. + /// @return ordersInfo Array of the hash, status, and `takerAssetAmount` already filled for each order. + /// @return fillableTakerAssetAmounts Array of amounts for each order's `takerAssetAmount` that is fillable given all on-chain state. + /// @return isValidSignature Array containing the validity of each provided signature. function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures) public view @@ -152,39 +161,17 @@ contract OrderValidationUtils is return (ordersInfo, fillableTakerAssetAmounts, isValidSignature); } - /// @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 (address assetProxyAddress) - { - if (assetData.equals(ZRX_ASSET_DATA)) { - return ERC20_PROXY_ADDRESS; - } - 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. + /// @param assetData Description of tokens, per the AssetProxy contract specification. /// @return The amount of the asset tranferable by the owner. - function getTransferableAssetAmount(bytes memory assetData, address ownerAddress) + function getTransferableAssetAmount(address ownerAddress, bytes memory assetData) public view returns (uint256 transferableAssetAmount) { - uint256 assetBalance = getBalance(ownerAddress, assetData); - address assetProxyAddress = getAssetProxyAddress(assetData); - uint256 assetAllowance = getAllowance( - ownerAddress, - assetProxyAddress, - assetData - ); - transferableAssetAmount = min256(assetBalance, assetAllowance); + (uint256 balance, uint256 allowance) = getBalanceAndAssetProxyAllowance(ownerAddress, assetData); + transferableAssetAmount = min256(balance, allowance); return transferableAssetAmount; } } From d9edb9675f1247f0cf86659e3783cc388b0eedee Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 5 Jun 2019 14:30:27 -0700 Subject: [PATCH 10/15] Add tests for multiAssetData, remove tests for old functions --- .../contracts/src/OrderValidationUtils.sol | 2 +- .../dev-utils/test/order_validation_utils.ts | 131 ++++++++++++------ 2 files changed, 91 insertions(+), 42 deletions(-) diff --git a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol index 6bcf6bae7a..c30ea13d1a 100644 --- a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol +++ b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol @@ -81,7 +81,7 @@ contract OrderValidationUtils is uint256 makerFee = order.makerFee; bytes memory zrxAssetData = _ZRX_ASSET_DATA; - // Get the amount of `takerAsset` that is transferable to maker given the transferability of `makerAsset` and `makerFeeAsset` + // Get the amount of `takerAsset` that is transferable to maker given the transferability of `makerAsset`, `makerFeeAsset`, // and the total amounts specified in the order uint256 transferableTakerAssetAmount; if (order.makerAssetData.equals(zrxAssetData)) { diff --git a/contracts/dev-utils/test/order_validation_utils.ts b/contracts/dev-utils/test/order_validation_utils.ts index 4e9a423f79..d03cefc18e 100644 --- a/contracts/dev-utils/test/order_validation_utils.ts +++ b/contracts/dev-utils/test/order_validation_utils.ts @@ -1,4 +1,11 @@ -import { ERC20ProxyContract, ERC20Wrapper, ERC721ProxyContract, ERC721Wrapper } from '@0x/contracts-asset-proxy'; +import { + artifacts as proxyArtifacts, + ERC20ProxyContract, + ERC20Wrapper, + ERC721ProxyContract, + ERC721Wrapper, + MultiAssetProxyContract, +} from '@0x/contracts-asset-proxy'; import { DummyERC20TokenContract } from '@0x/contracts-erc20'; import { DummyERC721TokenContract } from '@0x/contracts-erc721'; import { artifacts as exchangeArtifacts, ExchangeContract, ExchangeWrapper } from '@0x/contracts-exchange'; @@ -40,6 +47,7 @@ describe('OrderValidationUtils', () => { let devUtils: DevUtilsContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; + let multiAssetProxy: MultiAssetProxyContract; let signedOrder: SignedOrder; let orderFactory: OrderFactory; @@ -77,9 +85,16 @@ describe('OrderValidationUtils', () => { txDefaults, zrxAssetData, ); + + multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync( + proxyArtifacts.MultiAssetProxy, + provider, + txDefaults, + ); const exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, owner); await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner }); await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner }); @@ -113,19 +128,6 @@ describe('OrderValidationUtils', () => { await blockchainLifecycle.revertAsync(); }); - 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); - }); - 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('getTransferableAssetAmount', () => { it('should return the balance when balance < allowance', async () => { const balance = new BigNumber(123); @@ -135,8 +137,8 @@ describe('OrderValidationUtils', () => { from: makerAddress, }); const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync( - erc20AssetData, makerAddress, + erc20AssetData, ); expect(transferableAmount).to.bignumber.equal(balance); }); @@ -148,11 +150,32 @@ describe('OrderValidationUtils', () => { from: makerAddress, }); const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync( - erc20AssetData, makerAddress, + erc20AssetData, ); expect(transferableAmount).to.bignumber.equal(allowance); }); + it('should return the correct transferable amount for multiAssetData', async () => { + const multiAssetData = assetDataUtils.encodeMultiAssetData( + [new BigNumber(1), new BigNumber(1)], + [erc20AssetData, erc20AssetData2], + ); + const transferableAmount1 = new BigNumber(10); + const transferableAmount2 = new BigNumber(5); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, transferableAmount1); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, transferableAmount1, { + from: makerAddress, + }); + await erc20Token2.setBalance.awaitTransactionSuccessAsync(makerAddress, transferableAmount2); + await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, transferableAmount2, { + from: makerAddress, + }); + const transferableAmount = await devUtils.getTransferableAssetAmount.callAsync( + makerAddress, + multiAssetData, + ); + expect(transferableAmount).to.bignumber.equal(transferableAmount2); + }); }); describe('getOrderRelevantState', () => { beforeEach(async () => { @@ -179,7 +202,7 @@ describe('OrderValidationUtils', () => { ); expect(isValidSignature).to.equal(false); }); - it('should return a fillableTakerAssetAmount of 0 when no balances or allowances are insufficient', async () => { + it('should return a fillableTakerAssetAmount of 0 when balances or allowances are insufficient', async () => { const [, fillableTakerAssetAmount] = await devUtils.getOrderRelevantState.callAsync( signedOrder, signedOrder.signature, @@ -197,19 +220,32 @@ describe('OrderValidationUtils', () => { ); expect(fillableTakerAssetAmount).to.bignumber.equal(constants.ZERO_AMOUNT); }); + it('should return a fillableTakerAssetAmount of 0 when balances/allowances of one asset within a multiAssetData are insufficient', async () => { + const multiAssetData = assetDataUtils.encodeMultiAssetData( + [new BigNumber(1), new BigNumber(1)], + [erc20AssetData, erc20AssetData2], + ); + signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: multiAssetData }); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + 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 erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { from: makerAddress, }); const divisor = 4; - await zrxToken.setBalance.awaitTransactionSuccessAsync( makerAddress, signedOrder.makerFee.dividedToIntegerBy(divisor), ); - await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { from: makerAddress, }); @@ -223,18 +259,14 @@ describe('OrderValidationUtils', () => { }); it('should return the correct fillableTakerAssetAmount when non-fee balances/allowances are partially sufficient', async () => { const divisor = 4; - await erc20Token.setBalance.awaitTransactionSuccessAsync( makerAddress, signedOrder.makerAssetAmount.dividedToIntegerBy(divisor), ); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { from: makerAddress, }); - await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); - await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { from: makerAddress, }); @@ -246,9 +278,42 @@ describe('OrderValidationUtils', () => { signedOrder.takerAssetAmount.dividedToIntegerBy(divisor), ); }); + it('should return the correct fillableTakerAssetAmount when balances/allowances of one asset within a multiAssetData are partially sufficient', async () => { + const multiAssetData = assetDataUtils.encodeMultiAssetData( + [new BigNumber(1), new BigNumber(1)], + [erc20AssetData, erc20AssetData2], + ); + signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: multiAssetData }); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); + await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); + const divisor = 4; + await erc20Token2.setBalance.awaitTransactionSuccessAsync( + makerAddress, + signedOrder.makerAssetAmount.dividedToIntegerBy(divisor), + ); + await erc20Token2.approve.awaitTransactionSuccessAsync( + erc20Proxy.address, + signedOrder.makerAssetAmount.dividedToIntegerBy(divisor), + { + from: makerAddress, + }, + ); + 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 zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); - await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { from: makerAddress, }); @@ -260,13 +325,10 @@ describe('OrderValidationUtils', () => { }); it('should return a fillableTakerAssetAmount equal to the takerAssetAmount when the order is unfilled and balances/allowances are sufficient', async () => { await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { from: makerAddress, }); - await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); - await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { from: makerAddress, }); @@ -284,9 +346,7 @@ describe('OrderValidationUtils', () => { makerFee: new BigNumber(40), }); const transferableMakerAssetAmount = new BigNumber(10); - await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, transferableMakerAssetAmount); - await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, transferableMakerAssetAmount, { from: makerAddress, }); @@ -301,9 +361,7 @@ describe('OrderValidationUtils', () => { }); 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 erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { from: makerAddress, }); @@ -315,23 +373,17 @@ describe('OrderValidationUtils', () => { }); it('should return a fillableTakerAssetAmount when the remaining takerAssetAmount is less than the transferable amount', async () => { await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { from: makerAddress, }); - await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); - await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { from: makerAddress, }); - await erc20Token2.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerAssetAmount); - await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerAssetAmount, { from: takerAddress, }); - await zrxToken.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerFee); await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerFee, { @@ -356,13 +408,10 @@ describe('OrderValidationUtils', () => { describe('getOrderRelevantStates', async () => { it('should return the correct information for multiple orders', async () => { await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { from: makerAddress, }); - await zrxToken.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee); - await zrxToken.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { from: makerAddress, }); From 5c5f815b207947011dd60e08724bfa7650f1546c Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 5 Jun 2019 16:21:54 -0700 Subject: [PATCH 11/15] Fix LibAssetData tests --- contracts/dev-utils/package.json | 1 + contracts/dev-utils/test/lib_asset_data.ts | 125 +++++++++++++-------- 2 files changed, 81 insertions(+), 45 deletions(-) diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index 51b99ffa07..10958e8a06 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -73,6 +73,7 @@ "@0x/contracts-asset-proxy": "^2.1.5", "@0x/contracts-erc20": "^2.2.5", "@0x/contracts-erc721": "^2.1.6", + "@0x/contracts-erc1155": "^1.1.6", "@0x/contracts-exchange": "^2.1.5", "@0x/contracts-exchange-libs": "^2.1.6", "@0x/contracts-utils": "^3.1.6", diff --git a/contracts/dev-utils/test/lib_asset_data.ts b/contracts/dev-utils/test/lib_asset_data.ts index e44ef85468..c3023c9a19 100644 --- a/contracts/dev-utils/test/lib_asset_data.ts +++ b/contracts/dev-utils/test/lib_asset_data.ts @@ -1,7 +1,13 @@ -// TODO: change test titles to say "... from asset data" import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; +import { + artifacts as proxyArtifacts, + ERC1155ProxyContract, + ERC20ProxyContract, + ERC721ProxyContract, + MultiAssetProxyContract, +} from '@0x/contracts-asset-proxy'; import { artifacts as erc1155Artifacts, ERC1155MintableContract, @@ -9,6 +15,7 @@ import { } from '@0x/contracts-erc1155'; import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721'; +import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange'; import { chaiSetup, constants, LogDecoder, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { AssetProxyId } from '@0x/types'; @@ -52,11 +59,14 @@ const KNOWN_MULTI_ASSET_ENCODING = { }; describe('LibAssetData', () => { + let exchange: ExchangeContract; + let erc20Proxy: ERC20ProxyContract; + let erc721Proxy: ERC721ProxyContract; + let erc1155Proxy: ERC1155ProxyContract; + let multiAssetProxy: MultiAssetProxyContract; let libAssetData: LibAssetDataContract; let tokenOwnerAddress: string; - let approvedSpenderAddress: string; - let anotherApprovedSpenderAddress: string; let erc20Token: DummyERC20TokenContract; let erc721Token: DummyERC721TokenContract; @@ -72,17 +82,47 @@ describe('LibAssetData', () => { before(async () => { await blockchainLifecycle.startAsync(); - libAssetData = await LibAssetDataContract.deployFrom0xArtifactAsync( - artifacts.LibAssetData, + exchange = await ExchangeContract.deployFrom0xArtifactAsync( + exchangeArtifacts.Exchange, + provider, + txDefaults, + constants.NULL_BYTES, + ); + + erc20Proxy = await ERC20ProxyContract.deployFrom0xArtifactAsync( + proxyArtifacts.ERC20Proxy, + provider, + txDefaults, + ); + erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync( + proxyArtifacts.ERC721Proxy, + provider, + txDefaults, + ); + erc1155Proxy = await ERC1155ProxyContract.deployFrom0xArtifactAsync( + proxyArtifacts.ERC1155Proxy, + provider, + txDefaults, + ); + multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync( + proxyArtifacts.MultiAssetProxy, provider, txDefaults, ); - [ - tokenOwnerAddress, - approvedSpenderAddress, - anotherApprovedSpenderAddress, - ] = await web3Wrapper.getAvailableAddressesAsync(); + await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address); + await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc721Proxy.address); + await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc1155Proxy.address); + await exchange.registerAssetProxy.awaitTransactionSuccessAsync(multiAssetProxy.address); + + libAssetData = await LibAssetDataContract.deployFrom0xArtifactAsync( + artifacts.LibAssetData, + provider, + txDefaults, + exchange.address, + ); + + [tokenOwnerAddress] = await web3Wrapper.getAvailableAddressesAsync(); erc20Token = await DummyERC20TokenContract.deployFrom0xArtifactAsync( erc20Artifacts.DummyERC20Token, @@ -134,6 +174,13 @@ describe('LibAssetData', () => { await blockchainLifecycle.revertAsync(); }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + it('should have a deployed-to address', () => { expect(libAssetData.address.slice(0, 2)).to.equal('0x'); }); @@ -255,52 +302,48 @@ describe('LibAssetData', () => { it('should query ERC20 allowances by asset data', async () => { const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { from: tokenOwnerAddress, }); expect( - await libAssetData.getAllowance.callAsync( + await libAssetData.getAssetProxyAllowance.callAsync( tokenOwnerAddress, - approvedSpenderAddress, await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), ), ).to.bignumber.equal(allowance); }); it('should query ERC721 approval by asset data', async () => { - await erc721Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, firstERC721TokenId, { + await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { from: tokenOwnerAddress, }); expect( - await libAssetData.getAllowance.callAsync( + await libAssetData.getAssetProxyAllowance.callAsync( tokenOwnerAddress, - approvedSpenderAddress, await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ), ).to.bignumber.equal(1); }); it('should query ERC721 approvalForAll by assetData', async () => { - await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync(anotherApprovedSpenderAddress, true, { + await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync(erc721Proxy.address, true, { from: tokenOwnerAddress, }); expect( - await libAssetData.getAllowance.callAsync( + await libAssetData.getAssetProxyAllowance.callAsync( tokenOwnerAddress, - anotherApprovedSpenderAddress, await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), ), ).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); }); it('should query ERC1155 allowances by asset data', async () => { - await erc1155Token.setApprovalForAll.awaitTransactionSuccessAsync(approvedSpenderAddress, true, { + await erc1155Token.setApprovalForAll.awaitTransactionSuccessAsync(erc1155Proxy.address, true, { from: tokenOwnerAddress, }); expect( - await libAssetData.getAllowance.callAsync( + await libAssetData.getAssetProxyAllowance.callAsync( tokenOwnerAddress, - approvedSpenderAddress, await libAssetData.encodeERC1155AssetData.callAsync( erc1155Token.address, [erc1155TokenId], @@ -313,16 +356,15 @@ describe('LibAssetData', () => { it('should query multi-asset allowances by asset data', async () => { const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { from: tokenOwnerAddress, }); - await erc721Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, firstERC721TokenId, { + await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { from: tokenOwnerAddress, }); expect( - await libAssetData.getAllowance.callAsync( + await libAssetData.getAssetProxyAllowance.callAsync( tokenOwnerAddress, - approvedSpenderAddress, await libAssetData.encodeMultiAssetData.callAsync( [new BigNumber(1), new BigNumber(1)], [ @@ -346,21 +388,17 @@ describe('LibAssetData', () => { it('should query allowances for a batch of asset data strings', async () => { const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { from: tokenOwnerAddress, }); - await erc721Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, firstERC721TokenId, { + await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { from: tokenOwnerAddress, }); expect( - await libAssetData.getBatchAllowances.callAsync( - tokenOwnerAddress, - [approvedSpenderAddress, approvedSpenderAddress], - [ - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), - ], - ), + await libAssetData.getBatchAssetProxyAllowances.callAsync(tokenOwnerAddress, [ + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), + await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), + ]), ).to.deep.equal([new BigNumber(1), new BigNumber(1)]); }); @@ -380,13 +418,12 @@ describe('LibAssetData', () => { it('should query balance and allowance together, from asset data', async () => { const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { from: tokenOwnerAddress, }); expect( - await libAssetData.getBalanceAndAllowance.callAsync( + await libAssetData.getBalanceAndAssetProxyAllowance.callAsync( tokenOwnerAddress, - approvedSpenderAddress, await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), ), ).to.deep.equal([new BigNumber(erc20TokenTotalSupply), allowance]); @@ -394,15 +431,13 @@ describe('LibAssetData', () => { it('should query balances and allowances together, from an asset data array', async () => { const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(approvedSpenderAddress, allowance, { + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { from: tokenOwnerAddress, }); expect( - await libAssetData.getBatchBalancesAndAllowances.callAsync( - tokenOwnerAddress, - [approvedSpenderAddress, approvedSpenderAddress], - [await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address)], - ), + await libAssetData.getBatchBalancesAndAssetProxyAllowances.callAsync(tokenOwnerAddress, [ + await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), + ]), ).to.deep.equal([[new BigNumber(erc20TokenTotalSupply)], [allowance]]); }); }); From b1cfdc7a6aac9be05b27796aca90a87eb83825f2 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 5 Jun 2019 17:44:26 -0700 Subject: [PATCH 12/15] Return 0 balance/allowance if the call would otherwise revert --- .../dev-utils/contracts/src/LibAssetData.sol | 111 ++++++++++++++++-- 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/contracts/dev-utils/contracts/src/LibAssetData.sol b/contracts/dev-utils/contracts/src/LibAssetData.sol index 637d69e151..dc6f2929fd 100644 --- a/contracts/dev-utils/contracts/src/LibAssetData.sol +++ b/contracts/dev-utils/contracts/src/LibAssetData.sol @@ -20,9 +20,6 @@ pragma solidity ^0.5.5; pragma experimental ABIEncoderV2; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; -import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; -import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; -import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol"; import "@0x/contracts-asset-proxy/contracts/src/libs/LibAssetProxyIds.sol"; import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; @@ -30,8 +27,22 @@ import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; contract LibAssetData is LibAssetProxyIds { + // 2^256 - 1 uint256 constant internal _MAX_UINT256 = uint256(-1); + // ERC20 selectors + bytes4 constant internal _ERC20_BALANCE_OF_SELECTOR = 0x70a08231; + bytes4 constant internal _ERC20_ALLOWANCE_SELECTOR = 0xdd62ed3e; + + // ERC721 selectors + bytes4 constant internal _ERC721_OWNER_OF_SELECTOR = 0x6352211e; + bytes4 constant internal _ERC721_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5; + bytes4 constant internal _ERC721_GET_APPROVED_SELECTOR = 0x081812fc; + + // ERC1155 selectors + bytes4 constant internal _ERC1155_BALANCE_OF_SELECTOR = 0x00fdd58e; + bytes4 constant internal _ERC1155_IS_APPROVED_FOR_ALL_SELECTOR = 0xe985e9c5; + using LibBytes for bytes; // solhint-disable var-name-mixedcase @@ -62,29 +73,58 @@ contract LibAssetData is view returns (uint256 balance) { + // Get id of AssetProxy contract bytes4 assetProxyId = assetData.readBytes4(0); if (assetProxyId == ERC20_PROXY_ID) { + // Get ERC20 token address address tokenAddress = assetData.readAddress(16); - balance = IERC20Token(tokenAddress).balanceOf(ownerAddress); + + // Encode data for `balanceOf(ownerAddress)` + bytes memory balanceOfData = abi.encodeWithSelector(_ERC20_BALANCE_OF_SELECTOR, ownerAddress); + + // Query balance + (bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData); + balance = success && returnData.length == 32 ? returnData.readUint256(0) : 0; } else if (assetProxyId == ERC721_PROXY_ID) { + // Get ERC721 token address and id (, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData); + + // Check if id is owned by ownerAddress balance = getERC721TokenOwner(tokenAddress, tokenId) == ownerAddress ? 1 : 0; } else if (assetProxyId == ERC1155_PROXY_ID) { + // Get ERC1155 token address, array of ids, and array of values (, address tokenAddress, uint256[] memory tokenIds, uint256[] memory tokenValues,) = decodeERC1155AssetData(assetData); + uint256 length = tokenIds.length; for (uint256 i = 0; i != length; i++) { - uint256 totalBalance = IERC1155(tokenAddress).balanceOf(ownerAddress, tokenIds[i]); + // Encode data for `balanceOf(ownerAddress, tokenIds[i]) + bytes memory balanceOfData = abi.encodeWithSelector( + _ERC1155_BALANCE_OF_SELECTOR, + ownerAddress, + tokenIds[i] + ); + + // Query balance + (bool success, bytes memory returnData) = tokenAddress.staticcall(balanceOfData); + uint256 totalBalance = success && returnData.length == 32 ? returnData.readUint256(0) : 0; + + // Scale total balance down by corresponding value in assetData uint256 scaledBalance = totalBalance / tokenValues[i]; if (scaledBalance < balance || balance == 0) { balance = scaledBalance; } } } else if (assetProxyId == MULTI_ASSET_PROXY_ID) { + // Get array of values and array of assetDatas (, uint256[] memory assetAmounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData); + uint256 length = nestedAssetData.length; for (uint256 i = 0; i != length; i++) { + // Query balance of individual assetData uint256 totalBalance = getBalance(ownerAddress, nestedAssetData[i]); + + // Scale total balance down by corresponding value in assetData uint256 scaledBalance = totalBalance / assetAmounts[i]; if (scaledBalance < balance || balance == 0) { balance = scaledBalance; @@ -127,35 +167,80 @@ contract LibAssetData is view returns (uint256 allowance) { + // Get id of AssetProxy contract bytes4 assetProxyId = assetData.readBytes4(0); if (assetProxyId == MULTI_ASSET_PROXY_ID) { + // Get array of values and array of assetDatas (, uint256[] memory amounts, bytes[] memory nestedAssetData) = decodeMultiAssetData(assetData); + uint256 length = nestedAssetData.length; for (uint256 i = 0; i != length; i++) { + // Query allowance of individual assetData uint256 totalAllowance = getAssetProxyAllowance(ownerAddress, nestedAssetData[i]); + + // Scale total allowance down by corresponding value in assetData uint256 scaledAllowance = totalAllowance / amounts[i]; if (scaledAllowance < allowance || allowance == 0) { allowance = scaledAllowance; } } + return allowance; } if (assetProxyId == ERC20_PROXY_ID) { + // Get ERC20 token address address tokenAddress = assetData.readAddress(16); - allowance = IERC20Token(tokenAddress).allowance(ownerAddress, _ERC20_PROXY_ADDRESS); + + // Encode data for `allowance(ownerAddress, _ERC20_PROXY_ADDRESS)` + bytes memory allowanceData = abi.encodeWithSelector( + _ERC20_ALLOWANCE_SELECTOR, + ownerAddress, + _ERC20_PROXY_ADDRESS + ); + + // Query allowance + (bool success, bytes memory returnData) = tokenAddress.staticcall(allowanceData); + allowance = success && returnData.length == 32 ? returnData.readUint256(0) : 0; } else if (assetProxyId == ERC721_PROXY_ID) { + // Get ERC721 token address and id (, address tokenAddress, uint256 tokenId) = decodeERC721AssetData(assetData); - IERC721Token token = IERC721Token(tokenAddress); - address assetProxyAddress = _ERC721_PROXY_ADDRESS; - if (token.isApprovedForAll(ownerAddress, assetProxyAddress)) { + + // Encode data for `isApprovedForAll(ownerAddress, _ERC721_PROXY_ADDRESS)` + bytes memory isApprovedForAllData = abi.encodeWithSelector( + _ERC721_IS_APPROVED_FOR_ALL_SELECTOR, + ownerAddress, + _ERC721_PROXY_ADDRESS + ); + + (bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData); + + // If not approved for all, call `getApproved(tokenId)` + if (!success || returnData.length != 32 || returnData.readUint256(0) != 1) { + // Encode data for `getApproved(tokenId)` + bytes memory getApprovedData = abi.encodeWithSelector(_ERC721_GET_APPROVED_SELECTOR, tokenId); + (success, returnData) = tokenAddress.staticcall(getApprovedData); + + // Allowance is 1 if successful and the approved address is the ERC721Proxy + allowance = success && returnData.length == 32 && returnData.readAddress(12) == _ERC721_PROXY_ADDRESS ? 1 : 0; + } else { + // Allowance is 2^256 - 1 if `isApprovedForAll` returned true allowance = _MAX_UINT256; - } else if (token.getApproved(tokenId) == assetProxyAddress) { - allowance = 1; } } else if (assetProxyId == ERC1155_PROXY_ID) { + // Get ERC1155 token address (, address tokenAddress, , , ) = decodeERC1155AssetData(assetData); - allowance = IERC1155(tokenAddress).isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS) ? _MAX_UINT256 : 0; + + // Encode data for `isApprovedForAll(ownerAddress, _ERC1155_PROXY_ADDRESS)` + bytes memory isApprovedForAllData = abi.encodeWithSelector( + _ERC1155_IS_APPROVED_FOR_ALL_SELECTOR, + ownerAddress, + _ERC1155_PROXY_ADDRESS + ); + + // Query allowance + (bool success, bytes memory returnData) = tokenAddress.staticcall(isApprovedForAllData); + allowance = success && returnData.length == 32 && returnData.readUint256(0) == 1 ? _MAX_UINT256 : 0; } // Allowance will be 0 if the assetProxyId is unknown @@ -421,7 +506,7 @@ contract LibAssetData is returns (address ownerAddress) { bytes memory ownerOfCalldata = abi.encodeWithSelector( - 0x6352211e, + _ERC721_OWNER_OF_SELECTOR, tokenId ); From d5e88677ae3a740c3da045585e263d5e77a143f4 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 5 Jun 2019 17:44:34 -0700 Subject: [PATCH 13/15] Update tests --- contracts/dev-utils/test/lib_asset_data.ts | 459 +++++++++++---------- 1 file changed, 244 insertions(+), 215 deletions(-) diff --git a/contracts/dev-utils/test/lib_asset_data.ts b/contracts/dev-utils/test/lib_asset_data.ts index c3023c9a19..39fbfee911 100644 --- a/contracts/dev-utils/test/lib_asset_data.ts +++ b/contracts/dev-utils/test/lib_asset_data.ts @@ -13,11 +13,12 @@ import { ERC1155MintableContract, ERC1155TransferSingleEventArgs, } from '@0x/contracts-erc1155'; -import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; +import { artifacts as erc20Artifacts, DummyERC20TokenContract, ERC20TokenContract } from '@0x/contracts-erc20'; import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721'; import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange'; import { chaiSetup, constants, LogDecoder, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; import { BlockchainLifecycle } from '@0x/dev-utils'; +import { assetDataUtils } from '@0x/order-utils'; import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; @@ -185,221 +186,229 @@ describe('LibAssetData', () => { expect(libAssetData.address.slice(0, 2)).to.equal('0x'); }); - it('should encode ERC20 asset data', async () => { - expect(await libAssetData.encodeERC20AssetData.callAsync(KNOWN_ERC20_ENCODING.address)).to.equal( - KNOWN_ERC20_ENCODING.assetData, - ); - }); + describe('encoding and decoding', () => { + it('should encode ERC20 asset data', async () => { + expect(await libAssetData.encodeERC20AssetData.callAsync(KNOWN_ERC20_ENCODING.address)).to.equal( + KNOWN_ERC20_ENCODING.assetData, + ); + }); - it('should decode ERC20 asset data', async () => { - expect(await libAssetData.decodeERC20AssetData.callAsync(KNOWN_ERC20_ENCODING.assetData)).to.deep.equal([ - AssetProxyId.ERC20, - KNOWN_ERC20_ENCODING.address, - ]); - }); + it('should decode ERC20 asset data', async () => { + expect(await libAssetData.decodeERC20AssetData.callAsync(KNOWN_ERC20_ENCODING.assetData)).to.deep.equal([ + AssetProxyId.ERC20, + KNOWN_ERC20_ENCODING.address, + ]); + }); - it('should encode ERC721 asset data', async () => { - expect( - await libAssetData.encodeERC721AssetData.callAsync( + it('should encode ERC721 asset data', async () => { + expect( + await libAssetData.encodeERC721AssetData.callAsync( + KNOWN_ERC721_ENCODING.address, + KNOWN_ERC721_ENCODING.tokenId, + ), + ).to.equal(KNOWN_ERC721_ENCODING.assetData); + }); + + it('should decode ERC721 asset data', async () => { + expect(await libAssetData.decodeERC721AssetData.callAsync(KNOWN_ERC721_ENCODING.assetData)).to.deep.equal([ + AssetProxyId.ERC721, KNOWN_ERC721_ENCODING.address, KNOWN_ERC721_ENCODING.tokenId, - ), - ).to.equal(KNOWN_ERC721_ENCODING.assetData); - }); + ]); + }); - it('should decode ERC721 asset data', async () => { - expect(await libAssetData.decodeERC721AssetData.callAsync(KNOWN_ERC721_ENCODING.assetData)).to.deep.equal([ - AssetProxyId.ERC721, - KNOWN_ERC721_ENCODING.address, - KNOWN_ERC721_ENCODING.tokenId, - ]); - }); + it('should encode ERC1155 asset data', async () => { + expect( + await libAssetData.encodeERC1155AssetData.callAsync( + KNOWN_ERC1155_ENCODING.tokenAddress, + KNOWN_ERC1155_ENCODING.tokenIds, + KNOWN_ERC1155_ENCODING.tokenValues, + KNOWN_ERC1155_ENCODING.callbackData, + ), + ).to.equal(KNOWN_ERC1155_ENCODING.assetData); + }); - it('should encode ERC1155 asset data', async () => { - expect( - await libAssetData.encodeERC1155AssetData.callAsync( - KNOWN_ERC1155_ENCODING.tokenAddress, - KNOWN_ERC1155_ENCODING.tokenIds, - KNOWN_ERC1155_ENCODING.tokenValues, - KNOWN_ERC1155_ENCODING.callbackData, - ), - ).to.equal(KNOWN_ERC1155_ENCODING.assetData); - }); + it('should decode ERC1155 asset data', async () => { + expect(await libAssetData.decodeERC1155AssetData.callAsync(KNOWN_ERC1155_ENCODING.assetData)).to.deep.equal( + [ + AssetProxyId.ERC1155, + KNOWN_ERC1155_ENCODING.tokenAddress, + KNOWN_ERC1155_ENCODING.tokenIds, + KNOWN_ERC1155_ENCODING.tokenValues, + KNOWN_ERC1155_ENCODING.callbackData, + ], + ); + }); - it('should decode ERC1155 asset data', async () => { - expect(await libAssetData.decodeERC1155AssetData.callAsync(KNOWN_ERC1155_ENCODING.assetData)).to.deep.equal([ - AssetProxyId.ERC1155, - KNOWN_ERC1155_ENCODING.tokenAddress, - KNOWN_ERC1155_ENCODING.tokenIds, - KNOWN_ERC1155_ENCODING.tokenValues, - KNOWN_ERC1155_ENCODING.callbackData, - ]); - }); + it('should encode multiasset data', async () => { + expect( + await libAssetData.encodeMultiAssetData.callAsync( + KNOWN_MULTI_ASSET_ENCODING.amounts, + KNOWN_MULTI_ASSET_ENCODING.nestedAssetData, + ), + ).to.equal(KNOWN_MULTI_ASSET_ENCODING.assetData); + }); - it('should encode multiasset data', async () => { - expect( - await libAssetData.encodeMultiAssetData.callAsync( + it('should decode multiasset data', async () => { + expect( + await libAssetData.decodeMultiAssetData.callAsync(KNOWN_MULTI_ASSET_ENCODING.assetData), + ).to.deep.equal([ + AssetProxyId.MultiAsset, KNOWN_MULTI_ASSET_ENCODING.amounts, KNOWN_MULTI_ASSET_ENCODING.nestedAssetData, - ), - ).to.equal(KNOWN_MULTI_ASSET_ENCODING.assetData); - }); - - it('should decode multiasset data', async () => { - expect(await libAssetData.decodeMultiAssetData.callAsync(KNOWN_MULTI_ASSET_ENCODING.assetData)).to.deep.equal([ - AssetProxyId.MultiAsset, - KNOWN_MULTI_ASSET_ENCODING.amounts, - KNOWN_MULTI_ASSET_ENCODING.nestedAssetData, - ]); - }); - - it('should query ERC20 balance by asset data', async () => { - expect( - await libAssetData.getBalance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - ), - ).to.bignumber.equal(erc20TokenTotalSupply); - }); - - it('should query ERC721 balance by asset data', async () => { - expect( - await libAssetData.getBalance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), - ), - ).to.bignumber.equal(1); - }); - - it('should query ERC1155 balances by asset data', async () => { - expect( - await libAssetData.getBalance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeERC1155AssetData.callAsync( - erc1155Token.address, - [erc1155TokenId], - [new BigNumber(1)], // token values - '0x', // callback data - ), - ), - ).to.bignumber.equal(1); - }); - - it('should query multi-asset batch balance by asset data', async () => { - expect( - await libAssetData.getBalance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeMultiAssetData.callAsync( - [new BigNumber(1), new BigNumber(1)], - [ - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), - ], - ), - ), - ).to.bignumber.equal(Math.min(erc20TokenTotalSupply.toNumber(), numberOfERC721Tokens)); - }); - - it('should query ERC20 allowances by asset data', async () => { - const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { - from: tokenOwnerAddress, + ]); }); - expect( - await libAssetData.getAssetProxyAllowance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - ), - ).to.bignumber.equal(allowance); }); - it('should query ERC721 approval by asset data', async () => { - await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { - from: tokenOwnerAddress, + describe('getBalance', () => { + it('should query ERC20 balance by asset data', async () => { + const assetData = assetDataUtils.encodeERC20AssetData(erc20Token.address); + expect(await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData)).to.bignumber.equal( + erc20TokenTotalSupply, + ); + }); + + it('should return 0 if ERC20 token does not exist', async () => { + const assetData = assetDataUtils.encodeERC20AssetData(constants.NULL_ADDRESS); + const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData); + expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT); + }); + + it('should query ERC721 balance by asset data', async () => { + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId); + expect(await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData)).to.bignumber.equal(1); + }); + + it('should return 0 if ERC721 token does not exist', async () => { + const assetData = assetDataUtils.encodeERC721AssetData(constants.NULL_ADDRESS, firstERC721TokenId); + const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData); + expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT); + }); + + it('should query ERC1155 balances by asset data', async () => { + const assetData = assetDataUtils.encodeERC1155AssetData( + erc1155Token.address, + [erc1155TokenId], + [new BigNumber(1)], + constants.NULL_BYTES, + ); + expect(await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData)).to.bignumber.equal(1); + }); + + it('should return 0 if ERC1155 token does not exist', async () => { + const assetData = assetDataUtils.encodeERC1155AssetData( + constants.NULL_ADDRESS, + [erc1155TokenId], + [new BigNumber(1)], + constants.NULL_BYTES, + ); + const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData); + expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT); + }); + + it('should query multi-asset batch balance by asset data', async () => { + const assetData = assetDataUtils.encodeMultiAssetData( + [new BigNumber(1), new BigNumber(1)], + [ + assetDataUtils.encodeERC20AssetData(erc20Token.address), + assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId), + ], + ); + expect(await libAssetData.getBalance.callAsync(tokenOwnerAddress, assetData)).to.bignumber.equal( + Math.min(erc20TokenTotalSupply.toNumber(), numberOfERC721Tokens), + ); + }); + + it('should return a balance of 0 if the assetData does not correspond to an AssetProxy contract', async () => { + const fakeAssetData = '0x01020304'; + const balance = await libAssetData.getBalance.callAsync(tokenOwnerAddress, fakeAssetData); + expect(balance).to.bignumber.equal(constants.ZERO_AMOUNT); }); - expect( - await libAssetData.getAssetProxyAllowance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), - ), - ).to.bignumber.equal(1); }); - it('should query ERC721 approvalForAll by assetData', async () => { - await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync(erc721Proxy.address, true, { - from: tokenOwnerAddress, + describe('getAssetProxyAllowance', () => { + it('should query ERC20 allowances by asset data', async () => { + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { + from: tokenOwnerAddress, + }); + const assetData = assetDataUtils.encodeERC20AssetData(erc20Token.address); + expect( + await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData), + ).to.bignumber.equal(allowance); + }); + + it('should query ERC721 approval by asset data', async () => { + await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { + from: tokenOwnerAddress, + }); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId); + expect( + await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData), + ).to.bignumber.equal(1); + }); + + it('should query ERC721 approvalForAll by assetData', async () => { + await erc721Token.setApprovalForAll.awaitTransactionSuccessAsync(erc721Proxy.address, true, { + from: tokenOwnerAddress, + }); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId); + expect( + await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData), + ).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + }); + + it('should query ERC1155 allowances by asset data', async () => { + await erc1155Token.setApprovalForAll.awaitTransactionSuccessAsync(erc1155Proxy.address, true, { + from: tokenOwnerAddress, + }); + const assetData = assetDataUtils.encodeERC1155AssetData( + erc1155Token.address, + [erc1155TokenId], + [new BigNumber(1)], + constants.NULL_BYTES, + ); + expect( + await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData), + ).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + }); + + it('should query multi-asset allowances by asset data', async () => { + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { + from: tokenOwnerAddress, + }); + await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { + from: tokenOwnerAddress, + }); + const assetData = assetDataUtils.encodeMultiAssetData( + [new BigNumber(1), new BigNumber(1)], + [ + assetDataUtils.encodeERC20AssetData(erc20Token.address), + assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId), + ], + ); + expect( + await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData), + ).to.bignumber.equal(1); + return; + }); + + it('should return an allowance of 0 if the assetData does not correspond to an AssetProxy contract', async () => { + const fakeAssetData = '0x01020304'; + const allowance = await libAssetData.getAssetProxyAllowance.callAsync(tokenOwnerAddress, fakeAssetData); + expect(allowance).to.bignumber.equal(constants.ZERO_AMOUNT); }); - expect( - await libAssetData.getAssetProxyAllowance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), - ), - ).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); }); - it('should query ERC1155 allowances by asset data', async () => { - await erc1155Token.setApprovalForAll.awaitTransactionSuccessAsync(erc1155Proxy.address, true, { - from: tokenOwnerAddress, + describe('getBatchBalances', () => { + it('should query balances for a batch of asset data strings', async () => { + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId); + expect( + await libAssetData.getBatchBalances.callAsync(tokenOwnerAddress, [erc20AssetData, erc721AssetData]), + ).to.deep.equal([new BigNumber(erc20TokenTotalSupply), new BigNumber(1)]); }); - expect( - await libAssetData.getAssetProxyAllowance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeERC1155AssetData.callAsync( - erc1155Token.address, - [erc1155TokenId], - [new BigNumber(1)], - '0x', - ), - ), - ).to.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); - }); - - it('should query multi-asset allowances by asset data', async () => { - const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { - from: tokenOwnerAddress, - }); - await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { - from: tokenOwnerAddress, - }); - expect( - await libAssetData.getAssetProxyAllowance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeMultiAssetData.callAsync( - [new BigNumber(1), new BigNumber(1)], - [ - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), - ], - ), - ), - ).to.bignumber.equal(1); - return; - }); - - it('should query balances for a batch of asset data strings', async () => { - expect( - await libAssetData.getBatchBalances.callAsync(tokenOwnerAddress, [ - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), - ]), - ).to.deep.equal([new BigNumber(erc20TokenTotalSupply), new BigNumber(1)]); - }); - - it('should query allowances for a batch of asset data strings', async () => { - const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { - from: tokenOwnerAddress, - }); - await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { - from: tokenOwnerAddress, - }); - expect( - await libAssetData.getBatchAssetProxyAllowances.callAsync(tokenOwnerAddress, [ - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - await libAssetData.encodeERC721AssetData.callAsync(erc721Token.address, firstERC721TokenId), - ]), - ).to.deep.equal([new BigNumber(1), new BigNumber(1)]); }); describe('getERC721TokenOwner', async () => { @@ -416,28 +425,48 @@ describe('LibAssetData', () => { }); }); - it('should query balance and allowance together, from asset data', async () => { - const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { - from: tokenOwnerAddress, + describe('getBalanceAndAllowance', () => { + it('should query balance and allowance together, from asset data', async () => { + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { + from: tokenOwnerAddress, + }); + const assetData = assetDataUtils.encodeERC20AssetData(erc20Token.address); + expect( + await libAssetData.getBalanceAndAssetProxyAllowance.callAsync(tokenOwnerAddress, assetData), + ).to.deep.equal([new BigNumber(erc20TokenTotalSupply), allowance]); + }); + }); + describe('getBatchBalancesAndAllowances', () => { + it('should query balances and allowances together, from an asset data array', async () => { + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { + from: tokenOwnerAddress, + }); + const assetData = assetDataUtils.encodeERC20AssetData(erc20Token.address); + expect( + await libAssetData.getBatchBalancesAndAssetProxyAllowances.callAsync(tokenOwnerAddress, [assetData]), + ).to.deep.equal([[new BigNumber(erc20TokenTotalSupply)], [allowance]]); }); - expect( - await libAssetData.getBalanceAndAssetProxyAllowance.callAsync( - tokenOwnerAddress, - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - ), - ).to.deep.equal([new BigNumber(erc20TokenTotalSupply), allowance]); }); - it('should query balances and allowances together, from an asset data array', async () => { - const allowance = new BigNumber(1); - await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { - from: tokenOwnerAddress, + describe('getBatchAssetProxyAllowances', () => { + it('should query allowances for a batch of asset data strings', async () => { + const allowance = new BigNumber(1); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, allowance, { + from: tokenOwnerAddress, + }); + await erc721Token.approve.awaitTransactionSuccessAsync(erc721Proxy.address, firstERC721TokenId, { + from: tokenOwnerAddress, + }); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, firstERC721TokenId); + expect( + await libAssetData.getBatchAssetProxyAllowances.callAsync(tokenOwnerAddress, [ + erc20AssetData, + erc721AssetData, + ]), + ).to.deep.equal([new BigNumber(1), new BigNumber(1)]); }); - expect( - await libAssetData.getBatchBalancesAndAssetProxyAllowances.callAsync(tokenOwnerAddress, [ - await libAssetData.encodeERC20AssetData.callAsync(erc20Token.address), - ]), - ).to.deep.equal([[new BigNumber(erc20TokenTotalSupply)], [allowance]]); }); }); From 718d48b7d54c702d5d41f3f22de6a664db172e04 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 6 Jun 2019 08:35:45 -0700 Subject: [PATCH 14/15] Fix linting errors and update CHANGELOGs --- contracts/asset-proxy/CHANGELOG.json | 2 +- contracts/dev-utils/CHANGELOG.json | 16 ++++++++++++++++ contracts/dev-utils/test/lib_asset_data.ts | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/contracts/asset-proxy/CHANGELOG.json b/contracts/asset-proxy/CHANGELOG.json index f383c96a97..4f2e5ce78a 100644 --- a/contracts/asset-proxy/CHANGELOG.json +++ b/contracts/asset-proxy/CHANGELOG.json @@ -12,7 +12,7 @@ "version": "2.2.0", "changes": [ { - "note": "Add `LibAssetProxyIds` and `LibAssetData` contracts", + "note": "Add `LibAssetProxyIds` contract", "pr": 1835 } ] diff --git a/contracts/dev-utils/CHANGELOG.json b/contracts/dev-utils/CHANGELOG.json index 4249f2d831..0c8c344861 100644 --- a/contracts/dev-utils/CHANGELOG.json +++ b/contracts/dev-utils/CHANGELOG.json @@ -5,6 +5,22 @@ { "note": "Create dev-utils package", "pr": 1848 + }, + { + "note": "Add `LibssetData` and `LibTransactionDecoder` contracts", + "pr": 1848 + }, + { + "note": "Refactor `LibAssetData` to only check 0x-specific allowances", + "pr": 1848 + }, + { + "note": "Refactor `LibAssetData` balance/allowance checks to never revert", + "pr": 1848 + }, + { + "note": "Refactor `OrderValidationUtils` to calculate `fillableTakerAssetAmount`", + "pr": 1848 } ] } diff --git a/contracts/dev-utils/test/lib_asset_data.ts b/contracts/dev-utils/test/lib_asset_data.ts index 39fbfee911..d9708f3f4f 100644 --- a/contracts/dev-utils/test/lib_asset_data.ts +++ b/contracts/dev-utils/test/lib_asset_data.ts @@ -13,7 +13,7 @@ import { ERC1155MintableContract, ERC1155TransferSingleEventArgs, } from '@0x/contracts-erc1155'; -import { artifacts as erc20Artifacts, DummyERC20TokenContract, ERC20TokenContract } from '@0x/contracts-erc20'; +import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20'; import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721'; import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange'; import { chaiSetup, constants, LogDecoder, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; From f0c4ccfa1e8528a6caf4d9ce292d7122cd8ad607 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 6 Jun 2019 10:30:12 -0700 Subject: [PATCH 15/15] Remove multiple returns from devdoc comments, fix typos --- contracts/dev-utils/CHANGELOG.json | 2 +- .../contracts/src/OrderValidationUtils.sol | 21 ++++++++++++------- yarn.lock | 7 ++++++- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/contracts/dev-utils/CHANGELOG.json b/contracts/dev-utils/CHANGELOG.json index 0c8c344861..37287903d7 100644 --- a/contracts/dev-utils/CHANGELOG.json +++ b/contracts/dev-utils/CHANGELOG.json @@ -7,7 +7,7 @@ "pr": 1848 }, { - "note": "Add `LibssetData` and `LibTransactionDecoder` contracts", + "note": "Add `LibAssetData` and `LibTransactionDecoder` contracts", "pr": 1848 }, { diff --git a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol index c30ea13d1a..0b04ff5804 100644 --- a/contracts/dev-utils/contracts/src/OrderValidationUtils.sol +++ b/contracts/dev-utils/contracts/src/OrderValidationUtils.sol @@ -47,12 +47,12 @@ contract OrderValidationUtils is /// @param order The order structure. /// @param signature Signature provided by maker that proves the order's authenticity. /// `0x01` can always be provided if the signature does not need to be validated. - /// @return orderInfo The hash, status, and `takerAssetAmount` already filled for the given order. - /// @return fillableTakerAssetAmount The amount of the order's `takerAssetAmount` that is fillable given all on-chain state. - /// If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled" + /// @return The orderInfo (hash, status, and `takerAssetAmount` already filled for the given order), + /// fillableTakerAssetAmount (amount of the order's `takerAssetAmount` that is fillable given all on-chain state), + /// and isValidSignature (validity of the provided signature). + /// NOTE: If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled" /// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final /// amount of each asset that can be filled. - /// @return isValidSignature The validity of the provided signature. function getOrderRelevantState(LibOrder.Order memory order, bytes memory signature) public view @@ -134,9 +134,12 @@ contract OrderValidationUtils is /// @param orders Array of order structures. /// @param signatures Array of signatures provided by makers that prove the authenticity of the orders. /// `0x01` can always be provided if a signature does not need to be validated. - /// @return ordersInfo Array of the hash, status, and `takerAssetAmount` already filled for each order. - /// @return fillableTakerAssetAmounts Array of amounts for each order's `takerAssetAmount` that is fillable given all on-chain state. - /// @return isValidSignature Array containing the validity of each provided signature. + /// @return The ordersInfo (array of the hash, status, and `takerAssetAmount` already filled for each order), + /// fillableTakerAssetAmounts (array of amounts for each order's `takerAssetAmount` that is fillable given all on-chain state), + /// and isValidSignature (array containing the validity of each provided signature). + /// NOTE: If the `takerAssetData` encodes data for multiple assets, each element of `fillableTakerAssetAmounts` + /// will represent a "scaled" amount, meaning it must be multiplied by all the individual asset amounts within + /// the `takerAssetData` to get the final amount of each asset that can be filled. function getOrderRelevantStates(LibOrder.Order[] memory orders, bytes[] memory signatures) public view @@ -165,6 +168,10 @@ contract OrderValidationUtils is /// @param ownerAddress Address of the owner of the asset. /// @param assetData Description of tokens, per the AssetProxy contract specification. /// @return The amount of the asset tranferable by the owner. + /// NOTE: If the `assetData` encodes data for multiple assets, the `transferableAssetAmount` + /// will represent the amount of times the entire `assetData` can be transferred. To calculate + /// the total individual transferable amounts, this scaled `transferableAmount` must be multiplied by + /// the individual asset amounts located within the `assetData`. function getTransferableAssetAmount(address ownerAddress, bytes memory assetData) public view diff --git a/yarn.lock b/yarn.lock index 0bd6bd4f39..4550e56184 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5658,6 +5658,11 @@ deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + integrity sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8= + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -16341,7 +16346,7 @@ tape@~2.3.2: resumer "~0.0.0" through "~2.3.4" -tar-fs@~1.16.3: +tar-fs@^1.13.0, tar-fs@~1.16.3: version "1.16.3" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" dependencies: