diff options
Diffstat (limited to 'packages/contracts/test')
-rw-r--r-- | packages/contracts/test/asset_proxy/authorizable.ts | 88 | ||||
-rw-r--r-- | packages/contracts/test/asset_proxy/decoder.ts | 86 | ||||
-rw-r--r-- | packages/contracts/test/asset_proxy/proxies.ts | 194 | ||||
-rw-r--r-- | packages/contracts/test/asset_proxy_owner.ts | 344 | ||||
-rw-r--r-- | packages/contracts/test/exchange/core.ts | 105 | ||||
-rw-r--r-- | packages/contracts/test/exchange/dispatcher.ts | 25 | ||||
-rw-r--r-- | packages/contracts/test/exchange/match_orders.ts | 25 | ||||
-rw-r--r-- | packages/contracts/test/exchange/signature_validator.ts | 10 | ||||
-rw-r--r-- | packages/contracts/test/exchange/transactions.ts | 40 | ||||
-rw-r--r-- | packages/contracts/test/exchange/wrapper.ts | 29 | ||||
-rw-r--r-- | packages/contracts/test/libraries/lib_bytes.ts | 346 | ||||
-rw-r--r-- | packages/contracts/test/libraries/lib_mem.ts | 190 | ||||
-rw-r--r-- | packages/contracts/test/unlimited_allowance_token.ts | 7 |
13 files changed, 768 insertions, 721 deletions
diff --git a/packages/contracts/test/asset_proxy/authorizable.ts b/packages/contracts/test/asset_proxy/authorizable.ts index ed582b63e..8c9d0495d 100644 --- a/packages/contracts/test/asset_proxy/authorizable.ts +++ b/packages/contracts/test/asset_proxy/authorizable.ts @@ -1,9 +1,11 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { RevertReason } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import { MixinAuthorizableContract } from '../../src/generated_contract_wrappers/mixin_authorizable'; import { artifacts } from '../../src/utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; +import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; @@ -42,8 +44,9 @@ describe('Authorizable', () => { }); describe('addAuthorizedAddress', () => { it('should throw if not called by owner', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }), + RevertReason.OnlyContractOwner, ); }); it('should allow owner to add an authorized address', async () => { @@ -59,8 +62,9 @@ describe('Authorizable', () => { await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), + RevertReason.TargetAlreadyAuthorized, ); }); }); @@ -71,10 +75,11 @@ describe('Authorizable', () => { await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: notOwner, }), + RevertReason.OnlyContractOwner, ); }); @@ -94,14 +99,87 @@ describe('Authorizable', () => { }); it('should throw if owner attempts to remove an address that is not authorized', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: owner, }), + RevertReason.TargetNotAuthorized, ); }); }); + describe('removeAuthorizedAddressAtIndex', () => { + it('should throw if not called by owner', async () => { + await web3Wrapper.awaitTransactionSuccessAsync( + await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const index = new BigNumber(0); + return expectRevertReasonOrAlwaysFailingTransactionAsync( + authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { + from: notOwner, + }), + RevertReason.OnlyContractOwner, + ); + }); + it('should throw if index is >= authorities.length', async () => { + await web3Wrapper.awaitTransactionSuccessAsync( + await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const index = new BigNumber(1); + return expectRevertReasonOrAlwaysFailingTransactionAsync( + authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { + from: owner, + }), + RevertReason.IndexOutOfBounds, + ); + }); + it('should throw if owner attempts to remove an address that is not authorized', async () => { + const index = new BigNumber(0); + return expectRevertReasonOrAlwaysFailingTransactionAsync( + authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { + from: owner, + }), + RevertReason.TargetNotAuthorized, + ); + }); + it('should throw if address at index does not match target', async () => { + const address1 = address; + const address2 = notOwner; + await web3Wrapper.awaitTransactionSuccessAsync( + await authorizable.addAuthorizedAddress.sendTransactionAsync(address1, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await authorizable.addAuthorizedAddress.sendTransactionAsync(address2, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const address1Index = new BigNumber(0); + return expectRevertReasonOrAlwaysFailingTransactionAsync( + authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address2, address1Index, { + from: owner, + }), + RevertReason.AuthorizedAddressMismatch, + ); + }); + it('should allow owner to remove an authorized address', async () => { + await web3Wrapper.awaitTransactionSuccessAsync( + await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const index = new BigNumber(0); + await web3Wrapper.awaitTransactionSuccessAsync( + await authorizable.removeAuthorizedAddressAtIndex.sendTransactionAsync(address, index, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const isAuthorized = await authorizable.authorized.callAsync(address); + expect(isAuthorized).to.be.false(); + }); + }); + describe('getAuthorizedAddresses', () => { it('should return all authorized addresses', async () => { const initial = await authorizable.getAuthorizedAddresses.callAsync(); diff --git a/packages/contracts/test/asset_proxy/decoder.ts b/packages/contracts/test/asset_proxy/decoder.ts deleted file mode 100644 index 875d55daa..000000000 --- a/packages/contracts/test/asset_proxy/decoder.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import ethUtil = require('ethereumjs-util'); - -import { TestAssetDataDecodersContract } from '../../src/generated_contract_wrappers/test_asset_data_decoders'; -import { artifacts } from '../../src/utils/artifacts'; -import { chaiSetup } from '../../src/utils/chai_setup'; -import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('TestAssetDataDecoders', () => { - let testAssetProxyDecoder: TestAssetDataDecodersContract; - let testAddress: string; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - // Setup accounts & addresses - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - testAddress = accounts[0]; - // Deploy TestLibMem - testAssetProxyDecoder = await TestAssetDataDecodersContract.deployFrom0xArtifactAsync( - artifacts.TestAssetDataDecoders, - provider, - txDefaults, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - - describe('Asset Data Decoders', () => { - it('should correctly decode ERC721 asset data', async () => { - const tokenId = generatePseudoRandomSalt(); - const encodedAssetData = assetProxyUtils.encodeERC721AssetData(testAddress, tokenId); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); - const expectedDecodedAssetData = assetProxyUtils.decodeERC721AssetData(encodedAssetData); - let decodedTokenAddress: string; - let decodedTokenId: BigNumber; - let decodedData: string; - [ - decodedTokenAddress, - decodedTokenId, - decodedData, - ] = await testAssetProxyDecoder.publicDecodeERC721Data.callAsync(encodedAssetDataWithoutProxyId); - expect(decodedTokenAddress).to.be.equal(expectedDecodedAssetData.tokenAddress); - expect(decodedTokenId).to.be.bignumber.equal(expectedDecodedAssetData.tokenId); - expect(decodedData).to.be.equal(expectedDecodedAssetData.receiverData); - }); - - it('should correctly decode ERC721 asset data with receiver data', async () => { - const tokenId = generatePseudoRandomSalt(); - const receiverDataFirst32Bytes = ethUtil.bufferToHex( - assetProxyUtils.encodeUint256(generatePseudoRandomSalt()), - ); - const receiverDataExtraBytes = 'FFFF'; - // We add extra bytes to generate a value that doesn't fit perfectly into one word - const receiverData = receiverDataFirst32Bytes + receiverDataExtraBytes; - const encodedAssetData = assetProxyUtils.encodeERC721AssetData(testAddress, tokenId, receiverData); - const expectedDecodedAssetData = assetProxyUtils.decodeERC721AssetData(encodedAssetData); - let decodedTokenAddress: string; - let decodedTokenId: BigNumber; - let decodedReceiverData: string; - [ - decodedTokenAddress, - decodedTokenId, - decodedReceiverData, - ] = await testAssetProxyDecoder.publicDecodeERC721Data.callAsync(encodedAssetData); - expect(decodedTokenAddress).to.be.equal(expectedDecodedAssetData.tokenAddress); - expect(decodedTokenId).to.be.bignumber.equal(expectedDecodedAssetData.tokenId); - expect(decodedReceiverData).to.be.equal(expectedDecodedAssetData.receiverData); - }); - }); -}); diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 2c27f7382..595839519 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -1,10 +1,10 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { RevertReason } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); -import * as _ from 'lodash'; import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c20_token'; import { @@ -15,7 +15,7 @@ import { DummyERC721TokenContract } from '../../src/generated_contract_wrappers/ import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy'; import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy'; import { artifacts } from '../../src/utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; +import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -96,13 +96,12 @@ describe('Asset Transfer Proxies', () => { it('should successfully transfer tokens', async () => { // Construct ERC20 asset data const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(10); await web3Wrapper.awaitTransactionSuccessAsync( await erc20Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, takerAddress, amount, @@ -123,13 +122,12 @@ describe('Asset Transfer Proxies', () => { it('should do nothing if transferring 0 amount of a token', async () => { // Construct ERC20 asset data const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(0); await web3Wrapper.awaitTransactionSuccessAsync( await erc20Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, takerAddress, amount, @@ -160,93 +158,37 @@ describe('Asset Transfer Proxies', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); // Perform a transfer; expect this to fail. - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( erc20Proxy.transferFrom.sendTransactionAsync( encodedAssetData, makerAddress, takerAddress, transferAmount, - { from: notAuthorized }, + { from: exchangeAddress }, ), + RevertReason.TransferFailed, ); }); it('should throw if requesting address is not authorized', async () => { // Construct ERC20 asset data const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); - // Perform a transfer from makerAddress to takerAddress - const amount = new BigNumber(10); - return expectRevertOrAlwaysFailingTransactionAsync( - erc20Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, - makerAddress, - takerAddress, - amount, - { - from: notAuthorized, - }, - ), - ); - }); - }); - - describe('batchTransferFrom', () => { - it('should succesfully make multiple token transfers', async () => { - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - - const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); - const amount = new BigNumber(10); - const numTransfers = 2; - const assetData = _.times(numTransfers, () => encodedAssetDataWithoutProxyId); - const fromAddresses = _.times(numTransfers, () => makerAddress); - const toAddresses = _.times(numTransfers, () => takerAddress); - const amounts = _.times(numTransfers, () => amount); - - const txHash = await erc20Proxy.batchTransferFrom.sendTransactionAsync( - assetData, - fromAddresses, - toAddresses, - amounts, - { from: exchangeAddress }, - ); - const res = await web3Wrapper.awaitTransactionSuccessAsync( - txHash, - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - - expect(res.logs.length).to.equal(numTransfers); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(amount.times(numTransfers)), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].add(amount.times(numTransfers)), - ); - }); - it('should throw if not called by an authorized address', async () => { - const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); + // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(10); - const numTransfers = 2; - const assetData = _.times(numTransfers, () => encodedAssetDataWithoutProxyId); - const fromAddresses = _.times(numTransfers, () => makerAddress); - const toAddresses = _.times(numTransfers, () => takerAddress); - const amounts = _.times(numTransfers, () => amount); - - return expectRevertOrAlwaysFailingTransactionAsync( - erc20Proxy.batchTransferFrom.sendTransactionAsync(assetData, fromAddresses, toAddresses, amounts, { + return expectRevertReasonOrAlwaysFailingTransactionAsync( + erc20Proxy.transferFrom.sendTransactionAsync(encodedAssetData, makerAddress, takerAddress, amount, { from: notAuthorized, }), + RevertReason.SenderNotAuthorized, ); }); }); - it('should have an id of 1', async () => { + it('should have an id of 0xf47261b0', async () => { const proxyId = await erc20Proxy.getProxyId.callAsync(); - expect(proxyId).to.equal(1); + const expectedProxyId = '0xf47261b0'; + expect(proxyId).to.equal(expectedProxyId); }); }); @@ -255,7 +197,6 @@ describe('Asset Transfer Proxies', () => { it('should successfully transfer tokens', async () => { // Construct ERC721 asset data const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); @@ -263,7 +204,7 @@ describe('Asset Transfer Proxies', () => { const amount = new BigNumber(1); await web3Wrapper.awaitTransactionSuccessAsync( await erc721Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, takerAddress, amount, @@ -279,14 +220,13 @@ describe('Asset Transfer Proxies', () => { it('should call onERC721Received when transferring to a smart contract without receiver data', async () => { // Construct ERC721 asset data const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(1); const txHash = await erc721Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, erc721Receiver.address, amount, @@ -315,14 +255,13 @@ describe('Asset Transfer Proxies', () => { erc721MakerTokenId, receiverData, ); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(1); const txHash = await erc721Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, erc721Receiver.address, amount, @@ -351,67 +290,66 @@ describe('Asset Transfer Proxies', () => { erc721MakerTokenId, receiverData, ); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(1); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, erc20Proxy.address, // the ERC20 proxy does not have an ERC721 receiver amount, { from: exchangeAddress }, ), + RevertReason.TransferFailed, ); }); it('should throw if transferring 0 amount of a token', async () => { // Construct ERC721 asset data const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(0); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, takerAddress, amount, { from: exchangeAddress }, ), + RevertReason.InvalidAmount, ); }); it('should throw if transferring > 1 amount of a token', async () => { // Construct ERC721 asset data const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Verify pre-condition const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(500); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, takerAddress, amount, { from: exchangeAddress }, ), + RevertReason.InvalidAmount, ); }); it('should throw if allowances are too low', async () => { // Construct ERC721 asset data const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Remove transfer approval for makerAddress. await web3Wrapper.awaitTransactionSuccessAsync( await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, false, { @@ -421,94 +359,36 @@ describe('Asset Transfer Proxies', () => { ); // Perform a transfer; expect this to fail. const amount = new BigNumber(1); - return expectRevertOrAlwaysFailingTransactionAsync( - erc20Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, - makerAddress, - takerAddress, - amount, - { - from: notAuthorized, - }, - ), + return expectRevertReasonOrAlwaysFailingTransactionAsync( + erc20Proxy.transferFrom.sendTransactionAsync(encodedAssetData, makerAddress, takerAddress, amount, { + from: exchangeAddress, + }), + RevertReason.TransferFailed, ); }); it('should throw if requesting address is not authorized', async () => { // Construct ERC721 asset data const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(1); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, + encodedAssetData, makerAddress, takerAddress, amount, { from: notAuthorized }, ), + RevertReason.SenderNotAuthorized, ); }); }); - describe('batchTransferFrom', () => { - it('should succesfully make multiple token transfers', async () => { - const erc721TokensById = await erc721Wrapper.getBalancesAsync(); - const [makerTokenIdA, makerTokenIdB] = erc721TokensById[makerAddress][erc721Token.address]; - - const numTransfers = 2; - const assetData = [ - assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdA).slice(0, -2), - assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdB).slice(0, -2), - ]; - const fromAddresses = _.times(numTransfers, () => makerAddress); - const toAddresses = _.times(numTransfers, () => takerAddress); - const amounts = _.times(numTransfers, () => new BigNumber(1)); - - const txHash = await erc721Proxy.batchTransferFrom.sendTransactionAsync( - assetData, - fromAddresses, - toAddresses, - amounts, - { from: exchangeAddress }, - ); - const res = await web3Wrapper.awaitTransactionSuccessAsync( - txHash, - constants.AWAIT_TRANSACTION_MINED_MS, - ); - expect(res.logs.length).to.equal(numTransfers); - - const newOwnerMakerAssetA = await erc721Token.ownerOf.callAsync(makerTokenIdA); - const newOwnerMakerAssetB = await erc721Token.ownerOf.callAsync(makerTokenIdB); - expect(newOwnerMakerAssetA).to.be.bignumber.equal(takerAddress); - expect(newOwnerMakerAssetB).to.be.bignumber.equal(takerAddress); - }); - - it('should throw if not called by an authorized address', async () => { - const erc721TokensById = await erc721Wrapper.getBalancesAsync(); - const [makerTokenIdA, makerTokenIdB] = erc721TokensById[makerAddress][erc721Token.address]; - - const numTransfers = 2; - const assetData = [ - assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdA).slice(0, -2), - assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerTokenIdB).slice(0, -2), - ]; - const fromAddresses = _.times(numTransfers, () => makerAddress); - const toAddresses = _.times(numTransfers, () => takerAddress); - const amounts = _.times(numTransfers, () => new BigNumber(1)); - - return expectRevertOrAlwaysFailingTransactionAsync( - erc721Proxy.batchTransferFrom.sendTransactionAsync(assetData, fromAddresses, toAddresses, amounts, { - from: notAuthorized, - }), - ); - }); - }); - - it('should have an id of 2', async () => { + it('should have an id of 0x08e937fa', async () => { const proxyId = await erc721Proxy.getProxyId.callAsync(); - expect(proxyId).to.equal(2); + const expectedProxyId = '0x08e937fa'; + expect(proxyId).to.equal(expectedProxyId); }); }); }); diff --git a/packages/contracts/test/asset_proxy_owner.ts b/packages/contracts/test/asset_proxy_owner.ts index aa4999e95..188cba5a3 100644 --- a/packages/contracts/test/asset_proxy_owner.ts +++ b/packages/contracts/test/asset_proxy_owner.ts @@ -11,6 +11,7 @@ import { SubmissionContractEventArgs, } from '../src/generated_contract_wrappers/asset_proxy_owner'; import { MixinAuthorizableContract } from '../src/generated_contract_wrappers/mixin_authorizable'; +import { TestAssetProxyOwnerContract } from '../src/generated_contract_wrappers/test_asset_proxy_owner'; import { artifacts } from '../src/utils/artifacts'; import { expectRevertOrAlwaysFailingTransactionAsync, @@ -34,7 +35,7 @@ describe('AssetProxyOwner', () => { let erc20Proxy: MixinAuthorizableContract; let erc721Proxy: MixinAuthorizableContract; - let multiSig: AssetProxyOwnerContract; + let testAssetProxyOwner: TestAssetProxyOwnerContract; let multiSigWrapper: MultiSigWrapper; before(async () => { @@ -58,8 +59,8 @@ describe('AssetProxyOwner', () => { txDefaults, ); const defaultAssetProxyContractAddresses: string[] = []; - multiSig = await AssetProxyOwnerContract.deployFrom0xArtifactAsync( - artifacts.AssetProxyOwner, + testAssetProxyOwner = await TestAssetProxyOwnerContract.deployFrom0xArtifactAsync( + artifacts.TestAssetProxyOwner, provider, txDefaults, owners, @@ -67,13 +68,17 @@ describe('AssetProxyOwner', () => { REQUIRED_APPROVALS, SECONDS_TIME_LOCKED, ); - multiSigWrapper = new MultiSigWrapper(multiSig, provider); + multiSigWrapper = new MultiSigWrapper(testAssetProxyOwner, provider); await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: initialOwner }), + await erc20Proxy.transferOwnership.sendTransactionAsync(testAssetProxyOwner.address, { + from: initialOwner, + }), constants.AWAIT_TRANSACTION_MINED_MS, ); await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: initialOwner }), + await erc721Proxy.transferOwnership.sendTransactionAsync(testAssetProxyOwner.address, { + from: initialOwner, + }), constants.AWAIT_TRANSACTION_MINED_MS, ); }); @@ -117,24 +122,28 @@ describe('AssetProxyOwner', () => { }); }); - describe('isFunctionRemoveAuthorizedAddress', () => { - it('should throw if data is not for removeAuthorizedAddress', async () => { + describe('isFunctionRemoveAuthorizedAddressAtIndex', () => { + it('should return false if data is not for removeAuthorizedAddressAtIndex', async () => { const notRemoveAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData( owners[0], ); - return expectRevertOrContractCallFailedAsync( - multiSig.isFunctionRemoveAuthorizedAddress.callAsync(notRemoveAuthorizedAddressData), + + const isFunctionRemoveAuthorizedAddressAtIndex = await testAssetProxyOwner.isFunctionRemoveAuthorizedAddressAtIndex.callAsync( + notRemoveAuthorizedAddressData, ); + expect(isFunctionRemoveAuthorizedAddressAtIndex).to.be.false(); }); - it('should return true if data is for removeAuthorizedAddress', async () => { - const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData( + it('should return true if data is for removeAuthorizedAddressAtIndex', async () => { + const index = new BigNumber(0); + const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( owners[0], + index, ); - const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress.callAsync( - removeAuthorizedAddressData, + const isFunctionRemoveAuthorizedAddressAtIndex = await testAssetProxyOwner.isFunctionRemoveAuthorizedAddressAtIndex.callAsync( + removeAuthorizedAddressAtIndexData, ); - expect(isFunctionRemoveAuthorizedAddress).to.be.true(); + expect(isFunctionRemoveAuthorizedAddressAtIndex).to.be.true(); }); }); @@ -142,19 +151,21 @@ describe('AssetProxyOwner', () => { it('should throw if not called by multisig', async () => { const isRegistered = true; return expectRevertOrAlwaysFailingTransactionAsync( - multiSig.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, { from: owners[0] }), + testAssetProxyOwner.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, { + from: owners[0], + }), ); }); it('should register an address if called by multisig after timelock', async () => { const addressToRegister = erc20Proxy.address; const isRegistered = true; - const registerAssetProxyData = multiSig.registerAssetProxy.getABIEncodedTransactionData( + const registerAssetProxyData = testAssetProxyOwner.registerAssetProxy.getABIEncodedTransactionData( addressToRegister, isRegistered, ); const submitTxRes = await multiSigWrapper.submitTransactionAsync( - multiSig.address, + testAssetProxyOwner.address, registerAssetProxyData, owners[0], ); @@ -170,19 +181,21 @@ describe('AssetProxyOwner', () => { expect(registerLog.args.assetProxyContract).to.equal(addressToRegister); expect(registerLog.args.isRegistered).to.equal(isRegistered); - const isAssetProxyRegistered = await multiSig.isAssetProxyRegistered.callAsync(addressToRegister); + const isAssetProxyRegistered = await testAssetProxyOwner.isAssetProxyRegistered.callAsync( + addressToRegister, + ); expect(isAssetProxyRegistered).to.equal(isRegistered); }); it('should fail if registering a null address', async () => { const addressToRegister = constants.NULL_ADDRESS; const isRegistered = true; - const registerAssetProxyData = multiSig.registerAssetProxy.getABIEncodedTransactionData( + const registerAssetProxyData = testAssetProxyOwner.registerAssetProxy.getABIEncodedTransactionData( addressToRegister, isRegistered, ); const submitTxRes = await multiSigWrapper.submitTransactionAsync( - multiSig.address, + testAssetProxyOwner.address, registerAssetProxyData, owners[0], ); @@ -196,22 +209,26 @@ describe('AssetProxyOwner', () => { const failureLog = executeTxRes.logs[0] as LogWithDecodedArgs<ExecutionFailureContractEventArgs>; expect(failureLog.args.transactionId).to.be.bignumber.equal(txId); - const isAssetProxyRegistered = await multiSig.isAssetProxyRegistered.callAsync(addressToRegister); + const isAssetProxyRegistered = await testAssetProxyOwner.isAssetProxyRegistered.callAsync( + addressToRegister, + ); expect(isAssetProxyRegistered).to.equal(false); }); }); - describe('executeRemoveAuthorizedAddress', () => { + describe('Calling removeAuthorizedAddressAtIndex', () => { + const erc20Index = new BigNumber(0); + const erc721Index = new BigNumber(1); before('authorize both proxies and register erc20 proxy', async () => { // Only register ERC20 proxy const addressToRegister = erc20Proxy.address; const isRegistered = true; - const registerAssetProxyData = multiSig.registerAssetProxy.getABIEncodedTransactionData( + const registerAssetProxyData = testAssetProxyOwner.registerAssetProxy.getABIEncodedTransactionData( addressToRegister, isRegistered, ); const registerAssetProxySubmitRes = await multiSigWrapper.submitTransactionAsync( - multiSig.address, + testAssetProxyOwner.address, registerAssetProxyData, owners[0], ); @@ -248,113 +265,180 @@ describe('AssetProxyOwner', () => { await multiSigWrapper.executeTransactionAsync(erc721AddAuthorizedAddressTxId, owners[0]); }); - it('should throw without the required confirmations', async () => { - const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData( - authorized, - ); - const res = await multiSigWrapper.submitTransactionAsync( - erc20Proxy.address, - removeAuthorizedAddressData, - owners[0], - ); - const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; - const txId = log.args.transactionId; - - return expectRevertOrAlwaysFailingTransactionAsync( - multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }), - ); - }); - - it('should throw if tx destination is not registered', async () => { - const removeAuthorizedAddressData = erc721Proxy.removeAuthorizedAddress.getABIEncodedTransactionData( - authorized, - ); - const res = await multiSigWrapper.submitTransactionAsync( - erc721Proxy.address, - removeAuthorizedAddressData, - owners[0], - ); - const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; - const txId = log.args.transactionId; - - await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - - return expectRevertOrAlwaysFailingTransactionAsync( - multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }), - ); - }); - - it('should throw if tx data is not for removeAuthorizedAddress', async () => { - const newAuthorized = owners[1]; - const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData( - newAuthorized, - ); - const res = await multiSigWrapper.submitTransactionAsync( - erc20Proxy.address, - addAuthorizedAddressData, - owners[0], - ); - const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; - const txId = log.args.transactionId; - - await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - - return expectRevertOrAlwaysFailingTransactionAsync( - multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }), - ); - }); - - it('should execute removeAuthorizedAddress for registered address if fully confirmed', async () => { - const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData( - authorized, - ); - const submitRes = await multiSigWrapper.submitTransactionAsync( - erc20Proxy.address, - removeAuthorizedAddressData, - owners[0], - ); - const submitLog = submitRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; - const txId = submitLog.args.transactionId; - - await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - - const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAsync(txId, owners[0]); - const execLog = execRes.logs[0] as LogWithDecodedArgs<ExecutionContractEventArgs>; - expect(execLog.args.transactionId).to.be.bignumber.equal(txId); - - const tx = await multiSig.transactions.callAsync(txId); - const isExecuted = tx[3]; - expect(isExecuted).to.equal(true); - - const isAuthorized = await erc20Proxy.authorized.callAsync(authorized); - expect(isAuthorized).to.equal(false); + describe('validRemoveAuthorizedAddressAtIndexTx', () => { + it('should revert if data is not for removeAuthorizedAddressAtIndex and proxy is registered', async () => { + const notRemoveAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData( + authorized, + ); + const submitTxRes = await multiSigWrapper.submitTransactionAsync( + erc20Proxy.address, + notRemoveAuthorizedAddressData, + owners[0], + ); + const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; + const txId = log.args.transactionId; + return expectRevertOrContractCallFailedAsync( + testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId), + ); + }); + + it('should return true if data is for removeAuthorizedAddressAtIndex and proxy is registered', async () => { + const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( + authorized, + erc20Index, + ); + const submitTxRes = await multiSigWrapper.submitTransactionAsync( + erc20Proxy.address, + removeAuthorizedAddressAtIndexData, + owners[0], + ); + const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; + const txId = log.args.transactionId; + const isValidRemoveAuthorizedAddressAtIndexTx = await testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync( + txId, + ); + expect(isValidRemoveAuthorizedAddressAtIndexTx).to.be.true(); + }); + + it('should revert if data is for removeAuthorizedAddressAtIndex and proxy is not registered', async () => { + const removeAuthorizedAddressAtIndexData = erc721Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( + authorized, + erc721Index, + ); + const submitTxRes = await multiSigWrapper.submitTransactionAsync( + erc721Proxy.address, + removeAuthorizedAddressAtIndexData, + owners[0], + ); + const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; + const txId = log.args.transactionId; + return expectRevertOrContractCallFailedAsync( + testAssetProxyOwner.testValidRemoveAuthorizedAddressAtIndexTx.callAsync(txId), + ); + }); }); - it('should throw if already executed', async () => { - const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData( - authorized, - ); - const submitRes = await multiSigWrapper.submitTransactionAsync( - erc20Proxy.address, - removeAuthorizedAddressData, - owners[0], - ); - const submitLog = submitRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; - const txId = submitLog.args.transactionId; - - await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - - const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAsync(txId, owners[0]); - const execLog = execRes.logs[0] as LogWithDecodedArgs<ExecutionContractEventArgs>; - expect(execLog.args.transactionId).to.be.bignumber.equal(txId); - - const tx = await multiSig.transactions.callAsync(txId); - const isExecuted = tx[3]; - expect(isExecuted).to.equal(true); - - return expectRevertOrAlwaysFailingTransactionAsync( - multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }), - ); + describe('executeRemoveAuthorizedAddressAtIndex', () => { + it('should throw without the required confirmations', async () => { + const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( + authorized, + erc20Index, + ); + const res = await multiSigWrapper.submitTransactionAsync( + erc20Proxy.address, + removeAuthorizedAddressAtIndexData, + owners[0], + ); + const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; + const txId = log.args.transactionId; + + return expectRevertOrAlwaysFailingTransactionAsync( + testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { + from: owners[1], + }), + ); + }); + + it('should throw if tx destination is not registered', async () => { + const removeAuthorizedAddressAtIndexData = erc721Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( + authorized, + erc721Index, + ); + const res = await multiSigWrapper.submitTransactionAsync( + erc721Proxy.address, + removeAuthorizedAddressAtIndexData, + owners[0], + ); + const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; + const txId = log.args.transactionId; + + await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); + + return expectRevertOrAlwaysFailingTransactionAsync( + testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { + from: owners[1], + }), + ); + }); + + it('should throw if tx data is not for removeAuthorizedAddressAtIndex', async () => { + const newAuthorized = owners[1]; + const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData( + newAuthorized, + ); + const res = await multiSigWrapper.submitTransactionAsync( + erc20Proxy.address, + addAuthorizedAddressData, + owners[0], + ); + const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; + const txId = log.args.transactionId; + + await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); + + return expectRevertOrAlwaysFailingTransactionAsync( + testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { + from: owners[1], + }), + ); + }); + + it('should execute removeAuthorizedAddressAtIndex for registered address if fully confirmed', async () => { + const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( + authorized, + erc20Index, + ); + const submitRes = await multiSigWrapper.submitTransactionAsync( + erc20Proxy.address, + removeAuthorizedAddressAtIndexData, + owners[0], + ); + const submitLog = submitRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; + const txId = submitLog.args.transactionId; + + await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); + + const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, owners[0]); + const execLog = execRes.logs[0] as LogWithDecodedArgs<ExecutionContractEventArgs>; + expect(execLog.args.transactionId).to.be.bignumber.equal(txId); + + const tx = await testAssetProxyOwner.transactions.callAsync(txId); + const isExecuted = tx[3]; + expect(isExecuted).to.equal(true); + + const isAuthorized = await erc20Proxy.authorized.callAsync(authorized); + expect(isAuthorized).to.equal(false); + }); + + it('should throw if already executed', async () => { + const removeAuthorizedAddressAtIndexData = erc20Proxy.removeAuthorizedAddressAtIndex.getABIEncodedTransactionData( + authorized, + erc20Index, + ); + const submitRes = await multiSigWrapper.submitTransactionAsync( + erc20Proxy.address, + removeAuthorizedAddressAtIndexData, + owners[0], + ); + const submitLog = submitRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>; + const txId = submitLog.args.transactionId; + + await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); + + const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, owners[0]); + const execLog = execRes.logs[0] as LogWithDecodedArgs<ExecutionContractEventArgs>; + expect(execLog.args.transactionId).to.be.bignumber.equal(txId); + + const tx = await testAssetProxyOwner.transactions.callAsync(txId); + const isExecuted = tx[3]; + expect(isExecuted).to.equal(true); + + return expectRevertOrAlwaysFailingTransactionAsync( + testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, { + from: owners[1], + }), + ); + }); }); }); }); diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index ff652d3aa..36f6fa8d3 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils'; -import { AssetProxyId, SignedOrder } from '@0xproject/types'; +import { AssetProxyId, RevertReason, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -17,7 +17,7 @@ import { FillContractEventArgs, } from '../../src/generated_contract_wrappers/exchange'; import { artifacts } from '../../src/utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; +import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -86,7 +86,7 @@ describe('Exchange core', () => { artifacts.Exchange, provider, txDefaults, - zrxToken.address, + assetProxyUtils.encodeERC20AssetData(zrxToken.address), ); exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); @@ -415,8 +415,9 @@ describe('Exchange core', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.InvalidTaker, ); }); @@ -432,8 +433,9 @@ describe('Exchange core', () => { const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]); const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; signedOrder.signature = invalidSigHex; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.InvalidOrderSignature, ); }); @@ -442,8 +444,9 @@ describe('Exchange core', () => { makerAssetAmount: new BigNumber(0), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.OrderUnfillable, ); }); @@ -452,18 +455,20 @@ describe('Exchange core', () => { takerAssetAmount: new BigNumber(0), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.OrderUnfillable, ); }); it('should throw if takerAssetFillAmount is 0', async () => { signedOrder = orderFactory.newSignedOrder(); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount: new BigNumber(0), }), + RevertReason.InvalidTakerAmount, ); }); @@ -472,8 +477,9 @@ describe('Exchange core', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.TransferFailed, ); }); @@ -481,8 +487,9 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.TransferFailed, ); }); @@ -493,8 +500,9 @@ describe('Exchange core', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.TransferFailed, ); }); @@ -505,8 +513,9 @@ describe('Exchange core', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.TransferFailed, ); }); @@ -514,16 +523,18 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.OrderUnfillable, ); }); it('should throw if no value is filled', async () => { signedOrder = orderFactory.newSignedOrder(); await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.OrderUnfillable, ); }); }); @@ -535,8 +546,9 @@ describe('Exchange core', () => { }); it('should throw if not sent by maker', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress), + RevertReason.InvalidMaker, ); }); @@ -545,8 +557,9 @@ describe('Exchange core', () => { makerAssetAmount: new BigNumber(0), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), + RevertReason.OrderUnfillable, ); }); @@ -555,17 +568,19 @@ describe('Exchange core', () => { takerAssetAmount: new BigNumber(0), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), + RevertReason.OrderUnfillable, ); }); it('should be able to cancel a full order', async () => { await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), }), + RevertReason.OrderUnfillable, ); }); @@ -586,8 +601,9 @@ describe('Exchange core', () => { it('should throw if already cancelled', async () => { await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), + RevertReason.OrderUnfillable, ); }); @@ -595,8 +611,9 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), + RevertReason.OrderUnfillable, ); }); @@ -612,10 +629,11 @@ describe('Exchange core', () => { }); const fillTakerAssetAmount2 = new BigNumber(1); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount: fillTakerAssetAmount2, }), + RevertReason.RoundingError, ); }); }); @@ -625,16 +643,18 @@ describe('Exchange core', () => { const orderEpoch = new BigNumber(1); await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); const lesserOrderEpoch = new BigNumber(0); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.cancelOrdersUpToAsync(lesserOrderEpoch, makerAddress), + RevertReason.InvalidNewOrderEpoch, ); }); it('should fail to set orderEpoch equal to existing orderEpoch', async () => { const orderEpoch = new BigNumber(1); await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress), + RevertReason.InvalidNewOrderEpoch, ); }); @@ -748,8 +768,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), + RevertReason.TransferFailed, ); }); @@ -770,8 +791,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.not.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), + RevertReason.TransferFailed, ); }); @@ -792,8 +814,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), + RevertReason.InvalidAmount, ); }); @@ -814,19 +837,40 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), + RevertReason.InvalidAmount, ); }); it('should throw on partial fill', async () => { // Construct Exchange parameters const makerAssetId = erc721MakerAssetIds[0]; - const takerAssetId = erc721TakerAssetIds[0]; signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), + takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress), + }); + // Call Exchange + const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); + return expectRevertReasonOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), + RevertReason.RoundingError, + ); + }); + + it('should throw if assetData has a length < 132', async () => { + // Construct Exchange parameters + const makerAssetId = erc721MakerAssetIds[0]; + const takerAssetId = erc721TakerAssetIds[0]; + const makerAssetData = assetProxyUtils + .encodeERC721AssetData(erc721Token.address, makerAssetId) + .slice(0, -2); + signedOrder = orderFactory.newSignedOrder({ + makerAssetAmount: new BigNumber(1), + takerAssetAmount: new BigNumber(1), + makerAssetData, takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), }); // Verify pre-conditions @@ -836,8 +880,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), + RevertReason.LengthGreaterThan131Required, ); }); diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts index abbfd7ac7..8adbee6a3 100644 --- a/packages/contracts/test/exchange/dispatcher.ts +++ b/packages/contracts/test/exchange/dispatcher.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetProxyUtils } from '@0xproject/order-utils'; -import { AssetProxyId } from '@0xproject/types'; +import { AssetProxyId, RevertReason } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -9,7 +9,7 @@ import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c2 import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy'; import { TestAssetProxyDispatcherContract } from '../../src/generated_contract_wrappers/test_asset_proxy_dispatcher'; import { artifacts } from '../../src/utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; +import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -175,13 +175,14 @@ describe('AssetProxyDispatcher', () => { const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); expect(proxyAddress).to.be.equal(erc20Proxy.address); // The following transaction will throw because the currentAddress is no longer constants.NULL_ADDRESS - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( AssetProxyId.ERC20, erc20Proxy.address, constants.NULL_ADDRESS, { from: owner }, ), + RevertReason.AssetProxyMismatch, ); }); @@ -216,25 +217,27 @@ describe('AssetProxyDispatcher', () => { it('should throw if requesting address is not owner', async () => { const prevProxyAddress = constants.NULL_ADDRESS; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( AssetProxyId.ERC20, erc20Proxy.address, prevProxyAddress, { from: notOwner }, ), + RevertReason.OnlyContractOwner, ); }); it('should throw if attempting to register a proxy to the incorrect id', async () => { const prevProxyAddress = constants.NULL_ADDRESS; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( AssetProxyId.ERC721, erc20Proxy.address, prevProxyAddress, { from: owner }, ), + RevertReason.AssetProxyIdMismatch, ); }); }); @@ -276,14 +279,13 @@ describe('AssetProxyDispatcher', () => { ); // Construct metadata for ERC20 proxy const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); + // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(10); await web3Wrapper.awaitTransactionSuccessAsync( await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, - AssetProxyId.ERC20, + encodedAssetData, makerAddress, takerAddress, amount, @@ -304,18 +306,17 @@ describe('AssetProxyDispatcher', () => { it('should throw if dispatching to unregistered proxy', async () => { // Construct metadata for ERC20 proxy const encodedAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); - const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(10); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( - encodedAssetDataWithoutProxyId, - AssetProxyId.ERC20, + encodedAssetData, makerAddress, takerAddress, amount, { from: owner }, ), + RevertReason.AssetProxyDoesNotExist, ); }); }); diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index b8dca04fd..bcf691471 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetProxyUtils } from '@0xproject/order-utils'; -import { AssetProxyId } from '@0xproject/types'; +import { AssetProxyId, RevertReason } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -12,7 +12,7 @@ import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c2 import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy'; import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange'; import { artifacts } from '../../src/utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; +import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -96,7 +96,7 @@ describe('matchOrders', () => { artifacts.Exchange, provider, txDefaults, - zrxToken.address, + assetProxyUtils.encodeERC20AssetData(zrxToken.address), ); exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); @@ -598,8 +598,9 @@ describe('matchOrders', () => { // Cancel left order await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress); // Match orders - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), + RevertReason.OrderUnfillable, ); }); @@ -622,8 +623,9 @@ describe('matchOrders', () => { // Cancel right order await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress); // Match orders - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), + RevertReason.OrderUnfillable, ); }); @@ -644,7 +646,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, @@ -652,6 +654,7 @@ describe('matchOrders', () => { erc20BalancesByOwner, erc721TokenIdsByOwner, ), + RevertReason.NegativeSpreadRequired, ); }); @@ -672,7 +675,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, @@ -680,6 +683,11 @@ describe('matchOrders', () => { erc20BalancesByOwner, erc721TokenIdsByOwner, ), + // We are assuming assetData fields of the right order are the + // reverse of the left order, rather than checking equality. This + // saves a bunch of gas, but as a result if the assetData fields are + // off then the failure ends up happening at signature validation + RevertReason.InvalidOrderSignature, ); }); @@ -702,7 +710,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, @@ -710,6 +718,7 @@ describe('matchOrders', () => { erc20BalancesByOwner, erc721TokenIdsByOwner, ), + RevertReason.InvalidOrderSignature, ); }); diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 8e221e3f1..21cc343b3 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { addSignedMessagePrefix, assetProxyUtils, MessagePrefixType, orderHashUtils } from '@0xproject/order-utils'; -import { SignatureType, SignedOrder } from '@0xproject/types'; +import { RevertReason, SignatureType, SignedOrder } from '@0xproject/types'; import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); @@ -107,7 +107,7 @@ describe('MixinSignatureValidator', () => { signedOrder.makerAddress, emptySignature, ), - constants.EXCHANGE_LENGTH_GREATER_THAN_0_REQUIRED, + RevertReason.LengthGreaterThan0Required, ); }); @@ -121,7 +121,7 @@ describe('MixinSignatureValidator', () => { signedOrder.makerAddress, unsupportedSignatureHex, ), - constants.EXCHANGE_SIGNATURE_UNSUPPORTED, + RevertReason.SignatureUnsupported, ); }); @@ -134,7 +134,7 @@ describe('MixinSignatureValidator', () => { signedOrder.makerAddress, unsupportedSignatureHex, ), - constants.EXCHANGE_SIGNATURE_ILLEGAL, + RevertReason.SignatureIllegal, ); }); @@ -161,7 +161,7 @@ describe('MixinSignatureValidator', () => { signedOrder.makerAddress, signatureHex, ), - constants.EXCHANGE_LENGTH_0_REQUIRED, + RevertReason.Length0Required, ); }); diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts index 9a625880c..b0c08e46e 100644 --- a/packages/contracts/test/exchange/transactions.ts +++ b/packages/contracts/test/exchange/transactions.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; -import { AssetProxyId, OrderWithoutExchangeAddress, SignedOrder } from '@0xproject/types'; +import { AssetProxyId, OrderWithoutExchangeAddress, RevertReason, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -10,7 +10,7 @@ import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange import { ExchangeWrapperContract } from '../../src/generated_contract_wrappers/exchange_wrapper'; import { WhitelistContract } from '../../src/generated_contract_wrappers/whitelist'; import { artifacts } from '../../src/utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; +import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -59,6 +59,12 @@ describe('Exchange transactions', () => { after(async () => { await blockchainLifecycle.revertAsync(); }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); before(async () => { const accounts = await web3Wrapper.getAvailableAddressesAsync(); const usedAddresses = ([owner, senderAddress, makerAddress, takerAddress, feeRecipientAddress] = accounts); @@ -73,7 +79,7 @@ describe('Exchange transactions', () => { artifacts.Exchange, provider, txDefaults, - zrxToken.address, + assetProxyUtils.encodeERC20AssetData(zrxToken.address), ); exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); @@ -101,13 +107,6 @@ describe('Exchange transactions', () => { makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address); takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address); }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('executeTransaction', () => { describe('fillOrder', () => { let takerAssetFillAmount: BigNumber; @@ -126,8 +125,9 @@ describe('Exchange transactions', () => { }); it('should throw if not called by specified sender', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.executeTransactionAsync(signedTx, takerAddress), + RevertReason.FailedExecution, ); }); @@ -168,8 +168,9 @@ describe('Exchange transactions', () => { it('should throw if the a 0x transaction with the same transactionHash has already been executed', async () => { await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.executeTransactionAsync(signedTx, senderAddress), + RevertReason.InvalidTxHash, ); }); @@ -187,15 +188,17 @@ describe('Exchange transactions', () => { }); it('should throw if not called by specified sender', async () => { - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.executeTransactionAsync(signedTx, makerAddress), + RevertReason.FailedExecution, ); }); it('should cancel the order when signed by maker and called by sender', async () => { await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, senderAddress), + RevertReason.OrderUnfillable, ); }); }); @@ -238,7 +241,7 @@ describe('Exchange transactions', () => { signedOrder.signature, ); const signedFillTx = takerTransactionFactory.newSignedTransaction(fillData); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapperContract.fillOrder.sendTransactionAsync( orderWithoutExchangeAddress, takerAssetFillAmount, @@ -247,6 +250,7 @@ describe('Exchange transactions', () => { signedFillTx.signature, { from: takerAddress }, ), + RevertReason.FailedExecution, ); }); @@ -357,7 +361,7 @@ describe('Exchange transactions', () => { orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( whitelist.fillOrderIfWhitelisted.sendTransactionAsync( orderWithoutExchangeAddress, takerAssetFillAmount, @@ -365,6 +369,7 @@ describe('Exchange transactions', () => { signedOrder.signature, { from: takerAddress }, ), + RevertReason.MakerNotWhitelisted, ); }); @@ -378,7 +383,7 @@ describe('Exchange transactions', () => { orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( whitelist.fillOrderIfWhitelisted.sendTransactionAsync( orderWithoutExchangeAddress, takerAssetFillAmount, @@ -386,6 +391,7 @@ describe('Exchange transactions', () => { signedOrder.signature, { from: takerAddress }, ), + RevertReason.TakerNotWhitelisted, ); }); diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index 703f644b8..9ee68f94d 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetProxyUtils } from '@0xproject/order-utils'; -import { AssetProxyId, SignedOrder } from '@0xproject/types'; +import { AssetProxyId, RevertReason, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -12,7 +12,7 @@ import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c2 import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy'; import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange'; import { artifacts } from '../../src/utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; +import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -80,7 +80,7 @@ describe('Exchange wrappers', () => { artifacts.Exchange, provider, txDefaults, - zrxToken.address, + assetProxyUtils.encodeERC20AssetData(zrxToken.address), ); exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); @@ -165,13 +165,14 @@ describe('Exchange wrappers', () => { ); }); - it('should throw if an signedOrder is expired', async () => { + it('should throw if a signedOrder is expired', async () => { const signedOrder = orderFactory.newSignedOrder({ expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), + RevertReason.OrderUnfillable, ); }); @@ -182,8 +183,9 @@ describe('Exchange wrappers', () => { takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), }); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), + RevertReason.CompleteFillFailed, ); }); }); @@ -494,10 +496,11 @@ describe('Exchange wrappers', () => { await exchangeWrapper.fillOrKillOrderAsync(signedOrders[0], takerAddress); - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.batchFillOrKillOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmounts, }), + RevertReason.OrderUnfillable, ); }); }); @@ -687,7 +690,7 @@ describe('Exchange wrappers', () => { expect(newBalances).to.be.deep.equal(erc20Balances); }); - it('should throw when an signedOrder does not use the same takerAssetAddress', async () => { + it('should throw when a signedOrder does not use the same takerAssetAddress', async () => { signedOrders = [ orderFactory.newSignedOrder(), orderFactory.newSignedOrder({ @@ -696,10 +699,13 @@ describe('Exchange wrappers', () => { orderFactory.newSignedOrder(), ]; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), }), + // We simply use the takerAssetData from the first order for all orders. + // If they are not the same, the contract throws when validating the order signature + RevertReason.InvalidOrderSignature, ); }); }); @@ -902,7 +908,7 @@ describe('Exchange wrappers', () => { expect(newBalances).to.be.deep.equal(erc20Balances); }); - it('should throw when an signedOrder does not use the same makerAssetAddress', async () => { + it('should throw when a signedOrder does not use the same makerAssetAddress', async () => { signedOrders = [ orderFactory.newSignedOrder(), orderFactory.newSignedOrder({ @@ -911,10 +917,11 @@ describe('Exchange wrappers', () => { orderFactory.newSignedOrder(), ]; - return expectRevertOrAlwaysFailingTransactionAsync( + return expectRevertReasonOrAlwaysFailingTransactionAsync( exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, { makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), }), + RevertReason.InvalidOrderSignature, ); }); }); diff --git a/packages/contracts/test/libraries/lib_bytes.ts b/packages/contracts/test/libraries/lib_bytes.ts index a31a4789c..3d4058cbc 100644 --- a/packages/contracts/test/libraries/lib_bytes.ts +++ b/packages/contracts/test/libraries/lib_bytes.ts @@ -1,5 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { RevertReason } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import BN = require('bn.js'); import * as chai from 'chai'; @@ -17,6 +18,12 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +// BUG: Ideally we would use Buffer.from(memory).toString('hex') +// https://github.com/Microsoft/TypeScript/issues/23155 +const toHex = (buf: Uint8Array): string => buf.reduce((a, v) => a + ('00' + v.toString(16)).slice(-2), '0x'); + +const fromHex = (str: string): Uint8Array => Uint8Array.from(Buffer.from(str.slice(2), 'hex')); + describe('LibBytes', () => { let libBytes: TestLibBytesContract; const byteArrayShorterThan32Bytes = '0x012345'; @@ -95,7 +102,7 @@ describe('LibBytes', () => { it('should revert if length is 0', async () => { return expectRevertOrOtherErrorAsync( libBytes.publicPopLastByte.callAsync(constants.NULL_BYTES), - constants.LIB_BYTES_GREATER_THAN_ZERO_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterThanZeroLengthRequired, ); }); it('should pop the last byte from the input and return it', async () => { @@ -111,7 +118,7 @@ describe('LibBytes', () => { it('should revert if length is less than 20', async () => { return expectRevertOrOtherErrorAsync( libBytes.publicPopLast20Bytes.callAsync(byteArrayShorterThan20Bytes), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, ); }); it('should pop the last 20 bytes from the input and return it', async () => { @@ -123,48 +130,55 @@ describe('LibBytes', () => { }); }); - describe('areBytesEqual', () => { + describe('equals', () => { it('should return true if byte arrays are equal (both arrays < 32 bytes)', async () => { - const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync( + const isEqual = await libBytes.publicEquals.callAsync( byteArrayShorterThan32Bytes, byteArrayShorterThan32Bytes, ); - return expect(areBytesEqual).to.be.true(); + return expect(isEqual).to.be.true(); }); it('should return true if byte arrays are equal (both arrays > 32 bytes)', async () => { - const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync( + const isEqual = await libBytes.publicEquals.callAsync( byteArrayLongerThan32Bytes, byteArrayLongerThan32Bytes, ); - return expect(areBytesEqual).to.be.true(); + return expect(isEqual).to.be.true(); }); it('should return false if byte arrays are not equal (first array < 32 bytes, second array > 32 bytes)', async () => { - const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync( + const isEqual = await libBytes.publicEquals.callAsync( byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes, ); - return expect(areBytesEqual).to.be.false(); + return expect(isEqual).to.be.false(); }); it('should return false if byte arrays are not equal (first array > 32 bytes, second array < 32 bytes)', async () => { - const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync( + const isEqual = await libBytes.publicEquals.callAsync( byteArrayLongerThan32Bytes, byteArrayShorterThan32Bytes, ); - return expect(areBytesEqual).to.be.false(); + return expect(isEqual).to.be.false(); }); it('should return false if byte arrays are not equal (same length, but a byte in first word differs)', async () => { - const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync( + const isEqual = await libBytes.publicEquals.callAsync( byteArrayLongerThan32BytesFirstBytesSwapped, byteArrayLongerThan32Bytes, ); - return expect(areBytesEqual).to.be.false(); + return expect(isEqual).to.be.false(); }); it('should return false if byte arrays are not equal (same length, but a byte in last word differs)', async () => { - const areBytesEqual = await libBytes.publicAreBytesEqual.callAsync( + const isEqual = await libBytes.publicEquals.callAsync( byteArrayLongerThan32BytesLastBytesSwapped, byteArrayLongerThan32Bytes, ); - return expect(areBytesEqual).to.be.false(); + return expect(isEqual).to.be.false(); + }); + + describe('should ignore trailing data', () => { + it('should return true when both < 32 bytes', async () => { + const isEqual = await libBytes.publicEqualsPop1.callAsync('0x0102', '0x0103'); + return expect(isEqual).to.be.true(); + }); }); }); @@ -172,7 +186,7 @@ describe('LibBytes', () => { it('should revert if dest is shorter than source', async () => { return expectRevertOrOtherErrorAsync( libBytes.publicDeepCopyBytes.callAsync(byteArrayShorterThan32Bytes, byteArrayLongerThan32Bytes), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualToSourceBytesLengthRequired, ); }); it('should overwrite dest with source if source and dest have equal length', async () => { @@ -225,7 +239,7 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicReadAddress.callAsync(shortByteArray, offset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => { @@ -233,7 +247,7 @@ describe('LibBytes', () => { const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicReadAddress.callAsync(byteArray, badOffset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, ); }); }); @@ -269,7 +283,7 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicWriteAddress.callAsync(byteArrayShorterThan20Bytes, offset, testAddress), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold an address', async () => { @@ -277,7 +291,7 @@ describe('LibBytes', () => { const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicWriteAddress.callAsync(byteArray, badOffset, testAddress), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo20LengthRequired, ); }); }); @@ -301,14 +315,14 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => { const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicReadBytes32.callAsync(testBytes32, badOffset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); }); @@ -344,7 +358,7 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicWriteBytes32.callAsync(byteArrayShorterThan32Bytes, offset, testBytes32), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32', async () => { @@ -352,7 +366,7 @@ describe('LibBytes', () => { const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicWriteBytes32.callAsync(byteArray, badOffset, testBytes32), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); }); @@ -380,7 +394,7 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => { @@ -390,7 +404,7 @@ describe('LibBytes', () => { const badOffset = new BigNumber(testUint256AsBuffer.byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicReadUint256.callAsync(byteArray, badOffset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); }); @@ -430,7 +444,7 @@ describe('LibBytes', () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( libBytes.publicWriteUint256.callAsync(byteArrayShorterThan32Bytes, offset, testUint256), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold a uint256', async () => { @@ -438,31 +452,31 @@ describe('LibBytes', () => { const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); return expectRevertOrOtherErrorAsync( libBytes.publicWriteUint256.callAsync(byteArray, badOffset, testUint256), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); }); - describe('readFirst4', () => { + describe('readBytes4', () => { // AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0' it('should revert if byte array has a length < 4', async () => { const byteArrayLessThan4Bytes = '0x010101'; return expectRevertOrOtherErrorAsync( - libBytes.publicReadFirst4.callAsync(byteArrayLessThan4Bytes), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED, + libBytes.publicReadBytes4.callAsync(byteArrayLessThan4Bytes, new BigNumber(0)), + RevertReason.LibBytesGreaterOrEqualTo4LengthRequired, ); }); it('should return the first 4 bytes of a byte array of arbitrary length', async () => { - const first4Bytes = await libBytes.publicReadFirst4.callAsync(byteArrayLongerThan32Bytes); + const first4Bytes = await libBytes.publicReadBytes4.callAsync(byteArrayLongerThan32Bytes, new BigNumber(0)); const expectedFirst4Bytes = byteArrayLongerThan32Bytes.slice(0, 10); expect(first4Bytes).to.equal(expectedFirst4Bytes); }); }); - describe('readBytes', () => { + describe('readBytesWithLength', () => { it('should successfully read short, nested array of bytes when it takes up the whole array', async () => { const testBytesOffset = new BigNumber(0); - const bytes = await libBytes.publicReadBytes.callAsync(shortTestBytes, testBytesOffset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(shortTestBytes, testBytesOffset); return expect(bytes).to.be.equal(shortData); }); it('should successfully read short, nested array of bytes when it is offset in the array', async () => { @@ -470,12 +484,12 @@ describe('LibBytes', () => { const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, shortTestBytesAsBuffer]); const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer); const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength); - const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(combinedByteArray, testUint256Offset); return expect(bytes).to.be.equal(shortData); }); it('should successfully read a nested array of bytes - one word in length - when it takes up the whole array', async () => { const testBytesOffset = new BigNumber(0); - const bytes = await libBytes.publicReadBytes.callAsync(wordOfTestBytes, testBytesOffset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(wordOfTestBytes, testBytesOffset); return expect(bytes).to.be.equal(wordOfData); }); it('should successfully read a nested array of bytes - one word in length - when it is offset in the array', async () => { @@ -483,12 +497,12 @@ describe('LibBytes', () => { const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, wordOfTestBytesAsBuffer]); const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer); const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength); - const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(combinedByteArray, testUint256Offset); return expect(bytes).to.be.equal(wordOfData); }); it('should successfully read long, nested array of bytes when it takes up the whole array', async () => { const testBytesOffset = new BigNumber(0); - const bytes = await libBytes.publicReadBytes.callAsync(longTestBytes, testBytesOffset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(longTestBytes, testBytesOffset); return expect(bytes).to.be.equal(longData); }); it('should successfully read long, nested array of bytes when it is offset in the array', async () => { @@ -496,46 +510,50 @@ describe('LibBytes', () => { const combinedByteArrayBuffer = Buffer.concat([prefixByteArrayBuffer, longTestBytesAsBuffer]); const combinedByteArray = ethUtil.bufferToHex(combinedByteArrayBuffer); const testUint256Offset = new BigNumber(prefixByteArrayBuffer.byteLength); - const bytes = await libBytes.publicReadBytes.callAsync(combinedByteArray, testUint256Offset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(combinedByteArray, testUint256Offset); return expect(bytes).to.be.equal(longData); }); it('should fail if the byte array is too short to hold the length of a nested byte array', async () => { // The length of the nested array is 32 bytes. By storing less than 32 bytes, a length cannot be read. const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( - libBytes.publicReadBytes.callAsync(byteArrayShorterThan32Bytes, offset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, offset), + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); it('should fail if we store a nested byte array length, without a nested byte array', async () => { const offset = new BigNumber(0); return expectRevertOrOtherErrorAsync( - libBytes.publicReadBytes.callAsync(testBytes32, offset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED, + libBytes.publicReadBytesWithLength.callAsync(testBytes32, offset), + RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array', async () => { const badOffset = new BigNumber(ethUtil.toBuffer(byteArrayShorterThan32Bytes).byteLength); return expectRevertOrOtherErrorAsync( - libBytes.publicReadBytes.callAsync(byteArrayShorterThan32Bytes, badOffset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + libBytes.publicReadBytesWithLength.callAsync(byteArrayShorterThan32Bytes, badOffset), + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold the nested byte array', async () => { const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); return expectRevertOrOtherErrorAsync( - libBytes.publicReadBytes.callAsync(testBytes32, badOffset), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED, + libBytes.publicReadBytesWithLength.callAsync(testBytes32, badOffset), + RevertReason.LibBytesGreaterOrEqualTo32LengthRequired, ); }); }); - describe('writeBytes', () => { + describe('writeBytesWithLength', () => { it('should successfully write short, nested array of bytes when it takes up the whole array)', async () => { const testBytesOffset = new BigNumber(0); const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength)); - const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, shortData); - const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + const bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync( + emptyByteArray, + testBytesOffset, + shortData, + ); + const bytesRead = await libBytes.publicReadBytesWithLength.callAsync(bytesWritten, testBytesOffset); return expect(bytesRead).to.be.equal(shortData); }); it('should successfully write short, nested array of bytes when it is offset in the array', async () => { @@ -546,19 +564,31 @@ describe('LibBytes', () => { const emptyByteArray = ethUtil.bufferToHex( new Buffer(prefixDataAsBuffer.byteLength + shortTestBytesAsBuffer.byteLength), ); - let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData); + let bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync( + emptyByteArray, + prefixOffset, + prefixData, + ); // Write data after prefix const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength); - bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, shortData); + bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync( + bytesWritten, + testBytesOffset, + shortData, + ); // Read data after prefix and validate - const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(bytesWritten, testBytesOffset); return expect(bytes).to.be.equal(shortData); }); it('should successfully write a nested array of bytes - one word in length - when it takes up the whole array', async () => { const testBytesOffset = new BigNumber(0); const emptyByteArray = ethUtil.bufferToHex(new Buffer(wordOfTestBytesAsBuffer.byteLength)); - const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, wordOfData); - const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + const bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync( + emptyByteArray, + testBytesOffset, + wordOfData, + ); + const bytesRead = await libBytes.publicReadBytesWithLength.callAsync(bytesWritten, testBytesOffset); return expect(bytesRead).to.be.equal(wordOfData); }); it('should successfully write a nested array of bytes - one word in length - when it is offset in the array', async () => { @@ -569,19 +599,31 @@ describe('LibBytes', () => { const emptyByteArray = ethUtil.bufferToHex( new Buffer(prefixDataAsBuffer.byteLength + wordOfTestBytesAsBuffer.byteLength), ); - let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData); + let bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync( + emptyByteArray, + prefixOffset, + prefixData, + ); // Write data after prefix const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength); - bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, wordOfData); + bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync( + bytesWritten, + testBytesOffset, + wordOfData, + ); // Read data after prefix and validate - const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(bytesWritten, testBytesOffset); return expect(bytes).to.be.equal(wordOfData); }); it('should successfully write a long, nested bytes when it takes up the whole array', async () => { const testBytesOffset = new BigNumber(0); const emptyByteArray = ethUtil.bufferToHex(new Buffer(longTestBytesAsBuffer.byteLength)); - const bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, testBytesOffset, longData); - const bytesRead = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + const bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync( + emptyByteArray, + testBytesOffset, + longData, + ); + const bytesRead = await libBytes.publicReadBytesWithLength.callAsync(bytesWritten, testBytesOffset); return expect(bytesRead).to.be.equal(longData); }); it('should successfully write long, nested array of bytes when it is offset in the array', async () => { @@ -592,30 +634,200 @@ describe('LibBytes', () => { const emptyByteArray = ethUtil.bufferToHex( new Buffer(prefixDataAsBuffer.byteLength + longTestBytesAsBuffer.byteLength), ); - let bytesWritten = await libBytes.publicWriteBytes.callAsync(emptyByteArray, prefixOffset, prefixData); + let bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync( + emptyByteArray, + prefixOffset, + prefixData, + ); // Write data after prefix const testBytesOffset = new BigNumber(prefixDataAsBuffer.byteLength); - bytesWritten = await libBytes.publicWriteBytes.callAsync(bytesWritten, testBytesOffset, longData); + bytesWritten = await libBytes.publicWriteBytesWithLength.callAsync(bytesWritten, testBytesOffset, longData); // Read data after prefix and validate - const bytes = await libBytes.publicReadBytes.callAsync(bytesWritten, testBytesOffset); + const bytes = await libBytes.publicReadBytesWithLength.callAsync(bytesWritten, testBytesOffset); return expect(bytes).to.be.equal(longData); }); it('should fail if the byte array is too short to hold the length of a nested byte array', async () => { const offset = new BigNumber(0); const emptyByteArray = ethUtil.bufferToHex(new Buffer(1)); return expectRevertOrOtherErrorAsync( - libBytes.publicWriteBytes.callAsync(emptyByteArray, offset, longData), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED, + libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, offset, longData), + RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold the length of a nested byte array)', async () => { const emptyByteArray = ethUtil.bufferToHex(new Buffer(shortTestBytesAsBuffer.byteLength)); const badOffset = new BigNumber(ethUtil.toBuffer(shortTestBytesAsBuffer).byteLength); return expectRevertOrOtherErrorAsync( - libBytes.publicWriteBytes.callAsync(emptyByteArray, badOffset, shortData), - constants.LIB_BYTES_GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED, + libBytes.publicWriteBytesWithLength.callAsync(emptyByteArray, badOffset, shortData), + RevertReason.LibBytesGreaterOrEqualToNestedBytesLengthRequired, ); }); }); + + describe('memCopy', () => { + // Create memory 0x000102...FF + const memSize = 256; + // tslint:disable:no-shadowed-variable + const memory = new Uint8Array(memSize).map((_, i) => i); + const memHex = toHex(memory); + + // Reference implementation to test against + const refMemcpy = (mem: Uint8Array, dest: number, source: number, length: number): Uint8Array => + Uint8Array.from(mem).copyWithin(dest, source, source + length); + + // Test vectors: destination, source, length, job description + type Tests = Array<[number, number, number, string]>; + + const test = (tests: Tests) => + tests.forEach(([dest, source, length, job]) => + it(job, async () => { + const expected = refMemcpy(memory, dest, source, length); + const resultStr = await libBytes.testMemcpy.callAsync( + memHex, + new BigNumber(dest), + new BigNumber(source), + new BigNumber(length), + ); + const result = fromHex(resultStr); + expect(result).to.deep.equal(expected); + }), + ); + + test([[0, 0, 0, 'copies zero bytes with overlap']]); + + describe('copies forward', () => + test([ + [128, 0, 0, 'zero bytes'], + [128, 0, 1, 'one byte'], + [128, 0, 11, 'eleven bytes'], + [128, 0, 31, 'thirty-one bytes'], + [128, 0, 32, 'one word'], + [128, 0, 64, 'two words'], + [128, 0, 96, 'three words'], + [128, 0, 33, 'one word and one byte'], + [128, 0, 72, 'two words and eight bytes'], + [128, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward within one word', () => + test([ + [16, 0, 0, 'zero bytes'], + [16, 0, 1, 'one byte'], + [16, 0, 11, 'eleven bytes'], + [16, 0, 16, 'sixteen bytes'], + ])); + + describe('copies forward with one byte overlap', () => + test([ + [0, 0, 1, 'one byte'], + [10, 0, 11, 'eleven bytes'], + [30, 0, 31, 'thirty-one bytes'], + [31, 0, 32, 'one word'], + [32, 0, 33, 'one word and one byte'], + [71, 0, 72, 'two words and eight bytes'], + [99, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward with thirty-one bytes overlap', () => + test([ + [0, 0, 31, 'thirty-one bytes'], + [1, 0, 32, 'one word'], + [2, 0, 33, 'one word and one byte'], + [41, 0, 72, 'two words and eight bytes'], + [69, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward with one word overlap', () => + test([ + [0, 0, 32, 'one word'], + [1, 0, 33, 'one word and one byte'], + [41, 0, 72, 'two words and eight bytes'], + [69, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward with one word and one byte overlap', () => + test([ + [0, 0, 33, 'one word and one byte'], + [40, 0, 72, 'two words and eight bytes'], + [68, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward with two words overlap', () => + test([ + [0, 0, 64, 'two words'], + [8, 0, 72, 'two words and eight bytes'], + [36, 0, 100, 'three words and four bytes'], + ])); + + describe('copies forward within one word and one byte overlap', () => + test([[0, 0, 1, 'one byte'], [10, 0, 11, 'eleven bytes'], [15, 0, 16, 'sixteen bytes']])); + + describe('copies backward', () => + test([ + [0, 128, 0, 'zero bytes'], + [0, 128, 1, 'one byte'], + [0, 128, 11, 'eleven bytes'], + [0, 128, 31, 'thirty-one bytes'], + [0, 128, 32, 'one word'], + [0, 128, 64, 'two words'], + [0, 128, 96, 'three words'], + [0, 128, 33, 'one word and one byte'], + [0, 128, 72, 'two words and eight bytes'], + [0, 128, 100, 'three words and four bytes'], + ])); + + describe('copies backward within one word', () => + test([ + [0, 16, 0, 'zero bytes'], + [0, 16, 1, 'one byte'], + [0, 16, 11, 'eleven bytes'], + [0, 16, 16, 'sixteen bytes'], + ])); + + describe('copies backward with one byte overlap', () => + test([ + [0, 0, 1, 'one byte'], + [0, 10, 11, 'eleven bytes'], + [0, 30, 31, 'thirty-one bytes'], + [0, 31, 32, 'one word'], + [0, 32, 33, 'one word and one byte'], + [0, 71, 72, 'two words and eight bytes'], + [0, 99, 100, 'three words and four bytes'], + ])); + + describe('copies backward with thirty-one bytes overlap', () => + test([ + [0, 0, 31, 'thirty-one bytes'], + [0, 1, 32, 'one word'], + [0, 2, 33, 'one word and one byte'], + [0, 41, 72, 'two words and eight bytes'], + [0, 69, 100, 'three words and four bytes'], + ])); + + describe('copies backward with one word overlap', () => + test([ + [0, 0, 32, 'one word'], + [0, 1, 33, 'one word and one byte'], + [0, 41, 72, 'two words and eight bytes'], + [0, 69, 100, 'three words and four bytes'], + ])); + + describe('copies backward with one word and one byte overlap', () => + test([ + [0, 0, 33, 'one word and one byte'], + [0, 40, 72, 'two words and eight bytes'], + [0, 68, 100, 'three words and four bytes'], + ])); + + describe('copies backward with two words overlap', () => + test([ + [0, 0, 64, 'two words'], + [0, 8, 72, 'two words and eight bytes'], + [0, 36, 100, 'three words and four bytes'], + ])); + + describe('copies forward within one word and one byte overlap', () => + test([[0, 0, 1, 'one byte'], [0, 10, 11, 'eleven bytes'], [0, 15, 16, 'sixteen bytes']])); + }); }); // tslint:disable:max-file-line-count diff --git a/packages/contracts/test/libraries/lib_mem.ts b/packages/contracts/test/libraries/lib_mem.ts deleted file mode 100644 index 00f7c4d8b..000000000 --- a/packages/contracts/test/libraries/lib_mem.ts +++ /dev/null @@ -1,190 +0,0 @@ -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; - -import { TestLibMemContract } from '../../src/generated_contract_wrappers/test_lib_mem'; -import { artifacts } from '../../src/utils/artifacts'; -import { chaiSetup } from '../../src/utils/chai_setup'; -import { provider, txDefaults } from '../../src/utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; - -// BUG: Ideally we would use Buffer.from(memory).toString('hex') -// https://github.com/Microsoft/TypeScript/issues/23155 -const toHex = (buf: Uint8Array): string => buf.reduce((a, v) => a + ('00' + v.toString(16)).slice(-2), '0x'); - -const fromHex = (str: string): Uint8Array => Uint8Array.from(Buffer.from(str.slice(2), 'hex')); - -describe('LibMem', () => { - let testLibMem: TestLibMemContract; - - before(async () => { - // Deploy TestLibMem - testLibMem = await TestLibMemContract.deployFrom0xArtifactAsync(artifacts.TestLibMem, provider, txDefaults); - }); - - describe('memCopy', () => { - // Create memory 0x000102...FF - const memSize = 256; - const memory = new Uint8Array(memSize).map((_, i) => i); - const memHex = toHex(memory); - - // Reference implementation to test against - const refMemcpy = (_mem: Uint8Array, dest: number, source: number, length: number): Uint8Array => - Uint8Array.from(memory).copyWithin(dest, source, source + length); - - // Test vectors: destination, source, length, job description - type Tests = Array<[number, number, number, string]>; - - const test = (tests: Tests) => - tests.forEach(([dest, source, length, job]) => - it(job, async () => { - const expected = refMemcpy(memory, dest, source, length); - const resultStr = await testLibMem.testMemcpy.callAsync( - memHex, - new BigNumber(dest), - new BigNumber(source), - new BigNumber(length), - ); - const result = fromHex(resultStr); - expect(result).to.deep.equal(expected); - }), - ); - - test([[0, 0, 0, 'copies zero bytes with overlap']]); - - describe('copies forward', () => - test([ - [128, 0, 0, 'zero bytes'], - [128, 0, 1, 'one byte'], - [128, 0, 11, 'eleven bytes'], - [128, 0, 31, 'thirty-one bytes'], - [128, 0, 32, 'one word'], - [128, 0, 64, 'two words'], - [128, 0, 96, 'three words'], - [128, 0, 33, 'one word and one byte'], - [128, 0, 72, 'two words and eight bytes'], - [128, 0, 100, 'three words and four bytes'], - ])); - - describe('copies forward within one word', () => - test([ - [16, 0, 0, 'zero bytes'], - [16, 0, 1, 'one byte'], - [16, 0, 11, 'eleven bytes'], - [16, 0, 16, 'sixteen bytes'], - ])); - - describe('copies forward with one byte overlap', () => - test([ - [0, 0, 1, 'one byte'], - [10, 0, 11, 'eleven bytes'], - [30, 0, 31, 'thirty-one bytes'], - [31, 0, 32, 'one word'], - [32, 0, 33, 'one word and one byte'], - [71, 0, 72, 'two words and eight bytes'], - [99, 0, 100, 'three words and four bytes'], - ])); - - describe('copies forward with thirty-one bytes overlap', () => - test([ - [0, 0, 31, 'thirty-one bytes'], - [1, 0, 32, 'one word'], - [2, 0, 33, 'one word and one byte'], - [41, 0, 72, 'two words and eight bytes'], - [69, 0, 100, 'three words and four bytes'], - ])); - - describe('copies forward with one word overlap', () => - test([ - [0, 0, 32, 'one word'], - [1, 0, 33, 'one word and one byte'], - [41, 0, 72, 'two words and eight bytes'], - [69, 0, 100, 'three words and four bytes'], - ])); - - describe('copies forward with one word and one byte overlap', () => - test([ - [0, 0, 33, 'one word and one byte'], - [40, 0, 72, 'two words and eight bytes'], - [68, 0, 100, 'three words and four bytes'], - ])); - - describe('copies forward with two words overlap', () => - test([ - [0, 0, 64, 'two words'], - [8, 0, 72, 'two words and eight bytes'], - [36, 0, 100, 'three words and four bytes'], - ])); - - describe('copies forward within one word and one byte overlap', () => - test([[0, 0, 1, 'one byte'], [10, 0, 11, 'eleven bytes'], [15, 0, 16, 'sixteen bytes']])); - - describe('copies backward', () => - test([ - [0, 128, 0, 'zero bytes'], - [0, 128, 1, 'one byte'], - [0, 128, 11, 'eleven bytes'], - [0, 128, 31, 'thirty-one bytes'], - [0, 128, 32, 'one word'], - [0, 128, 64, 'two words'], - [0, 128, 96, 'three words'], - [0, 128, 33, 'one word and one byte'], - [0, 128, 72, 'two words and eight bytes'], - [0, 128, 100, 'three words and four bytes'], - ])); - - describe('copies backward within one word', () => - test([ - [0, 16, 0, 'zero bytes'], - [0, 16, 1, 'one byte'], - [0, 16, 11, 'eleven bytes'], - [0, 16, 16, 'sixteen bytes'], - ])); - - describe('copies backward with one byte overlap', () => - test([ - [0, 0, 1, 'one byte'], - [0, 10, 11, 'eleven bytes'], - [0, 30, 31, 'thirty-one bytes'], - [0, 31, 32, 'one word'], - [0, 32, 33, 'one word and one byte'], - [0, 71, 72, 'two words and eight bytes'], - [0, 99, 100, 'three words and four bytes'], - ])); - - describe('copies backward with thirty-one bytes overlap', () => - test([ - [0, 0, 31, 'thirty-one bytes'], - [0, 1, 32, 'one word'], - [0, 2, 33, 'one word and one byte'], - [0, 41, 72, 'two words and eight bytes'], - [0, 69, 100, 'three words and four bytes'], - ])); - - describe('copies backward with one word overlap', () => - test([ - [0, 0, 32, 'one word'], - [0, 1, 33, 'one word and one byte'], - [0, 41, 72, 'two words and eight bytes'], - [0, 69, 100, 'three words and four bytes'], - ])); - - describe('copies backward with one word and one byte overlap', () => - test([ - [0, 0, 33, 'one word and one byte'], - [0, 40, 72, 'two words and eight bytes'], - [0, 68, 100, 'three words and four bytes'], - ])); - - describe('copies backward with two words overlap', () => - test([ - [0, 0, 64, 'two words'], - [0, 8, 72, 'two words and eight bytes'], - [0, 36, 100, 'three words and four bytes'], - ])); - - describe('copies forward within one word and one byte overlap', () => - test([[0, 0, 1, 'one byte'], [0, 10, 11, 'eleven bytes'], [0, 15, 16, 'sixteen bytes']])); - }); -}); diff --git a/packages/contracts/test/unlimited_allowance_token.ts b/packages/contracts/test/unlimited_allowance_token.ts index 7132c57bf..0eb82b5fe 100644 --- a/packages/contracts/test/unlimited_allowance_token.ts +++ b/packages/contracts/test/unlimited_allowance_token.ts @@ -1,4 +1,5 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { RevertReason } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; @@ -55,7 +56,7 @@ describe('UnlimitedAllowanceToken', () => { const amountToTransfer = ownerBalance.plus(1); return expectRevertOrOtherErrorAsync( token.transfer.callAsync(spender, amountToTransfer, { from: owner }), - constants.ERC20_INSUFFICIENT_BALANCE, + RevertReason.Erc20InsufficientBalance, ); }); @@ -96,7 +97,7 @@ describe('UnlimitedAllowanceToken', () => { token.transferFrom.callAsync(owner, spender, amountToTransfer, { from: spender, }), - constants.ERC20_INSUFFICIENT_BALANCE, + RevertReason.Erc20InsufficientBalance, ); }); @@ -112,7 +113,7 @@ describe('UnlimitedAllowanceToken', () => { token.transferFrom.callAsync(owner, spender, amountToTransfer, { from: spender, }), - constants.ERC20_INSUFFICIENT_ALLOWANCE, + RevertReason.Erc20InsufficientAllowance, ); }); |