diff --git a/contracts/asset-proxy/CHANGELOG.json b/contracts/asset-proxy/CHANGELOG.json index d4a8083933..4bdbc98084 100644 --- a/contracts/asset-proxy/CHANGELOG.json +++ b/contracts/asset-proxy/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "2.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Do not reexport external dependencies", "pr": 1682 @@ -17,6 +13,10 @@ { "note": "Bumped solidity version to ^0.5.5", "pr": 1701 + }, + { + "note": "Integration testing for ERC1155Proxy", + "pr": 1673 } ] }, diff --git a/contracts/asset-proxy/compiler.json b/contracts/asset-proxy/compiler.json index 28f73402ca..6bf9c16c56 100644 --- a/contracts/asset-proxy/compiler.json +++ b/contracts/asset-proxy/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/asset-proxy/test/erc1155_proxy.ts b/contracts/asset-proxy/test/erc1155_proxy.ts index 7931fdfda0..f55e11024f 100644 --- a/contracts/asset-proxy/test/erc1155_proxy.ts +++ b/contracts/asset-proxy/test/erc1155_proxy.ts @@ -433,6 +433,50 @@ describe('ERC1155Proxy', () => { ]; await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); }); + it('should successfully transfer value to a smart contract and trigger its callback, when callback `data` is NULL', async () => { + // setup test parameters + const tokenHolders = [spender, receiverContract]; + const tokensToTransfer = fungibleTokens.slice(0, 1); + const valuesToTransfer = [fungibleValueToTransferLarge]; + const valueMultiplier = valueMultiplierSmall; + const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => { + return value.times(valueMultiplier); + }); + // check balances before transfer + const expectedInitialBalances = [spenderInitialFungibleBalance, receiverContractInitialFungibleBalance]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // execute transfer + const nullReceiverCallbackData = '0x'; + const txReceipt = await erc1155ProxyWrapper.transferFromWithLogsAsync( + spender, + receiverContract, + erc1155Contract.address, + tokensToTransfer, + valuesToTransfer, + valueMultiplier, + nullReceiverCallbackData, + authorized, + ); + // check receiver log ignored extra asset data + expect(txReceipt.logs.length).to.be.equal(2); + const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs< + DummyERC1155ReceiverBatchTokenReceivedEventArgs + >; + expect(receiverLog.args.operator).to.be.equal(erc1155Proxy.address); + expect(receiverLog.args.from).to.be.equal(spender); + expect(receiverLog.args.tokenIds.length).to.be.deep.equal(1); + expect(receiverLog.args.tokenIds[0]).to.be.bignumber.equal(tokensToTransfer[0]); + expect(receiverLog.args.tokenValues.length).to.be.deep.equal(1); + expect(receiverLog.args.tokenValues[0]).to.be.bignumber.equal(totalValuesTransferred[0]); + // note - if the `extraData` is ignored then the receiver log should ignore it as well. + expect(receiverLog.args.data).to.be.deep.equal(nullReceiverCallbackData); + // check balances after transfer + const expectedFinalBalances = [ + expectedInitialBalances[0].minus(totalValuesTransferred[0]), + expectedInitialBalances[1].plus(totalValuesTransferred[0]), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); it('should successfully transfer value and ignore extra assetData', async () => { // setup test parameters const tokenHolders = [spender, receiverContract]; diff --git a/contracts/asset-proxy/test/proxies.ts b/contracts/asset-proxy/test/proxies.ts index 7977871358..81d1475d82 100644 --- a/contracts/asset-proxy/test/proxies.ts +++ b/contracts/asset-proxy/test/proxies.ts @@ -1,3 +1,4 @@ +import { ERC1155MintableContract, Erc1155Wrapper } from '@0x/contracts-erc1155'; import { artifacts as erc20Artifacts, DummyERC20TokenContract, @@ -30,6 +31,7 @@ import * as _ from 'lodash'; import { artifacts, + ERC1155ProxyWrapper, ERC20ProxyContract, ERC20Wrapper, ERC721ProxyContract, @@ -77,6 +79,15 @@ describe('Asset Transfer Proxies', () => { let erc721AFromTokenId: BigNumber; let erc721BFromTokenId: BigNumber; + let erc1155Proxy: ERC721ProxyContract; + let erc1155ProxyWrapper: ERC1155ProxyWrapper; + let erc1155Contract: ERC1155MintableContract; + let erc1155Contract2: ERC1155MintableContract; + let erc1155Wrapper: Erc1155Wrapper; + let erc1155Wrapper2: Erc1155Wrapper; + let erc1155FungibleTokens: BigNumber[]; + let erc1155NonFungibleTokensOwnedBySpender: BigNumber[]; + before(async () => { await blockchainLifecycle.startAsync(); }); @@ -127,6 +138,22 @@ describe('Asset Transfer Proxies', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); + // Configure ERC115Proxy + erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner); + erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync(); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Configure MultiAssetProxy await web3Wrapper.awaitTransactionSuccessAsync( await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(authorized, { @@ -146,6 +173,12 @@ describe('Asset Transfer Proxies', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); + await web3Wrapper.awaitTransactionSuccessAsync( + await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc1155Proxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); // Deploy and configure ERC20 tokens const numDummyErc20ToDeploy = 2; @@ -213,6 +246,22 @@ describe('Asset Transfer Proxies', () => { const erc721Balances = await erc721Wrapper.getBalancesAsync(); erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0]; erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0]; + + // Deploy & configure ERC1155 tokens and receiver + [erc1155Wrapper, erc1155Wrapper2] = await erc1155ProxyWrapper.deployDummyContractsAsync(); + erc1155Contract = erc1155Wrapper.getContract(); + erc1155Contract2 = erc1155Wrapper2.getContract(); + await erc1155ProxyWrapper.setBalancesAndAllowancesAsync(); + erc1155FungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds(); + const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds(); + const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync(); + erc1155NonFungibleTokensOwnedBySpender = []; + _.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => { + const nonFungibleTokenAsString = nonFungibleToken.toString(); + const nonFungibleTokenHeldBySpender = + tokenBalances.nonFungible[fromAddress][erc1155Contract.address][nonFungibleTokenAsString][0]; + erc1155NonFungibleTokensOwnedBySpender.push(nonFungibleTokenHeldBySpender); + }); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -944,6 +993,314 @@ describe('Asset Transfer Proxies', () => { expect(newOwnerFromAsset1).to.be.equal(toAddress); expect(newOwnerFromAsset2).to.be.equal(toAddress); }); + it('should transfer a fungible ERC1155 token', async () => { + // setup test parameters + const tokenHolders = [fromAddress, toAddress]; + const tokensToTransfer = erc1155FungibleTokens.slice(0, 1); + const valuesToTransfer = [new BigNumber(25)]; + const valueMultiplier = new BigNumber(23); + const receiverCallbackData = '0x0102030405'; + // check balances before transfer + const expectedInitialBalances = [ + // from + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // to + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // encode erc1155 asset data + const erc1155AssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + ); + // encode multi-asset data + const multiAssetAmount = new BigNumber(5); + const amounts = [valueMultiplier]; + const nestedAssetData = [erc1155AssetData]; + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + multiAssetAmount, + ); + // execute transfer + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // check balances + const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount); + const expectedFinalBalances = [ + // from + expectedInitialBalances[0].minus(totalValueTransferred), + // to + expectedInitialBalances[1].plus(totalValueTransferred), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); + it('should successfully transfer multiple fungible tokens of the same ERC1155 contract', async () => { + // setup test parameters + const tokenHolders = [fromAddress, toAddress]; + const tokensToTransfer = erc1155FungibleTokens.slice(0, 3); + const valuesToTransfer = [new BigNumber(25), new BigNumber(35), new BigNumber(45)]; + const valueMultiplier = new BigNumber(23); + const receiverCallbackData = '0x0102030405'; + // check balances before transfer + const expectedInitialBalances = [ + // from + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // to + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // encode erc1155 asset data + const erc1155AssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + ); + // encode multi-asset data + const multiAssetAmount = new BigNumber(5); + const amounts = [valueMultiplier]; + const nestedAssetData = [erc1155AssetData]; + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + multiAssetAmount, + ); + // execute transfer + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // check balances + const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => { + return value.times(valueMultiplier).times(multiAssetAmount); + }); + const expectedFinalBalances = [ + // from + expectedInitialBalances[0].minus(totalValuesTransferred[0]), + expectedInitialBalances[1].minus(totalValuesTransferred[1]), + expectedInitialBalances[2].minus(totalValuesTransferred[2]), + // to + expectedInitialBalances[3].plus(totalValuesTransferred[0]), + expectedInitialBalances[4].plus(totalValuesTransferred[1]), + expectedInitialBalances[5].plus(totalValuesTransferred[2]), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); + it('should successfully transfer multiple fungible/non-fungible tokens of the same ERC1155 contract', async () => { + // setup test parameters + const tokenHolders = [fromAddress, toAddress]; + const fungibleTokensToTransfer = erc1155FungibleTokens.slice(0, 1); + const nonFungibleTokensToTransfer = erc1155NonFungibleTokensOwnedBySpender.slice(0, 1); + const tokensToTransfer = fungibleTokensToTransfer.concat(nonFungibleTokensToTransfer); + const valuesToTransfer = [new BigNumber(25), new BigNumber(1)]; + const valueMultiplier = new BigNumber(1); + const receiverCallbackData = '0x0102030405'; + // check balances before transfer + const nftOwnerBalance = new BigNumber(1); + const nftNotOwnerBalance = new BigNumber(0); + const expectedInitialBalances = [ + // from + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + nftOwnerBalance, + // to + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + nftNotOwnerBalance, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // encode erc1155 asset data + const erc1155AssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + ); + // encode multi-asset data + const multiAssetAmount = new BigNumber(1); + const amounts = [valueMultiplier]; + const nestedAssetData = [erc1155AssetData]; + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + multiAssetAmount, + ); + // execute transfer + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // check balances + const totalValuesTransferred = _.map(valuesToTransfer, (value: BigNumber) => { + return value.times(valueMultiplier).times(multiAssetAmount); + }); + const expectedFinalBalances = [ + // from + expectedInitialBalances[0].minus(totalValuesTransferred[0]), + expectedInitialBalances[1].minus(totalValuesTransferred[1]), + // to + expectedInitialBalances[2].plus(totalValuesTransferred[0]), + expectedInitialBalances[3].plus(totalValuesTransferred[1]), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); + it('should successfully transfer multiple different ERC1155 tokens', async () => { + // setup test parameters + const tokenHolders = [fromAddress, toAddress]; + const tokensToTransfer = erc1155FungibleTokens.slice(0, 1); + const valuesToTransfer = [new BigNumber(25)]; + const valueMultiplier = new BigNumber(23); + const receiverCallbackData = '0x0102030405'; + // check balances before transfer + const expectedInitialBalances = [ + // from + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // to + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // encode erc1155 asset data + const erc1155AssetData1 = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + ); + const erc1155AssetData2 = assetDataUtils.encodeERC1155AssetData( + erc1155Contract2.address, + tokensToTransfer, + valuesToTransfer, + receiverCallbackData, + ); + // encode multi-asset data + const multiAssetAmount = new BigNumber(5); + const amounts = [valueMultiplier, valueMultiplier]; + const nestedAssetData = [erc1155AssetData1, erc1155AssetData2]; + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + multiAssetAmount, + ); + // execute transfer + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // check balances + const totalValueTransferred = valuesToTransfer[0].times(valueMultiplier).times(multiAssetAmount); + const expectedFinalBalances = [ + // from + expectedInitialBalances[0].minus(totalValueTransferred), + // to + expectedInitialBalances[1].plus(totalValueTransferred), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + await erc1155Wrapper2.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); + it('should successfully transfer a combination of ERC20, ERC721, and ERC1155 tokens', async () => { + // setup test parameters + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc721Amount = new BigNumber(1); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const erc1155TokenHolders = [fromAddress, toAddress]; + const erc1155TokensToTransfer = erc1155FungibleTokens.slice(0, 1); + const erc1155ValuesToTransfer = [new BigNumber(25)]; + const erc1155Amount = new BigNumber(23); + const erc1155ReceiverCallbackData = '0x0102030405'; + const erc1155AssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + erc1155TokensToTransfer, + erc1155ValuesToTransfer, + erc1155ReceiverCallbackData, + ); + const amounts = [erc20Amount, erc721Amount, erc1155Amount]; + const nestedAssetData = [erc20AssetData, erc721AssetData, erc1155AssetData]; + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + // check balances before transfer + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + const erc1155ExpectedInitialBalances = [ + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + ]; + await erc1155Wrapper.assertBalancesAsync( + erc1155TokenHolders, + erc1155TokensToTransfer, + erc1155ExpectedInitialBalances, + ); + // execute transfer + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + gas: 1000000, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // check balances after transfer + const newBalances = await erc20Wrapper.getBalancesAsync(); + const totalAmount = inputAmount.times(erc20Amount); + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), + ); + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].plus(totalAmount), + ); + const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwnerFromAsset).to.be.equal(toAddress); + const erc1155TotalValueTransferred = erc1155ValuesToTransfer[0].times(erc1155Amount).times(inputAmount); + const expectedFinalBalances = [ + erc1155ExpectedInitialBalances[0].minus(erc1155TotalValueTransferred), + erc1155ExpectedInitialBalances[1].plus(erc1155TotalValueTransferred), + ]; + await erc1155Wrapper.assertBalancesAsync( + erc1155TokenHolders, + erc1155TokensToTransfer, + expectedFinalBalances, + ); + }); it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => { const inputAmount = new BigNumber(1); const erc20Amount = new BigNumber(10); diff --git a/contracts/coordinator/CHANGELOG.json b/contracts/coordinator/CHANGELOG.json index 9aae87f350..fc1a3f7f0d 100644 --- a/contracts/coordinator/CHANGELOG.json +++ b/contracts/coordinator/CHANGELOG.json @@ -1,6 +1,6 @@ [ { - "version": "0.0.1", + "version": "1.0.0", "changes": [ { "note": "Created Coordinator package" @@ -12,6 +12,10 @@ { "note": "Add `SignatureType.Invalid`", "pr": 1705 + }, + { + "note": "Set `evmVersion` to `constantinople`", + "pr": 1707 } ] } diff --git a/contracts/coordinator/compiler.json b/contracts/coordinator/compiler.json index 40b486d1a7..8df8f3cd33 100644 --- a/contracts/coordinator/compiler.json +++ b/contracts/coordinator/compiler.json @@ -3,7 +3,7 @@ "contractsDir": "./contracts", "useDockerisedSolc": true, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/erc1155/compiler.json b/contracts/erc1155/compiler.json index e545f46b08..2c3318284a 100644 --- a/contracts/erc1155/compiler.json +++ b/contracts/erc1155/compiler.json @@ -3,7 +3,7 @@ "contractsDir": "contracts", "useDockerisedSolc": true, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000 }, "outputSelection": { "*": { diff --git a/contracts/erc20/CHANGELOG.json b/contracts/erc20/CHANGELOG.json index 7d2d6d8218..87a442ea37 100644 --- a/contracts/erc20/CHANGELOG.json +++ b/contracts/erc20/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "2.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Upgrade contracts to Solidity 0.5.5", "pr": 1682 diff --git a/contracts/erc20/compiler.json b/contracts/erc20/compiler.json index 714ee44df9..c84caf47f1 100644 --- a/contracts/erc20/compiler.json +++ b/contracts/erc20/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/erc721/CHANGELOG.json b/contracts/erc721/CHANGELOG.json index f7cfc8b6b5..91d3674e16 100644 --- a/contracts/erc721/CHANGELOG.json +++ b/contracts/erc721/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "2.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Upgrade contracts to Solidity 0.5.5", "pr": 1682 diff --git a/contracts/erc721/compiler.json b/contracts/erc721/compiler.json index d7294e62ae..a3b8f417d5 100644 --- a/contracts/erc721/compiler.json +++ b/contracts/erc721/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/exchange-forwarder/CHANGELOG.json b/contracts/exchange-forwarder/CHANGELOG.json index 50d4375ca1..1bebb14105 100644 --- a/contracts/exchange-forwarder/CHANGELOG.json +++ b/contracts/exchange-forwarder/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "2.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Do not reexport external dependencies", "pr": 1682 diff --git a/contracts/exchange-forwarder/compiler.json b/contracts/exchange-forwarder/compiler.json index deb76ddfa8..d49cd34b7d 100644 --- a/contracts/exchange-forwarder/compiler.json +++ b/contracts/exchange-forwarder/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/exchange-libs/CHANGELOG.json b/contracts/exchange-libs/CHANGELOG.json index 23d12e531d..19f4222093 100644 --- a/contracts/exchange-libs/CHANGELOG.json +++ b/contracts/exchange-libs/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "2.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Upgrade contracts to Solidity 0.5.5", "pr": 1682 diff --git a/contracts/exchange-libs/compiler.json b/contracts/exchange-libs/compiler.json index 8523e1155e..fed143589c 100644 --- a/contracts/exchange-libs/compiler.json +++ b/contracts/exchange-libs/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/exchange/CHANGELOG.json b/contracts/exchange/CHANGELOG.json index 76bad01b67..6f9c055614 100644 --- a/contracts/exchange/CHANGELOG.json +++ b/contracts/exchange/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "2.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Do not reexport external dependencies", "pr": 1682 @@ -13,6 +9,10 @@ { "note": "Upgrade contracts to Solidity 0.5.5", "pr": 1682 + }, + { + "note": "Integration testing for ERC1155Proxy", + "pr": 1673 } ] }, diff --git a/contracts/exchange/compiler.json b/contracts/exchange/compiler.json index dfbf86a0bc..c1f8663d37 100644 --- a/contracts/exchange/compiler.json +++ b/contracts/exchange/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/exchange/package.json b/contracts/exchange/package.json index 69ba190525..c32eb31e5e 100644 --- a/contracts/exchange/package.json +++ b/contracts/exchange/package.json @@ -71,6 +71,7 @@ "@0x/contracts-asset-proxy": "^1.0.9", "@0x/contracts-erc20": "^1.0.9", "@0x/contracts-erc721": "^1.0.9", + "@0x/contracts-erc1155": "^1.0.0", "@0x/contracts-exchange-libs": "^1.1.3", "@0x/contracts-utils": "^2.0.8", "@0x/order-utils": "^7.0.2", diff --git a/contracts/exchange/test/core.ts b/contracts/exchange/test/core.ts index 7acb4e2aba..ff6874c0ea 100644 --- a/contracts/exchange/test/core.ts +++ b/contracts/exchange/test/core.ts @@ -1,11 +1,13 @@ import { artifacts as proxyArtifacts, + ERC1155ProxyWrapper, ERC20ProxyContract, ERC20Wrapper, ERC721ProxyContract, ERC721Wrapper, MultiAssetProxyContract, } from '@0x/contracts-asset-proxy'; +import { ERC1155MintableContract } from '@0x/contracts-erc1155'; import { artifacts as erc20Artifacts, DummyERC20TokenContract, @@ -36,6 +38,7 @@ import { LogWithDecodedArgs } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; +import { Erc1155Wrapper } from '../../erc1155/lib/src'; import { artifacts, ExchangeCancelEventArgs, @@ -64,19 +67,26 @@ describe('Exchange core', () => { let exchange: ExchangeContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; + let erc1155Proxy: ERC721ProxyContract; let multiAssetProxy: MultiAssetProxyContract; let maliciousWallet: TestStaticCallReceiverContract; let maliciousValidator: TestStaticCallReceiverContract; + let erc1155Contract: ERC1155MintableContract; let signedOrder: SignedOrder; let erc20Balances: ERC20BalancesByOwner; let exchangeWrapper: ExchangeWrapper; let erc20Wrapper: ERC20Wrapper; let erc721Wrapper: ERC721Wrapper; + let erc1155Wrapper: Erc1155Wrapper; + let erc1155ProxyWrapper: ERC1155ProxyWrapper; let orderFactory: OrderFactory; let erc721MakerAssetIds: BigNumber[]; let erc721TakerAssetIds: BigNumber[]; + let erc1155FungibleTokens: BigNumber[]; + let erc1155NonFungibleTokensOwnedByMaker: BigNumber[]; + let erc1155NonFungibleTokensOwnedByTaker: BigNumber[]; let defaultMakerAssetAddress: string; let defaultTakerAssetAddress: string; @@ -93,6 +103,7 @@ describe('Exchange core', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); + erc1155ProxyWrapper = new ERC1155ProxyWrapper(provider, usedAddresses, owner); // Deploy AssetProxies, Exchange, tokens, and malicious contracts erc20Proxy = await erc20Wrapper.deployProxyAsync(); @@ -108,6 +119,9 @@ describe('Exchange core', () => { constants.DUMMY_TOKEN_DECIMALS, ); [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); + erc1155Proxy = await erc1155ProxyWrapper.deployProxyAsync(); + [erc1155Wrapper] = await erc1155ProxyWrapper.deployDummyContractsAsync(); + erc1155Contract = erc1155Wrapper.getContract(); exchange = await ExchangeContract.deployFrom0xArtifactAsync( artifacts.Exchange, provider, @@ -154,6 +168,20 @@ describe('Exchange core', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); + // Configure ERC1155Proxy + await web3Wrapper.awaitTransactionSuccessAsync( + await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Configure MultiAssetProxy await web3Wrapper.awaitTransactionSuccessAsync( await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { @@ -178,6 +206,7 @@ describe('Exchange core', () => { exchangeWrapper = new ExchangeWrapper(exchange, provider); await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(erc1155Proxy.address, owner); await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, owner); // Configure ERC20 tokens @@ -189,6 +218,23 @@ describe('Exchange core', () => { erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address]; + // Configure ERC1155 tokens + await erc1155ProxyWrapper.setBalancesAndAllowancesAsync(); + erc1155FungibleTokens = erc1155ProxyWrapper.getFungibleTokenIds(); + const nonFungibleTokens = erc1155ProxyWrapper.getNonFungibleTokenIds(); + const tokenBalances = await erc1155ProxyWrapper.getBalancesAsync(); + erc1155NonFungibleTokensOwnedByMaker = []; + erc1155NonFungibleTokensOwnedByTaker = []; + _.each(nonFungibleTokens, (nonFungibleToken: BigNumber) => { + const nonFungibleTokenAsString = nonFungibleToken.toString(); + const nonFungibleTokenHeldByMaker = + tokenBalances.nonFungible[makerAddress][erc1155Contract.address][nonFungibleTokenAsString][0]; + erc1155NonFungibleTokensOwnedByMaker.push(nonFungibleTokenHeldByMaker); + const nonFungibleTokenHeldByTaker = + tokenBalances.nonFungible[takerAddress][erc1155Contract.address][nonFungibleTokenAsString][0]; + erc1155NonFungibleTokensOwnedByTaker.push(nonFungibleTokenHeldByTaker); + }); + // Configure order defaults defaultMakerAssetAddress = erc20TokenA.address; defaultTakerAssetAddress = erc20TokenB.address; @@ -1040,6 +1086,435 @@ describe('Exchange core', () => { ); }); }); + describe('Testing exchange of erc1155 assets', () => { + it('should allow a single fungible erc1155 asset to be exchanged for another', async () => { + // setup test parameters + const tokenHolders = [makerAddress, takerAddress]; + const makerAssetsToTransfer = erc1155FungibleTokens.slice(0, 1); + const takerAssetsToTransfer = erc1155FungibleTokens.slice(1, 2); + const makerValuesToTransfer = [new BigNumber(500)]; + const takerValuesToTransfer = [new BigNumber(200)]; + const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); + const makerAssetAmount = new BigNumber(1); + const takerAssetAmount = new BigNumber(1); + const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { + return value.times(makerAssetAmount); + }); + const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { + return value.times(takerAssetAmount); + }); + const receiverCallbackData = '0x'; + const makerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + makerAssetsToTransfer, + makerValuesToTransfer, + receiverCallbackData, + ); + const takerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + takerAssetsToTransfer, + takerValuesToTransfer, + receiverCallbackData, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + const takerAssetFillAmount = new BigNumber(1); + // check balances before transfer + const expectedInitialBalances = [ + // makerAddress / makerToken + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / takerToken + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / makerToken + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / takerToken + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // execute transfer + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + takerAssetFillAmount, + }); + // check balances after transfer + const expectedFinalBalances = [ + // makerAddress / makerToken + expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), + // makerAddress / takerToken + expectedInitialBalances[1].plus(totalTakerValuesTransferred[0]), + // takerAddress / makerToken + expectedInitialBalances[2].plus(totalMakerValuesTransferred[0]), + // takerAddress / takerToken + expectedInitialBalances[3].minus(totalTakerValuesTransferred[0]), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); + it('should allow a single non-fungible erc1155 asset to be exchanged for another', async () => { + // setup test parameters + const tokenHolders = [makerAddress, takerAddress]; + const makerAssetsToTransfer = erc1155NonFungibleTokensOwnedByMaker.slice(0, 1); + const takerAssetsToTransfer = erc1155NonFungibleTokensOwnedByTaker.slice(0, 1); + const makerValuesToTransfer = [new BigNumber(1)]; + const takerValuesToTransfer = [new BigNumber(1)]; + const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); + const makerAssetAmount = new BigNumber(1); + const takerAssetAmount = new BigNumber(1); + const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { + return value.times(makerAssetAmount); + }); + const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { + return value.times(takerAssetAmount); + }); + const receiverCallbackData = '0x'; + const makerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + makerAssetsToTransfer, + makerValuesToTransfer, + receiverCallbackData, + ); + const takerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + takerAssetsToTransfer, + takerValuesToTransfer, + receiverCallbackData, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + const takerAssetFillAmount = new BigNumber(1); + // check balances before transfer + const nftOwnerBalance = new BigNumber(1); + const nftNotOwnerBalance = new BigNumber(0); + const expectedInitialBalances = [ + // makerAddress / makerToken + nftOwnerBalance, + // makerAddress / takerToken + nftNotOwnerBalance, + // takerAddress / makerToken + nftNotOwnerBalance, + // takerAddress / takerToken + nftOwnerBalance, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // execute transfer + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + takerAssetFillAmount, + }); + // check balances after transfer + const expectedFinalBalances = [ + // makerAddress / makerToken + expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), + // makerAddress / takerToken + expectedInitialBalances[1].plus(totalTakerValuesTransferred[0]), + // takerAddress / makerToken + expectedInitialBalances[2].plus(totalMakerValuesTransferred[0]), + // takerAddress / takerToken + expectedInitialBalances[3].minus(totalTakerValuesTransferred[0]), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); + it('should allow multiple erc1155 assets to be exchanged for a single asset', async () => { + // setup test parameters + const tokenHolders = [makerAddress, takerAddress]; + const makerAssetsToTransfer = erc1155FungibleTokens.slice(0, 3); + const takerAssetsToTransfer = erc1155NonFungibleTokensOwnedByTaker.slice(0, 1); + const makerValuesToTransfer = [new BigNumber(500), new BigNumber(700), new BigNumber(900)]; + const takerValuesToTransfer = [new BigNumber(1)]; + const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); + const makerAssetAmount = new BigNumber(1); + const takerAssetAmount = new BigNumber(1); + const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { + return value.times(makerAssetAmount); + }); + const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { + return value.times(takerAssetAmount); + }); + const receiverCallbackData = '0x'; + const makerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + makerAssetsToTransfer, + makerValuesToTransfer, + receiverCallbackData, + ); + const takerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + takerAssetsToTransfer, + takerValuesToTransfer, + receiverCallbackData, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + const takerAssetFillAmount = new BigNumber(1); + // check balances before transfer + const nftOwnerBalance = new BigNumber(1); + const nftNotOwnerBalance = new BigNumber(0); + const expectedInitialBalances = [ + // makerAddress / makerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / makerToken[1] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / makerToken[2] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / takerToken + nftNotOwnerBalance, + // takerAddress / makerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / makerToken[1] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / makerToken[2] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / takerToken + nftOwnerBalance, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // execute transfer + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + takerAssetFillAmount, + }); + // check balances after transfer + const expectedFinalBalances = [ + // makerAddress / makerToken[0] + expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), + // makerAddress / makerToken[1] + expectedInitialBalances[1].minus(totalMakerValuesTransferred[1]), + // makerAddress / makerToken[2] + expectedInitialBalances[2].minus(totalMakerValuesTransferred[2]), + // makerAddress / takerToken + expectedInitialBalances[3].plus(totalTakerValuesTransferred[0]), + // takerAddress / makerToken[0] + expectedInitialBalances[4].plus(totalMakerValuesTransferred[0]), + // takerAddress / makerToken[1] + expectedInitialBalances[5].plus(totalMakerValuesTransferred[1]), + // takerAddress / makerToken[2] + expectedInitialBalances[6].plus(totalMakerValuesTransferred[2]), + // takerAddress / takerToken + expectedInitialBalances[7].minus(totalTakerValuesTransferred[0]), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); + it('should allow multiple erc1155 assets to be exchanged for multiple erc1155 assets, mixed fungible/non-fungible', async () => { + // setup test parameters + // the maker is trading two fungibles & one non-fungible + // the taker is trading one fungible & two non-fungibles + const tokenHolders = [makerAddress, takerAddress]; + const makerFungibleAssetsToTransfer = erc1155FungibleTokens.slice(0, 2); + const makerNonFungibleAssetsToTransfer = erc1155NonFungibleTokensOwnedByMaker.slice(0, 1); + const makerAssetsToTransfer = makerFungibleAssetsToTransfer.concat(makerNonFungibleAssetsToTransfer); + const takerFungibleAssetsToTransfer = erc1155FungibleTokens.slice(2, 3); + const takerNonFungibleAssetsToTransfer = erc1155NonFungibleTokensOwnedByTaker.slice(0, 2); + const takerAssetsToTransfer = takerFungibleAssetsToTransfer.concat(takerNonFungibleAssetsToTransfer); + const makerValuesToTransfer = [new BigNumber(500), new BigNumber(700), new BigNumber(1)]; + const takerValuesToTransfer = [new BigNumber(900), new BigNumber(1), new BigNumber(1)]; + const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); + const makerAssetAmount = new BigNumber(1); + const takerAssetAmount = new BigNumber(1); + const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { + return value.times(makerAssetAmount); + }); + const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { + return value.times(takerAssetAmount); + }); + const receiverCallbackData = '0x'; + const makerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + makerAssetsToTransfer, + makerValuesToTransfer, + receiverCallbackData, + ); + const takerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + takerAssetsToTransfer, + takerValuesToTransfer, + receiverCallbackData, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + const takerAssetFillAmount = new BigNumber(1); + // check balances before transfer + const nftOwnerBalance = new BigNumber(1); + const nftNotOwnerBalance = new BigNumber(0); + const expectedInitialBalances = [ + // makerAddress / makerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / makerToken[1] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / makerToken[2] + nftOwnerBalance, + // makerAddress / takerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / takerToken[1] + nftNotOwnerBalance, + // makerAddress / takerToken[2] + nftNotOwnerBalance, + // takerAddress / makerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / makerToken[1] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / makerToken[2] + nftNotOwnerBalance, + // takerAddress / takerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / takerToken[1] + nftOwnerBalance, + // takerAddress / takerToken[2] + nftOwnerBalance, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // execute transfer + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + takerAssetFillAmount, + }); + // check balances after transfer + const expectedFinalBalances = [ + // makerAddress / makerToken[0] + expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), + // makerAddress / makerToken[1] + expectedInitialBalances[1].minus(totalMakerValuesTransferred[1]), + // makerAddress / makerToken[2] + expectedInitialBalances[2].minus(totalMakerValuesTransferred[2]), + // makerAddress / takerToken[0] + expectedInitialBalances[3].plus(totalTakerValuesTransferred[0]), + // makerAddress / takerToken[1] + expectedInitialBalances[4].plus(totalTakerValuesTransferred[1]), + // makerAddress / takerToken[2] + expectedInitialBalances[5].plus(totalTakerValuesTransferred[2]), + // takerAddress / makerToken[0] + expectedInitialBalances[6].plus(totalMakerValuesTransferred[0]), + // takerAddress / makerToken[1] + expectedInitialBalances[7].plus(totalMakerValuesTransferred[1]), + // takerAddress / makerToken[2] + expectedInitialBalances[8].plus(totalMakerValuesTransferred[2]), + // takerAddress / takerToken[0] + expectedInitialBalances[9].minus(totalTakerValuesTransferred[0]), + // takerAddress / takerToken[1] + expectedInitialBalances[10].minus(totalTakerValuesTransferred[1]), + // takerAddress / takerToken[2] + expectedInitialBalances[11].minus(totalTakerValuesTransferred[2]), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + }); + it('should allow an order exchanging erc1155 assets to be partially filled', async () => { + // NOTICE: + // As-per the eip1155 standard, there is no way to distinguish between a fungible or non-fungible erc1155 assets. + // Hence we cannot force partial fills to fail if there is a non-fungible asset (which should be fill or kill). + // We considered encoding whether an asset is fungible/non-fungible in erc1155 assetData, but + // this is no more robust than a simple check by the client. Enforcing this at the smart contract level + // is something that could be done with the upcoming static call proxy. + // + // setup test parameters + // the maker is trading two fungibles and the taker is trading one fungible + // note that this will result in a partial fill because the `takerAssetAmount` + // less than the `takerAssetAmount` of the order. + const takerAssetFillAmount = new BigNumber(6); + const tokenHolders = [makerAddress, takerAddress]; + const makerAssetsToTransfer = erc1155FungibleTokens.slice(0, 2); + const takerAssetsToTransfer = erc1155FungibleTokens.slice(2, 3); + const makerValuesToTransfer = [new BigNumber(500), new BigNumber(700)]; + const takerValuesToTransfer = [new BigNumber(900)]; + const tokensToTransfer = makerAssetsToTransfer.concat(takerAssetsToTransfer); + const makerAssetAmount = new BigNumber(10); + const takerAssetAmount = new BigNumber(20); + const totalMakerValuesTransferred = _.map(makerValuesToTransfer, (value: BigNumber) => { + return value + .times(makerAssetAmount) + .times(takerAssetFillAmount) + .dividedToIntegerBy(takerAssetAmount); + }); + const totalTakerValuesTransferred = _.map(takerValuesToTransfer, (value: BigNumber) => { + return value + .times(takerAssetAmount) + .times(takerAssetFillAmount) + .dividedToIntegerBy(takerAssetAmount); + }); + const receiverCallbackData = '0x'; + const makerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + makerAssetsToTransfer, + makerValuesToTransfer, + receiverCallbackData, + ); + const takerAssetData = assetDataUtils.encodeERC1155AssetData( + erc1155Contract.address, + takerAssetsToTransfer, + takerValuesToTransfer, + receiverCallbackData, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + // check balances before transfer + const expectedInitialBalances = [ + // makerAddress / makerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / makerToken[1] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // makerAddress / takerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / makerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / makerToken[1] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + // takerAddress / takerToken[0] + constants.INITIAL_ERC1155_FUNGIBLE_BALANCE, + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedInitialBalances); + // execute transfer + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + takerAssetFillAmount, + }); + // check balances after transfer + const expectedFinalBalances = [ + // makerAddress / makerToken[0] + expectedInitialBalances[0].minus(totalMakerValuesTransferred[0]), + // makerAddress / makerToken[1] + expectedInitialBalances[1].minus(totalMakerValuesTransferred[1]), + // makerAddress / takerToken[0] + expectedInitialBalances[2].plus(totalTakerValuesTransferred[0]), + // takerAddress / makerToken[0] + expectedInitialBalances[3].plus(totalMakerValuesTransferred[0]), + // takerAddress / makerToken[1] + expectedInitialBalances[4].plus(totalMakerValuesTransferred[1]), + // takerAddress / takerToken[0] + expectedInitialBalances[5].minus(totalTakerValuesTransferred[0]), + ]; + await erc1155Wrapper.assertBalancesAsync(tokenHolders, tokensToTransfer, expectedFinalBalances); + // check that the order is partially filled + const orderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrder); + const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder); + const expectedTakerAssetFilledAmount = takerAssetFillAmount; + const expectedOrderStatus = OrderStatus.Fillable; + expect(orderInfo.orderHash).to.be.equal(expectedOrderHash); + expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(expectedTakerAssetFilledAmount); + expect(orderInfo.orderStatus).to.equal(expectedOrderStatus); + }); + }); describe('getOrderInfo', () => { beforeEach(async () => { diff --git a/contracts/exchange/test/utils/exchange_wrapper.ts b/contracts/exchange/test/utils/exchange_wrapper.ts index fef8b55db3..a06e35a462 100644 --- a/contracts/exchange/test/utils/exchange_wrapper.ts +++ b/contracts/exchange/test/utils/exchange_wrapper.ts @@ -1,3 +1,4 @@ +import { artifacts as erc1155Artifacts } from '@0x/contracts-erc1155'; import { artifacts as erc20Artifacts } from '@0x/contracts-erc20'; import { artifacts as erc721Artifacts } from '@0x/contracts-erc721'; import { @@ -25,7 +26,12 @@ export class ExchangeWrapper { constructor(exchangeContract: ExchangeContract, provider: Web3ProviderEngine | ZeroExProvider) { this._exchange = exchangeContract; this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, { ...artifacts, ...erc20Artifacts, ...erc721Artifacts }); + this._logDecoder = new LogDecoder(this._web3Wrapper, { + ...artifacts, + ...erc20Artifacts, + ...erc721Artifacts, + ...erc1155Artifacts, + }); } public async fillOrderAsync( signedOrder: SignedOrder, diff --git a/contracts/extensions/CHANGELOG.json b/contracts/extensions/CHANGELOG.json index 88f6fc4628..337addf6af 100644 --- a/contracts/extensions/CHANGELOG.json +++ b/contracts/extensions/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "3.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Do not reexport external dependencies", "pr": 1682 diff --git a/contracts/extensions/compiler.json b/contracts/extensions/compiler.json index 9e8471e32b..bf3756c15b 100644 --- a/contracts/extensions/compiler.json +++ b/contracts/extensions/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/multisig/CHANGELOG.json b/contracts/multisig/CHANGELOG.json index de02c953b0..9c224349a3 100644 --- a/contracts/multisig/CHANGELOG.json +++ b/contracts/multisig/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "3.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Do not reexport external dependencies", "pr": 1682 diff --git a/contracts/multisig/compiler.json b/contracts/multisig/compiler.json index 2395d7c8ff..942df6bb25 100644 --- a/contracts/multisig/compiler.json +++ b/contracts/multisig/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/contracts/test-utils/src/constants.ts b/contracts/test-utils/src/constants.ts index 2e58d764d0..199042edc0 100644 --- a/contracts/test-utils/src/constants.ts +++ b/contracts/test-utils/src/constants.ts @@ -38,7 +38,7 @@ export const constants = { NUM_DUMMY_ERC20_TO_DEPLOY: 3, NUM_DUMMY_ERC721_TO_DEPLOY: 2, NUM_ERC721_TOKENS_TO_MINT: 2, - NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY: 1, + NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY: 2, NUM_ERC1155_FUNGIBLE_TOKENS_MINT: 3, NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT: 3, NULL_ADDRESS: '0x0000000000000000000000000000000000000000', diff --git a/contracts/utils/CHANGELOG.json b/contracts/utils/CHANGELOG.json index a3787b8dbc..ecec7db642 100644 --- a/contracts/utils/CHANGELOG.json +++ b/contracts/utils/CHANGELOG.json @@ -2,10 +2,6 @@ { "version": "3.0.0", "changes": [ - { - "note": "Set evmVersion to byzantium", - "pr": 1678 - }, { "note": "Optimize loops in LibAddressArray", "pr": 1668 diff --git a/contracts/utils/compiler.json b/contracts/utils/compiler.json index 7dd36ba4d9..4632bf57b7 100644 --- a/contracts/utils/compiler.json +++ b/contracts/utils/compiler.json @@ -4,7 +4,7 @@ "useDockerisedSolc": true, "isOfflineMode": false, "compilerSettings": { - "evmVersion": "byzantium", + "evmVersion": "constantinople", "optimizer": { "enabled": true, "runs": 1000000, diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index 25468868b3..5ad875ea20 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Add addresses for coordinator extension contract and coordinator registry", "pr": 1689 + }, + { + "note": "Added Dutch Auction mainnet address", + "pr": 1715 } ] }, diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index f260196eff..b5318c38d8 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -34,8 +34,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { assetProxyOwner: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6', forwarder: '0x5468a1dc173652ee28d249c271fa9933144746b1', orderValidator: '0x9463e518dea6810309563c81d5266c1b1d149138', - // @todo hysz/dekz: Add mainnet address once deployed. - dutchAuction: NULL_ADDRESS, + dutchAuction: '0x07b32a653754945666cfca91168bb207323dfe67', coordinatorRegistry: '0x45797531b873fd5e519477a070a955764c1a5b07', coordinator: '0x24675738816c87ad37e712cc24f309a0c906187f', }, diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index 2634e7f595..203956948e 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -1,6 +1,6 @@ import * as chai from 'chai'; -import { AssetProxyId, ERC721AssetData } from '@0x/types'; +import { AssetProxyId, ERC1155AssetData, ERC721AssetData } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { assetDataUtils } from '../src/asset_data_utils'; @@ -30,10 +30,14 @@ const KNOWN_ERC1155_ENCODING = { '0x9645780d0000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000', }; const KNOWN_MULTI_ASSET_ENCODING = { - amounts: [new BigNumber(1), new BigNumber(1)], - nestedAssetData: [KNOWN_ERC20_ENCODING.assetData, KNOWN_ERC721_ENCODING.assetData], + amounts: [new BigNumber(70), new BigNumber(1), new BigNumber(18)], + nestedAssetData: [ + KNOWN_ERC20_ENCODING.assetData, + KNOWN_ERC721_ENCODING.assetData, + KNOWN_ERC1155_ENCODING.assetData, + ], assetData: - '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000', + '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c4800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002049645780d0000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c4800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', }; describe('assetDataUtils', () => { @@ -76,14 +80,14 @@ describe('assetDataUtils', () => { expect(decodedAssetData.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); expect(decodedAssetData.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); }); - it('should encode ERC20 and ERC721 multiAssetData', () => { + it('should encode ERC20, ERC721 and ERC1155 multiAssetData', () => { const assetData = assetDataUtils.encodeMultiAssetData( KNOWN_MULTI_ASSET_ENCODING.amounts, KNOWN_MULTI_ASSET_ENCODING.nestedAssetData, ); expect(assetData).to.equal(KNOWN_MULTI_ASSET_ENCODING.assetData); }); - it('should decode ERC20 and ERC721 multiAssetData', () => { + it('should decode ERC20, ERC721 and ERC1155 multiAssetData', () => { const decodedAssetData = assetDataUtils.decodeMultiAssetData(KNOWN_MULTI_ASSET_ENCODING.assetData); expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); @@ -93,44 +97,79 @@ describe('assetDataUtils', () => { const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(KNOWN_MULTI_ASSET_ENCODING.assetData); expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); + expect(decodedAssetData.nestedAssetData.length).to.equal(3); const decodedErc20AssetData = decodedAssetData.nestedAssetData[0]; - // tslint:disable-next-line:no-unnecessary-type-assertion - const decodedErc721AssetData = decodedAssetData.nestedAssetData[1] as ERC721AssetData; expect(decodedErc20AssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); expect(decodedErc20AssetData.assetProxyId).to.equal(AssetProxyId.ERC20); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData = decodedAssetData.nestedAssetData[1] as ERC721AssetData; expect(decodedErc721AssetData.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); expect(decodedErc721AssetData.assetProxyId).to.equal(AssetProxyId.ERC721); expect(decodedErc721AssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc1155AssetData = decodedAssetData.nestedAssetData[2] as ERC1155AssetData; + expect(decodedErc1155AssetData.tokenAddress).to.be.equal(KNOWN_ERC1155_ENCODING.tokenAddress); + expect(decodedErc1155AssetData.tokenValues).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenValues); + expect(decodedErc1155AssetData.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); + expect(decodedErc1155AssetData.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); }); it('should recursively decode nested assetData within multiAssetData', () => { - const amounts = [new BigNumber(1), new BigNumber(1), new BigNumber(2)]; + // setup test parameters + const erc20Amount = new BigNumber(1); + const erc721Amount = new BigNumber(1); + const erc1155Amount = new BigNumber(15); + const nestedAssetsAmount = new BigNumber(2); + const amounts = [erc20Amount, erc721Amount, erc1155Amount, nestedAssetsAmount]; const nestedAssetData = [ KNOWN_ERC20_ENCODING.assetData, KNOWN_ERC721_ENCODING.assetData, + KNOWN_ERC1155_ENCODING.assetData, KNOWN_MULTI_ASSET_ENCODING.assetData, ]; const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + // execute test const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(assetData); + // validate asset data expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); - const expectedAmounts = [new BigNumber(1), new BigNumber(1), new BigNumber(2), new BigNumber(2)]; + const expectedAmounts = [ + erc20Amount, + erc721Amount, + erc1155Amount, + KNOWN_MULTI_ASSET_ENCODING.amounts[0].times(nestedAssetsAmount), + KNOWN_MULTI_ASSET_ENCODING.amounts[1].times(nestedAssetsAmount), + KNOWN_MULTI_ASSET_ENCODING.amounts[2].times(nestedAssetsAmount), + ]; expect(decodedAssetData.amounts).to.deep.equal(expectedAmounts); - const expectedLength = 4; - expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedLength); - const decodedErc20AssetData1 = decodedAssetData.nestedAssetData[0]; - // tslint:disable-next-line:no-unnecessary-type-assertion - const decodedErc721AssetData1 = decodedAssetData.nestedAssetData[1] as ERC721AssetData; - const decodedErc20AssetData2 = decodedAssetData.nestedAssetData[2]; - // tslint:disable-next-line:no-unnecessary-type-assertion - const decodedErc721AssetData2 = decodedAssetData.nestedAssetData[3] as ERC721AssetData; + const expectedNestedAssetDataLength = 6; + expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedNestedAssetDataLength); + // validate nested asset data (outer) + let nestedAssetDataIndex = 0; + const decodedErc20AssetData1 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++]; expect(decodedErc20AssetData1.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); expect(decodedErc20AssetData1.assetProxyId).to.equal(AssetProxyId.ERC20); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData1 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC721AssetData; expect(decodedErc721AssetData1.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); expect(decodedErc721AssetData1.assetProxyId).to.equal(AssetProxyId.ERC721); - expect(decodedErc721AssetData1.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc1155AssetData1 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC1155AssetData; + expect(decodedErc1155AssetData1.tokenAddress).to.be.equal(KNOWN_ERC1155_ENCODING.tokenAddress); + expect(decodedErc1155AssetData1.tokenValues).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenValues); + expect(decodedErc1155AssetData1.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); + expect(decodedErc1155AssetData1.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); + // validate nested asset data (inner) + const decodedErc20AssetData2 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++]; expect(decodedErc20AssetData2.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); expect(decodedErc20AssetData2.assetProxyId).to.equal(AssetProxyId.ERC20); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData2 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC721AssetData; expect(decodedErc721AssetData2.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); expect(decodedErc721AssetData2.assetProxyId).to.equal(AssetProxyId.ERC721); - expect(decodedErc721AssetData2.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc1155AssetData2 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC1155AssetData; + expect(decodedErc1155AssetData2.tokenAddress).to.be.equal(KNOWN_ERC1155_ENCODING.tokenAddress); + expect(decodedErc1155AssetData2.tokenValues).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenValues); + expect(decodedErc1155AssetData2.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); + expect(decodedErc1155AssetData2.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); }); }); diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index 8d4e22944a..4e0015bce4 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -9,6 +9,10 @@ { "note": "Fix issue where ERC721 Approval events could cause a lookup on undefined object", "pr": 1692 + }, + { + "note": "Fix race-condition bugs due to async event callbacks modifying shared state", + "pr": 1718 } ] }, diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index bdb7d2a84c..7558a01052 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -81,6 +81,7 @@ "ethereumjs-blockstream": "6.0.0", "ethers": "~4.0.4", "lodash": "^4.17.11", + "semaphore-async-await": "^1.5.1", "websocket": "^1.0.26" }, "publishConfig": { diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts index d1cab2b10d..5ee01c42c0 100644 --- a/packages/order-watcher/src/order_watcher/order_watcher.ts +++ b/packages/order-watcher/src/order_watcher/order_watcher.ts @@ -42,6 +42,7 @@ import { ZeroExProvider, } from 'ethereum-types'; import * as _ from 'lodash'; +import { Lock } from 'semaphore-async-await'; import { orderWatcherPartialConfigSchema } from '../schemas/order_watcher_partial_config_schema'; import { OnOrderStateChangeCallback, OrderWatcherConfig, OrderWatcherError } from '../types'; @@ -84,6 +85,7 @@ export class OrderWatcher { private readonly _dependentOrderHashesTracker: DependentOrderHashesTracker; private readonly _orderStateByOrderHashCache: OrderStateByOrderHash = {}; private readonly _orderByOrderHash: OrderByOrderHash = {}; + private readonly _lock = new Lock(); private readonly _eventWatcher: EventWatcher; private readonly _provider: ZeroExProvider; private readonly _collisionResistantAbiDecoder: CollisionResistanceAbiDecoder; @@ -196,10 +198,12 @@ export class OrderWatcher { throw new Error(OrderWatcherError.SubscriptionAlreadyPresent); } this._callbackIfExists = callback; - this._eventWatcher.subscribe(this._onEventWatcherCallbackAsync.bind(this)); - this._expirationWatcher.subscribe(this._onOrderExpired.bind(this)); + this._eventWatcher.subscribe( + this._addLockToCallbackAsync.bind(this, this._onEventWatcherCallbackAsync.bind(this)), + ); + this._expirationWatcher.subscribe(this._addLockToCallbackAsync.bind(this, this._onOrderExpired.bind(this))); this._cleanupJobIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval( - this._cleanupAsync.bind(this), + this._addLockToCallbackAsync.bind(this, this._cleanupAsync.bind(this)), this._cleanupJobInterval, (err: Error) => { this.unsubscribe(); @@ -229,6 +233,17 @@ export class OrderWatcher { orderCount: _.size(this._orderByOrderHash), }; } + private async _addLockToCallbackAsync(cbAsync: any, ...params: any[]): Promise { + await this._lock.acquire(); + try { + await cbAsync(...params); + await this._lock.release(); + } catch (err) { + // Make sure to releasee the lock if an error is thrown + await this._lock.release(); + throw err; + } + } private async _cleanupAsync(): Promise { for (const orderHash of _.keys(this._orderByOrderHash)) { this._cleanupOrderRelatedState(orderHash); @@ -493,4 +508,4 @@ export class OrderWatcher { this._callbackIfExists(null, orderState); } } -} +} // tslint:disable:max-file-line-count diff --git a/packages/order-watcher/test/order_watcher_test.ts b/packages/order-watcher/test/order_watcher_test.ts index d272c7fa45..051c3121a9 100644 --- a/packages/order-watcher/test/order_watcher_test.ts +++ b/packages/order-watcher/test/order_watcher_test.ts @@ -175,10 +175,14 @@ describe('OrderWatcher', () => { }); }); describe('tests with cleanup', async () => { + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); afterEach(async () => { orderWatcher.unsubscribe(); const orderHash = orderHashUtils.getOrderHashHex(signedOrder); orderWatcher.removeOrder(orderHash); + await blockchainLifecycle.revertAsync(); }); it('should emit orderStateInvalid when makerAddress allowance set to 0 for watched order', (done: DoneCallback) => { (async () => { diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index cda828df83..7851c865f0 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "4.0.3", + "changes": [ + { + "note": "Update ganache-core to 2.5.3", + "pr": 1707 + } + ] + }, { "timestamp": 1551479279, "version": "4.0.2", diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index 925fa66b52..49dadb2ed9 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -45,7 +45,7 @@ "ethereum-types": "^2.1.0", "ethereumjs-tx": "^1.3.5", "ethereumjs-util": "^5.1.1", - "ganache-core": "^2.3.3", + "ganache-core": "^2.5.3", "hdkey": "^0.7.1", "json-rpc-error": "2.0.0", "lodash": "^4.17.11", diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index 6f92903073..216e058d70 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "6.0.3", + "changes": [ + { + "note": "Update ganache-core to 2.5.3", + "pr": 1707 + } + ] + }, { "timestamp": 1551479279, "version": "6.0.2", diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 21ed0f28f2..d93e1d1dc0 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -42,7 +42,7 @@ "chai-as-promised": "^7.1.0", "chai-bignumber": "^3.0.0", "dirty-chai": "^2.0.1", - "ganache-core": "^2.3.3", + "ganache-core": "^2.5.3", "make-promises-safe": "^1.1.0", "mocha": "^4.1.0", "npm-run-all": "^4.1.2", diff --git a/packages/web3-wrapper/test/web3_wrapper_test.ts b/packages/web3-wrapper/test/web3_wrapper_test.ts index c4ee91d3dc..54f77d75b6 100644 --- a/packages/web3-wrapper/test/web3_wrapper_test.ts +++ b/packages/web3-wrapper/test/web3_wrapper_test.ts @@ -35,7 +35,7 @@ describe('Web3Wrapper tests', () => { describe('#getNodeVersionAsync', () => { it('gets the node version', async () => { const nodeVersion = await web3Wrapper.getNodeVersionAsync(); - const NODE_VERSION = 'EthereumJS TestRPC/v2.3.3/ethereum-js'; + const NODE_VERSION = 'EthereumJS TestRPC/v2.5.3/ethereum-js'; expect(nodeVersion).to.be.equal(NODE_VERSION); }); }); diff --git a/packages/website/md/docs/sol_coverage/usage.md b/packages/website/md/docs/sol_coverage/usage.md index 89cee45122..202bb1aad8 100644 --- a/packages/website/md/docs/sol_coverage/usage.md +++ b/packages/website/md/docs/sol_coverage/usage.md @@ -8,10 +8,10 @@ In order to use `CoverageSubprovider` with your favorite framework you need to p ### Sol-compiler -If you are generating your artifacts with [@0x/sol-compiler](https://0x.org/docs/sol-compiler) you can use the `SolCompilerArtifactsAdapter` we've implemented for you. +If you are generating your artifacts with [@0x/sol-compiler](https://0x.org/docs/sol-compiler) you can use the `SolCompilerArtifactAdapter` we've implemented for you. ```typescript -import { SolCompilerArtifactsAdapter } from '@0x/sol-coverage'; +import { SolCompilerArtifactAdapter } from '@0x/sol-coverage'; // Both artifactsDir and contractsDir are optional and will be fetched from compiler.json if not passed in const artifactAdapter = new SolCompilerArtifactAdapter(artifactsDir, contractsDir); ``` diff --git a/packages/website/md/docs/sol_profiler/usage.md b/packages/website/md/docs/sol_profiler/usage.md index 0fbc31bc10..51e0b7bb73 100644 --- a/packages/website/md/docs/sol_profiler/usage.md +++ b/packages/website/md/docs/sol_profiler/usage.md @@ -8,10 +8,10 @@ In order to use `ProfilerSubprovider` with your favorite framework you need to p ### Sol-compiler -If you are generating your artifacts with [@0x/sol-compiler](https://0x.org/docs/sol-compiler) you can use the `SolCompilerArtifactsAdapter` we've implemented for you. +If you are generating your artifacts with [@0x/sol-compiler](https://0x.org/docs/sol-compiler) you can use the `SolCompilerArtifactAdapter` we've implemented for you. ```typescript -import { SolCompilerArtifactsAdapter } from '@0x/sol-profiler'; +import { SolCompilerArtifactAdapter } from '@0x/sol-profiler'; // Both artifactsDir and contractsDir are optional and will be fetched from compiler.json if not passed in const artifactAdapter = new SolCompilerArtifactAdapter(artifactsDir, contractsDir); ``` diff --git a/packages/website/md/docs/sol_trace/usage.md b/packages/website/md/docs/sol_trace/usage.md index d117a0faf1..0afe8c653d 100644 --- a/packages/website/md/docs/sol_trace/usage.md +++ b/packages/website/md/docs/sol_trace/usage.md @@ -8,10 +8,10 @@ In order to use `RevertTraceSubprovider` with your favorite framework you need t ### Sol-compiler -If you are generating your artifacts with [@0x/sol-compiler](https://0x.org/docs/sol-compiler) you can use the `SolCompilerArtifactsAdapter` we've implemented for you. +If you are generating your artifacts with [@0x/sol-compiler](https://0x.org/docs/sol-compiler) you can use the `SolCompilerArtifactAdapter` we've implemented for you. ```typescript -import { SolCompilerArtifactsAdapter } from '@0x/sol-trace'; +import { SolCompilerArtifactAdapter } from '@0x/sol-trace'; // Both artifactsDir and contractsDir are optional and will be fetched from compiler.json if not passed in const artifactAdapter = new SolCompilerArtifactAdapter(artifactsDir, contractsDir); ``` diff --git a/packages/website/public/images/team/danielp.png b/packages/website/public/images/team/danielp.png new file mode 100644 index 0000000000..d388e0df37 Binary files /dev/null and b/packages/website/public/images/team/danielp.png differ diff --git a/packages/website/ts/pages/about/team.tsx b/packages/website/ts/pages/about/team.tsx index 9efa9c5943..d93bdf9bc4 100644 --- a/packages/website/ts/pages/about/team.tsx +++ b/packages/website/ts/pages/about/team.tsx @@ -158,6 +158,11 @@ const team: TeamMember[] = [ name: 'Marc Savino', title: 'technical sourcer', }, + { + imageUrl: '/images/team/danielp.png', + name: 'Daniel Pyrathon', + title: 'engineer', + }, ]; const advisors: TeamMember[] = [ diff --git a/yarn.lock b/yarn.lock index 4f342df7da..19f79e4574 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6495,6 +6495,16 @@ ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0, ether ethereumjs-util "^5.0.0" merkle-patricia-tree "^2.1.2" +ethereumjs-block@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.0.tgz#8c6c3ab4a5eff0a16d9785fbeedbe643f4dbcbef" + dependencies: + async "^2.0.1" + ethereumjs-common "^1.1.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + ethereumjs-blockstream@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/ethereumjs-blockstream/-/ethereumjs-blockstream-6.0.0.tgz#79d726d1f358935eb65195e91d40344c31e87eff" @@ -6507,9 +6517,9 @@ ethereumjs-common@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-0.6.1.tgz#ec98edf315a7f107afb6acc48e937a8266979fae" -ethereumjs-common@~0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-0.4.1.tgz#27690a24a817b058cc3a2aedef9392e8d7d63984" +ethereumjs-common@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.1.0.tgz#5ec9086c314d619d8f05e79a0525829fcb0e93cb" ethereumjs-tx@1.3.7: version "1.3.7" @@ -6559,21 +6569,17 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereum safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-vm@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.4.0.tgz#244f1e35f2755e537a13546111d1a4c159d34b13" +ethereumjs-util@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz#e9c51e5549e8ebd757a339cc00f5380507e799c8" dependencies: - async "^2.1.2" - async-eventemitter "^0.2.2" - ethereumjs-account "^2.0.3" - ethereumjs-block "~1.7.0" - ethereumjs-common "~0.4.0" - ethereumjs-util "^5.2.0" - fake-merkle-patricia-tree "^1.0.1" - functional-red-black-tree "^1.0.1" - merkle-patricia-tree "^2.1.2" - rustbn.js "~0.2.0" + bn.js "^4.11.0" + create-hash "^1.1.2" + ethjs-util "0.1.6" + keccak "^1.0.2" + rlp "^2.0.0" safe-buffer "^5.1.1" + secp256k1 "^3.0.1" ethereumjs-vm@^2.0.2, ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4: version "2.3.4" @@ -6591,6 +6597,22 @@ ethereumjs-vm@^2.0.2, ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4: rustbn.js "~0.1.1" safe-buffer "^5.1.1" +ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + ethereumjs-wallet@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.2.tgz#67244b6af3e8113b53d709124b25477b64aeccda" @@ -6642,6 +6664,13 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" +ethjs-util@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + ethjs-util@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.4.tgz#1c8b6879257444ef4d3f3fbbac2ded12cd997d93" @@ -7506,9 +7535,9 @@ ganache-cli@6.4.1: source-map-support "0.5.9" yargs "11.1.0" -ganache-core@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.3.3.tgz#e35c76d405f0ffba5c48621596fdcc38b0a03136" +ganache-core@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.5.3.tgz#8c6f21d820a694826082dfbb2dc59f834a6874fc" dependencies: abstract-leveldown "3.0.0" async "2.6.1" @@ -7524,11 +7553,11 @@ ganache-core@^2.3.3: ethereumjs-block "2.1.0" ethereumjs-tx "1.3.7" ethereumjs-util "5.2.0" - ethereumjs-vm "2.4.0" + ethereumjs-vm "^2.6.0" heap "0.2.6" level-sublevel "6.6.4" levelup "3.1.1" - lodash "4.17.10" + lodash "4.17.11" merkle-patricia-tree "2.3.1" rlp "2.1.0" seedrandom "2.4.4" @@ -10627,9 +10656,9 @@ lodash.words@^3.0.0: dependencies: lodash._root "^3.0.0" -lodash@4.17.10, lodash@^4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" +lodash@4.17.11, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.3: + version "4.17.11" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" lodash@=4.17.4: version "4.17.4" @@ -10639,14 +10668,14 @@ lodash@^3.3.1, lodash@^3.6.0, lodash@^3.7.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.3: - version "4.17.11" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" - lodash@^4.14.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" +lodash@^4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" @@ -11046,6 +11075,19 @@ merkle-patricia-tree@2.3.1, merkle-patricia-tree@^2.1.2: rlp "^2.0.0" semaphore ">=1.0.1" +merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -13506,6 +13548,15 @@ react-dom@^16.3.2: object-assign "^4.1.1" prop-types "^15.6.0" +react-dom@^16.4.2: + version "16.8.4" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.4.tgz#1061a8e01a2b3b0c8160037441c3bf00a0e3bc48" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.4" + react-dom@^16.5.2: version "16.5.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7" @@ -13809,6 +13860,15 @@ react@^16.3.2: object-assign "^4.1.1" prop-types "^15.6.0" +react@^16.4.2: + version "16.8.4" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.4.tgz#fdf7bd9ae53f03a9c4cd1a371432c206be1c4768" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.4" + react@^16.5.2: version "16.5.2" resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42" @@ -14680,6 +14740,13 @@ schedule@^0.5.0: dependencies: object-assign "^4.1.1" +scheduler@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.4.tgz#8fef05e7a3580c76c0364d2df5e550e4c9140298" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^0.4.4: version "0.4.7" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"