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, });