aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml11
-rw-r--r--.gitignore10
-rw-r--r--.prettierignore10
-rw-r--r--CODEOWNERS4
-rw-r--r--README.md2
-rw-r--r--contracts/TESTING.md48
-rw-r--r--contracts/core/.solhint.json (renamed from packages/contracts/.solhint.json)0
-rw-r--r--contracts/core/.solhintignore (renamed from packages/contracts/.solhintignore)0
-rw-r--r--contracts/core/CHANGELOG.json (renamed from packages/contracts/CHANGELOG.json)10
-rw-r--r--contracts/core/README.md (renamed from packages/contracts/README.md)53
-rw-r--r--contracts/core/compiler.json (renamed from packages/contracts/compiler.json)7
-rw-r--r--contracts/core/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol (renamed from packages/contracts/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol)2
-rw-r--r--contracts/core/contracts/examples/Validator/Validator.sol (renamed from packages/contracts/contracts/examples/Validator/Validator.sol)0
-rw-r--r--contracts/core/contracts/examples/Wallet/Wallet.sol (renamed from packages/contracts/contracts/examples/Wallet/Wallet.sol)2
-rw-r--r--contracts/core/contracts/examples/Whitelist/Whitelist.sol (renamed from packages/contracts/contracts/examples/Whitelist/Whitelist.sol)4
-rw-r--r--contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol205
-rw-r--r--contracts/core/contracts/extensions/Forwarder/Forwarder.sol (renamed from packages/contracts/contracts/extensions/Forwarder/Forwarder.sol)0
-rw-r--r--contracts/core/contracts/extensions/Forwarder/MixinAssets.sol (renamed from packages/contracts/contracts/extensions/Forwarder/MixinAssets.sol)4
-rw-r--r--contracts/core/contracts/extensions/Forwarder/MixinExchangeWrapper.sol (renamed from packages/contracts/contracts/extensions/Forwarder/MixinExchangeWrapper.sol)8
-rw-r--r--contracts/core/contracts/extensions/Forwarder/MixinForwarderCore.sol (renamed from packages/contracts/contracts/extensions/Forwarder/MixinForwarderCore.sol)8
-rw-r--r--contracts/core/contracts/extensions/Forwarder/MixinWeth.sol (renamed from packages/contracts/contracts/extensions/Forwarder/MixinWeth.sol)2
-rw-r--r--contracts/core/contracts/extensions/Forwarder/interfaces/IAssets.sol (renamed from packages/contracts/contracts/extensions/Forwarder/interfaces/IAssets.sol)0
-rw-r--r--contracts/core/contracts/extensions/Forwarder/interfaces/IForwarder.sol (renamed from packages/contracts/contracts/extensions/Forwarder/interfaces/IForwarder.sol)0
-rw-r--r--contracts/core/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol (renamed from packages/contracts/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol)4
-rw-r--r--contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol (renamed from packages/contracts/contracts/extensions/Forwarder/libs/LibConstants.sol)2
-rw-r--r--contracts/core/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol (renamed from packages/contracts/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol)0
-rw-r--r--contracts/core/contracts/extensions/Forwarder/mixins/MAssets.sol (renamed from packages/contracts/contracts/extensions/Forwarder/mixins/MAssets.sol)0
-rw-r--r--contracts/core/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol (renamed from packages/contracts/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol)4
-rw-r--r--contracts/core/contracts/extensions/Forwarder/mixins/MWeth.sol (renamed from packages/contracts/contracts/extensions/Forwarder/mixins/MWeth.sol)0
-rw-r--r--contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol (renamed from packages/contracts/contracts/extensions/OrderValidator/OrderValidator.sol)4
-rw-r--r--contracts/core/contracts/protocol/AssetProxy/ERC20Proxy.sol (renamed from packages/contracts/contracts/protocol/AssetProxy/ERC20Proxy.sol)0
-rw-r--r--contracts/core/contracts/protocol/AssetProxy/ERC721Proxy.sol (renamed from packages/contracts/contracts/protocol/AssetProxy/ERC721Proxy.sol)0
-rw-r--r--contracts/core/contracts/protocol/AssetProxy/MixinAuthorizable.sol (renamed from packages/contracts/contracts/protocol/AssetProxy/MixinAuthorizable.sol)2
-rw-r--r--contracts/core/contracts/protocol/AssetProxy/MultiAssetProxy.sol300
-rw-r--r--contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetData.sol (renamed from packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol)16
-rw-r--r--contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol (renamed from packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol)0
-rw-r--r--contracts/core/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol (renamed from packages/contracts/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol)2
-rw-r--r--contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol (renamed from packages/contracts/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol)0
-rw-r--r--contracts/core/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol (renamed from packages/contracts/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol)4
-rw-r--r--contracts/core/contracts/protocol/Exchange/Exchange.sol (renamed from packages/contracts/contracts/protocol/Exchange/Exchange.sol)2
-rw-r--r--contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol (renamed from packages/contracts/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol)2
-rw-r--r--contracts/core/contracts/protocol/Exchange/MixinExchangeCore.sol (renamed from packages/contracts/contracts/protocol/Exchange/MixinExchangeCore.sol)10
-rw-r--r--contracts/core/contracts/protocol/Exchange/MixinMatchOrders.sol (renamed from packages/contracts/contracts/protocol/Exchange/MixinMatchOrders.sol)10
-rw-r--r--contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol (renamed from packages/contracts/contracts/protocol/Exchange/MixinSignatureValidator.sol)16
-rw-r--r--contracts/core/contracts/protocol/Exchange/MixinTransactions.sol (renamed from packages/contracts/contracts/protocol/Exchange/MixinTransactions.sol)4
-rw-r--r--contracts/core/contracts/protocol/Exchange/MixinWrapperFunctions.sol (renamed from packages/contracts/contracts/protocol/Exchange/MixinWrapperFunctions.sol)10
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/IExchange.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/IExchange.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/IExchangeCore.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/IExchangeCore.sol)4
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/IMatchOrders.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/IMatchOrders.sol)4
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/ITransactions.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/ITransactions.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/IValidator.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/IValidator.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/IWallet.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/IWallet.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol (renamed from packages/contracts/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol)4
-rw-r--r--contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol (renamed from packages/contracts/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol (renamed from packages/contracts/contracts/protocol/Exchange/mixins/MExchangeCore.sol)4
-rw-r--r--contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol (renamed from packages/contracts/contracts/protocol/Exchange/mixins/MMatchOrders.sol)4
-rw-r--r--contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol (renamed from packages/contracts/contracts/protocol/Exchange/mixins/MSignatureValidator.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol (renamed from packages/contracts/contracts/protocol/Exchange/mixins/MTransactions.sol)0
-rw-r--r--contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol (renamed from packages/contracts/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol)4
-rw-r--r--contracts/core/contracts/test/DummyERC20Token/DummyERC20Token.sol (renamed from packages/contracts/contracts/test/DummyERC20Token/DummyERC20Token.sol)2
-rw-r--r--contracts/core/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol (renamed from packages/contracts/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol)0
-rw-r--r--contracts/core/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol (renamed from packages/contracts/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol)0
-rw-r--r--contracts/core/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol (renamed from packages/contracts/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol)0
-rw-r--r--contracts/core/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol (renamed from packages/contracts/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol)0
-rw-r--r--contracts/core/contracts/test/DummyERC721Token/DummyERC721Token.sol (renamed from packages/contracts/contracts/test/DummyERC721Token/DummyERC721Token.sol)2
-rw-r--r--contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol (renamed from packages/contracts/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol)32
-rw-r--r--contracts/core/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol (renamed from packages/contracts/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol)0
-rw-r--r--contracts/core/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol (renamed from packages/contracts/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol)0
-rw-r--r--contracts/core/contracts/test/TestExchangeInternals/TestExchangeInternals.sol (renamed from packages/contracts/contracts/test/TestExchangeInternals/TestExchangeInternals.sol)0
-rw-r--r--contracts/core/contracts/test/TestSignatureValidator/TestSignatureValidator.sol (renamed from packages/contracts/contracts/test/TestSignatureValidator/TestSignatureValidator.sol)0
-rw-r--r--contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol (renamed from packages/contracts/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol)0
-rw-r--r--contracts/core/contracts/tokens/ERC20Token/ERC20Token.sol (renamed from packages/contracts/contracts/tokens/ERC20Token/ERC20Token.sol)0
-rw-r--r--contracts/core/contracts/tokens/ERC20Token/IERC20Token.sol (renamed from packages/contracts/contracts/tokens/ERC20Token/IERC20Token.sol)0
-rw-r--r--contracts/core/contracts/tokens/ERC20Token/MintableERC20Token.sol (renamed from packages/contracts/contracts/tokens/ERC20Token/MintableERC20Token.sol)2
-rw-r--r--contracts/core/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol (renamed from packages/contracts/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol)0
-rw-r--r--contracts/core/contracts/tokens/ERC721Token/ERC721Token.sol (renamed from packages/contracts/contracts/tokens/ERC721Token/ERC721Token.sol)2
-rw-r--r--contracts/core/contracts/tokens/ERC721Token/IERC721Receiver.sol (renamed from packages/contracts/contracts/tokens/ERC721Token/IERC721Receiver.sol)0
-rw-r--r--contracts/core/contracts/tokens/ERC721Token/IERC721Token.sol (renamed from packages/contracts/contracts/tokens/ERC721Token/IERC721Token.sol)0
-rw-r--r--contracts/core/contracts/tokens/ERC721Token/MintableERC721Token.sol (renamed from packages/contracts/contracts/tokens/ERC721Token/MintableERC721Token.sol)0
-rw-r--r--contracts/core/contracts/tokens/EtherToken/IEtherToken.sol (renamed from packages/contracts/contracts/tokens/EtherToken/IEtherToken.sol)0
-rw-r--r--contracts/core/contracts/tokens/EtherToken/WETH9.sol (renamed from packages/contracts/contracts/tokens/EtherToken/WETH9.sol)0
-rw-r--r--contracts/core/contracts/tokens/ZRXToken/ERC20Token_v1.sol (renamed from packages/contracts/contracts/tokens/ZRXToken/ERC20Token_v1.sol)0
-rw-r--r--contracts/core/contracts/tokens/ZRXToken/Token_v1.sol (renamed from packages/contracts/contracts/tokens/ZRXToken/Token_v1.sol)0
-rw-r--r--contracts/core/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol (renamed from packages/contracts/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol)0
-rw-r--r--contracts/core/contracts/tokens/ZRXToken/ZRXToken.sol (renamed from packages/contracts/contracts/tokens/ZRXToken/ZRXToken.sol)0
-rw-r--r--contracts/core/package.json (renamed from packages/contracts/package.json)35
-rw-r--r--contracts/core/src/artifacts/index.ts (renamed from packages/contracts/src/artifacts/index.ts)14
-rw-r--r--contracts/core/src/wrappers/index.ts (renamed from packages/contracts/src/wrappers/index.ts)6
-rw-r--r--contracts/core/test/asset_proxy/authorizable.ts (renamed from packages/contracts/test/asset_proxy/authorizable.ts)12
-rw-r--r--contracts/core/test/asset_proxy/proxies.ts1251
-rw-r--r--contracts/core/test/exchange/core.ts (renamed from packages/contracts/test/exchange/core.ts)403
-rw-r--r--contracts/core/test/exchange/dispatcher.ts (renamed from packages/contracts/test/exchange/dispatcher.ts)16
-rw-r--r--contracts/core/test/exchange/fill_order.ts (renamed from packages/contracts/test/exchange/fill_order.ts)22
-rw-r--r--contracts/core/test/exchange/internal.ts (renamed from packages/contracts/test/exchange/internal.ts)18
-rw-r--r--contracts/core/test/exchange/match_orders.ts (renamed from packages/contracts/test/exchange/match_orders.ts)17
-rw-r--r--contracts/core/test/exchange/signature_validator.ts (renamed from packages/contracts/test/exchange/signature_validator.ts)20
-rw-r--r--contracts/core/test/exchange/transactions.ts (renamed from packages/contracts/test/exchange/transactions.ts)21
-rw-r--r--contracts/core/test/exchange/wrapper.ts (renamed from packages/contracts/test/exchange/wrapper.ts)20
-rw-r--r--contracts/core/test/extensions/dutch_auction.ts452
-rw-r--r--contracts/core/test/extensions/forwarder.ts (renamed from packages/contracts/test/extensions/forwarder.ts)23
-rw-r--r--contracts/core/test/extensions/order_validator.ts (renamed from packages/contracts/test/extensions/order_validator.ts)14
-rw-r--r--contracts/core/test/global_hooks.ts17
-rw-r--r--contracts/core/test/multisig/asset_proxy_owner.ts (renamed from packages/contracts/test/multisig/asset_proxy_owner.ts)102
-rw-r--r--contracts/core/test/tokens/erc721_token.ts (renamed from packages/contracts/test/tokens/erc721_token.ts)17
-rw-r--r--contracts/core/test/tokens/unlimited_allowance_token.ts (renamed from packages/contracts/test/tokens/unlimited_allowance_token.ts)12
-rw-r--r--contracts/core/test/tokens/weth9.ts (renamed from packages/contracts/test/tokens/weth9.ts)13
-rw-r--r--contracts/core/test/tokens/zrx_token.ts (renamed from packages/contracts/test/tokens/zrx_token.ts)4
-rw-r--r--contracts/core/test/tutorials/arbitrage.ts (renamed from packages/contracts/test/tutorials/arbitrage.ts)0
-rw-r--r--contracts/core/test/utils/asset_proxy_owner_wrapper.ts69
-rw-r--r--contracts/core/test/utils/asset_wrapper.ts (renamed from packages/contracts/test/utils/asset_wrapper.ts)3
-rw-r--r--contracts/core/test/utils/erc20_wrapper.ts (renamed from packages/contracts/test/utils/erc20_wrapper.ts)5
-rw-r--r--contracts/core/test/utils/erc721_wrapper.ts (renamed from packages/contracts/test/utils/erc721_wrapper.ts)11
-rw-r--r--contracts/core/test/utils/exchange_wrapper.ts (renamed from packages/contracts/test/utils/exchange_wrapper.ts)16
-rw-r--r--contracts/core/test/utils/fill_order_combinatorial_utils.ts (renamed from packages/contracts/test/utils/fill_order_combinatorial_utils.ts)42
-rw-r--r--contracts/core/test/utils/forwarder_wrapper.ts (renamed from packages/contracts/test/utils/forwarder_wrapper.ts)9
-rw-r--r--contracts/core/test/utils/match_order_tester.ts (renamed from packages/contracts/test/utils/match_order_tester.ts)26
-rw-r--r--contracts/core/test/utils/order_factory_from_scenario.ts (renamed from packages/contracts/test/utils/order_factory_from_scenario.ts)15
-rw-r--r--contracts/core/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts (renamed from packages/contracts/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts)0
-rw-r--r--contracts/core/test/utils/simple_order_filled_cancelled_fetcher.ts (renamed from packages/contracts/test/utils/simple_order_filled_cancelled_fetcher.ts)0
-rw-r--r--contracts/core/tsconfig.json (renamed from packages/contracts/tsconfig.json)7
-rw-r--r--contracts/core/tslint.json (renamed from packages/contracts/tslint.json)0
-rw-r--r--contracts/libs/.solhint.json20
-rw-r--r--contracts/libs/README.md70
-rw-r--r--contracts/libs/compiler.json22
-rw-r--r--contracts/libs/contracts/libs/LibAbiEncoder.sol (renamed from packages/contracts/contracts/protocol/Exchange/libs/LibAbiEncoder.sol)0
-rw-r--r--contracts/libs/contracts/libs/LibAssetProxyErrors.sol (renamed from packages/contracts/contracts/protocol/AssetProxy/libs/LibAssetProxyErrors.sol)0
-rw-r--r--contracts/libs/contracts/libs/LibConstants.sol (renamed from packages/contracts/contracts/protocol/Exchange/libs/LibConstants.sol)0
-rw-r--r--contracts/libs/contracts/libs/LibEIP712.sol (renamed from packages/contracts/contracts/protocol/Exchange/libs/LibEIP712.sol)0
-rw-r--r--contracts/libs/contracts/libs/LibExchangeErrors.sol (renamed from packages/contracts/contracts/protocol/Exchange/libs/LibExchangeErrors.sol)0
-rw-r--r--contracts/libs/contracts/libs/LibFillResults.sol (renamed from packages/contracts/contracts/protocol/Exchange/libs/LibFillResults.sol)2
-rw-r--r--contracts/libs/contracts/libs/LibMath.sol (renamed from packages/contracts/contracts/protocol/Exchange/libs/LibMath.sol)2
-rw-r--r--contracts/libs/contracts/libs/LibOrder.sol (renamed from packages/contracts/contracts/protocol/Exchange/libs/LibOrder.sol)0
-rw-r--r--contracts/libs/contracts/test/TestLibs/TestLibs.sol (renamed from packages/contracts/contracts/test/TestLibs/TestLibs.sol)8
-rw-r--r--contracts/libs/package.json92
-rw-r--r--contracts/libs/src/artifacts/index.ts17
-rw-r--r--contracts/libs/src/index.ts2
-rw-r--r--contracts/libs/src/wrappers/index.ts6
-rw-r--r--contracts/libs/test/exchange/libs.ts (renamed from packages/contracts/test/exchange/libs.ts)30
-rw-r--r--contracts/libs/test/global_hooks.ts17
-rw-r--r--contracts/libs/tsconfig.json18
-rw-r--r--contracts/libs/tslint.json6
-rw-r--r--contracts/multisig/.solhint.json20
-rw-r--r--contracts/multisig/CHANGELOG.json1
-rw-r--r--contracts/multisig/README.md70
-rw-r--r--contracts/multisig/compiler.json22
-rw-r--r--contracts/multisig/contracts/multisig/MultiSigWallet.sol (renamed from packages/contracts/contracts/multisig/MultiSigWallet.sol)0
-rw-r--r--contracts/multisig/contracts/multisig/MultiSigWalletWithTimeLock.sol (renamed from packages/contracts/contracts/multisig/MultiSigWalletWithTimeLock.sol)0
-rw-r--r--contracts/multisig/contracts/test/TestRejectEther/TestRejectEther.sol23
-rw-r--r--contracts/multisig/package.json86
-rw-r--r--contracts/multisig/src/artifacts/index.ts11
-rw-r--r--contracts/multisig/src/wrappers/index.ts2
-rw-r--r--contracts/multisig/test/global_hooks.ts19
-rw-r--r--contracts/multisig/test/multi_sig_with_time_lock.ts (renamed from packages/contracts/test/multisig/multi_sig_with_time_lock.ts)32
-rw-r--r--contracts/multisig/test/utils/multi_sig_wrapper.ts (renamed from packages/contracts/test/utils/multi_sig_wrapper.ts)19
-rw-r--r--contracts/multisig/tsconfig.json15
-rw-r--r--contracts/multisig/tslint.json6
-rw-r--r--contracts/test-utils/README.md73
-rw-r--r--contracts/test-utils/package.json75
-rw-r--r--contracts/test-utils/src/abstract_asset_wrapper.ts (renamed from packages/contracts/test/utils/abstract_asset_wrapper.ts)0
-rw-r--r--contracts/test-utils/src/address_utils.ts (renamed from packages/contracts/test/utils/address_utils.ts)0
-rw-r--r--contracts/test-utils/src/assertions.ts (renamed from packages/contracts/test/utils/assertions.ts)0
-rw-r--r--contracts/test-utils/src/block_timestamp.ts (renamed from packages/contracts/test/utils/block_timestamp.ts)0
-rw-r--r--contracts/test-utils/src/chai_setup.ts (renamed from packages/contracts/test/utils/chai_setup.ts)0
-rw-r--r--contracts/test-utils/src/combinatorial_utils.ts (renamed from packages/contracts/test/utils/combinatorial_utils.ts)0
-rw-r--r--contracts/test-utils/src/constants.ts (renamed from packages/contracts/test/utils/constants.ts)2
-rw-r--r--contracts/test-utils/src/coverage.ts (renamed from packages/contracts/test/utils/coverage.ts)0
-rw-r--r--contracts/test-utils/src/formatters.ts (renamed from packages/contracts/test/utils/formatters.ts)0
-rw-r--r--contracts/test-utils/src/global_hooks.ts (renamed from packages/contracts/test/global_hooks.ts)4
-rw-r--r--contracts/test-utils/src/index.ts55
-rw-r--r--contracts/test-utils/src/log_decoder.ts (renamed from packages/contracts/test/utils/log_decoder.ts)4
-rw-r--r--contracts/test-utils/src/order_factory.ts (renamed from packages/contracts/test/utils/order_factory.ts)0
-rw-r--r--contracts/test-utils/src/order_utils.ts (renamed from packages/contracts/test/utils/order_utils.ts)0
-rw-r--r--contracts/test-utils/src/profiler.ts (renamed from packages/contracts/test/utils/profiler.ts)0
-rw-r--r--contracts/test-utils/src/revert_trace.ts (renamed from packages/contracts/test/utils/revert_trace.ts)0
-rw-r--r--contracts/test-utils/src/signing_utils.ts (renamed from packages/contracts/test/utils/signing_utils.ts)0
-rw-r--r--contracts/test-utils/src/test_with_reference.ts (renamed from packages/contracts/test/utils/test_with_reference.ts)0
-rw-r--r--contracts/test-utils/src/transaction_factory.ts (renamed from packages/contracts/test/utils/transaction_factory.ts)0
-rw-r--r--contracts/test-utils/src/type_encoding_utils.ts (renamed from packages/contracts/test/utils/type_encoding_utils.ts)0
-rw-r--r--contracts/test-utils/src/types.ts (renamed from packages/contracts/test/utils/types.ts)1
-rw-r--r--contracts/test-utils/src/web3_wrapper.ts (renamed from packages/contracts/test/utils/web3_wrapper.ts)1
-rw-r--r--contracts/test-utils/test/test_with_reference.ts (renamed from packages/contracts/test/utils_test/test_with_reference.ts)4
-rw-r--r--contracts/test-utils/tsconfig.json7
-rw-r--r--contracts/test-utils/tsconfig.lint.json7
-rw-r--r--contracts/test-utils/tslint.json6
-rw-r--r--contracts/utils/.solhint.json20
-rw-r--r--contracts/utils/README.md70
-rw-r--r--contracts/utils/compiler.json22
-rw-r--r--contracts/utils/contracts/test/TestConstants/TestConstants.sol (renamed from packages/contracts/contracts/test/TestConstants/TestConstants.sol)2
-rw-r--r--contracts/utils/contracts/test/TestLibBytes/TestLibBytes.sol (renamed from packages/contracts/contracts/test/TestLibBytes/TestLibBytes.sol)2
-rw-r--r--contracts/utils/contracts/utils/LibBytes/LibBytes.sol (renamed from packages/contracts/contracts/utils/LibBytes/LibBytes.sol)0
-rw-r--r--contracts/utils/contracts/utils/Ownable/IOwnable.sol (renamed from packages/contracts/contracts/utils/Ownable/IOwnable.sol)0
-rw-r--r--contracts/utils/contracts/utils/Ownable/Ownable.sol (renamed from packages/contracts/contracts/utils/Ownable/Ownable.sol)0
-rw-r--r--contracts/utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol (renamed from packages/contracts/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol)0
-rw-r--r--contracts/utils/contracts/utils/SafeMath/SafeMath.sol (renamed from packages/contracts/contracts/utils/SafeMath/SafeMath.sol)0
-rw-r--r--contracts/utils/package.json90
-rw-r--r--contracts/utils/src/artifacts/index.ts19
-rw-r--r--contracts/utils/src/index.ts2
-rw-r--r--contracts/utils/src/wrappers/index.ts2
-rw-r--r--contracts/utils/test/global_hooks.ts17
-rw-r--r--contracts/utils/test/lib_bytes.ts (renamed from packages/contracts/test/libraries/lib_bytes.ts)18
-rw-r--r--contracts/utils/test/libs.ts34
-rw-r--r--contracts/utils/tsconfig.json19
-rw-r--r--contracts/utils/tslint.json6
-rw-r--r--lerna.json2
-rw-r--r--package.json3
-rw-r--r--packages/0x.js/.npmignore2
-rw-r--r--packages/0x.js/CHANGELOG.json9
-rw-r--r--packages/0x.js/CHANGELOG.md4
-rw-r--r--packages/0x.js/package.json22
-rw-r--r--packages/abi-gen-templates/CHANGELOG.json9
-rw-r--r--packages/abi-gen-templates/CHANGELOG.md14
-rw-r--r--packages/abi-gen-templates/package.json2
-rw-r--r--packages/abi-gen-wrappers/CHANGELOG.json3
-rw-r--r--packages/abi-gen-wrappers/CHANGELOG.md4
-rw-r--r--packages/abi-gen-wrappers/package.json8
-rw-r--r--packages/abi-gen/README.md4
-rw-r--r--packages/asset-buyer/CHANGELOG.json17
-rw-r--r--packages/asset-buyer/CHANGELOG.md4
-rw-r--r--packages/asset-buyer/package.json12
-rw-r--r--packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts8
-rw-r--r--packages/base-contract/CHANGELOG.json9
-rw-r--r--packages/base-contract/CHANGELOG.md4
-rw-r--r--packages/base-contract/package.json4
-rw-r--r--packages/connect/CHANGELOG.json9
-rw-r--r--packages/connect/CHANGELOG.md4
-rw-r--r--packages/connect/package.json4
-rw-r--r--packages/contract-addresses/CHANGELOG.json14
-rw-r--r--packages/contract-addresses/CHANGELOG.md5
-rw-r--r--packages/contract-addresses/package.json2
-rw-r--r--packages/contract-addresses/src/index.ts26
-rw-r--r--packages/contract-artifacts/CHANGELOG.json3
-rw-r--r--packages/contract-artifacts/CHANGELOG.md4
-rw-r--r--packages/contract-artifacts/package.json2
-rw-r--r--packages/contract-wrappers/.npmignore2
-rw-r--r--packages/contract-wrappers/CHANGELOG.json9
-rw-r--r--packages/contract-wrappers/CHANGELOG.md4
-rw-r--r--packages/contract-wrappers/package.json20
-rw-r--r--packages/contracts/test/asset_proxy/proxies.ts629
-rw-r--r--packages/dev-tools-pages/package.json4
-rw-r--r--packages/dev-utils/CHANGELOG.json9
-rw-r--r--packages/dev-utils/CHANGELOG.md4
-rw-r--r--packages/dev-utils/README.md15
-rw-r--r--packages/dev-utils/package.json6
-rw-r--r--packages/ethereum-types/src/index.ts5
-rw-r--r--packages/fill-scenarios/CHANGELOG.json9
-rw-r--r--packages/fill-scenarios/CHANGELOG.md4
-rw-r--r--packages/fill-scenarios/package.json12
-rw-r--r--packages/instant/.DS_Storebin0 -> 8196 bytes
-rw-r--r--packages/instant/.dogfood.discharge.json4
-rw-r--r--packages/instant/.env_example7
-rw-r--r--packages/instant/.gitignore3
-rw-r--r--packages/instant/.npmignore3
-rw-r--r--packages/instant/.production.discharge.json13
-rw-r--r--packages/instant/.staging.discharge.json4
-rw-r--r--packages/instant/README.md52
-rw-r--r--packages/instant/package.json26
-rw-r--r--packages/instant/public/index.html1
-rw-r--r--packages/instant/src/assets/icons/zrx.svg7
-rw-r--r--packages/instant/src/components/buy_button.tsx20
-rw-r--r--packages/instant/src/components/buy_order_progress.tsx2
-rw-r--r--packages/instant/src/components/buy_order_state_buttons.tsx4
-rw-r--r--packages/instant/src/components/erc20_asset_amount_input.tsx8
-rw-r--r--packages/instant/src/components/erc20_token_selector.tsx10
-rw-r--r--packages/instant/src/components/install_wallet_panel_content.tsx9
-rw-r--r--packages/instant/src/components/instant_heading.tsx13
-rw-r--r--packages/instant/src/components/order_details.tsx2
-rw-r--r--packages/instant/src/components/payment_method.tsx23
-rw-r--r--packages/instant/src/components/payment_method_dropdown.tsx16
-rw-r--r--packages/instant/src/components/scaling_amount_input.tsx5
-rw-r--r--packages/instant/src/components/scaling_input.tsx6
-rw-r--r--packages/instant/src/components/search_input.tsx8
-rw-r--r--packages/instant/src/components/standard_panel_content.tsx13
-rw-r--r--packages/instant/src/components/standard_sliding_panel.tsx2
-rw-r--r--packages/instant/src/components/timed_progress_bar.tsx17
-rw-r--r--packages/instant/src/components/ui/container.tsx16
-rw-r--r--packages/instant/src/components/ui/dropdown.tsx8
-rw-r--r--packages/instant/src/components/ui/input.tsx1
-rw-r--r--packages/instant/src/components/ui/overlay.tsx2
-rw-r--r--packages/instant/src/components/ui/text.tsx6
-rw-r--r--packages/instant/src/components/wallet_prompt.tsx4
-rw-r--r--packages/instant/src/components/zero_ex_instant_container.tsx25
-rw-r--r--packages/instant/src/components/zero_ex_instant_overlay.tsx17
-rw-r--r--packages/instant/src/components/zero_ex_instant_provider.tsx11
-rw-r--r--packages/instant/src/constants.ts23
-rw-r--r--packages/instant/src/containers/connected_account_payment_method.ts41
-rw-r--r--packages/instant/src/containers/latest_error.tsx8
-rw-r--r--packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts8
-rw-r--r--packages/instant/src/containers/selected_erc20_asset_amount_input.ts4
-rw-r--r--packages/instant/src/data/asset_meta_data_map.ts6
-rw-r--r--packages/instant/src/index.umd.ts27
-rw-r--r--packages/instant/src/redux/analytics_middleware.ts52
-rw-r--r--packages/instant/src/redux/async_data.ts17
-rw-r--r--packages/instant/src/redux/reducer.ts1
-rw-r--r--packages/instant/src/style/theme.ts16
-rw-r--r--packages/instant/src/types.ts10
-rw-r--r--packages/instant/src/util/analytics.ts154
-rw-r--r--packages/instant/src/util/asset.ts17
-rw-r--r--packages/instant/src/util/buy_quote_updater.ts43
-rw-r--r--packages/instant/src/util/error_reporter.ts62
-rw-r--r--packages/instant/src/util/gas_price_estimator.ts5
-rw-r--r--packages/instant/src/util/heap.ts3
-rw-r--r--packages/instant/src/util/heartbeater_factory.ts12
-rw-r--r--packages/instant/test/util/asset.test.ts33
-rw-r--r--packages/instant/tsconfig.json8
-rw-r--r--packages/instant/webpack.config.js137
-rw-r--r--packages/metacoin/package.json16
-rw-r--r--packages/migrations/CHANGELOG.json3
-rw-r--r--packages/migrations/CHANGELOG.md6
-rw-r--r--packages/migrations/package.json20
-rw-r--r--packages/monorepo-scripts/CHANGELOG.json4
-rw-r--r--packages/monorepo-scripts/src/prepublish_checks.ts11
-rw-r--r--packages/monorepo-scripts/src/utils/github_release_utils.ts12
-rw-r--r--packages/order-utils/CHANGELOG.json9
-rw-r--r--packages/order-utils/CHANGELOG.md4
-rw-r--r--packages/order-utils/package.json12
-rw-r--r--packages/order-watcher/CHANGELOG.json9
-rw-r--r--packages/order-watcher/CHANGELOG.md4
-rw-r--r--packages/order-watcher/package.json22
-rw-r--r--packages/react-docs/CHANGELOG.json9
-rw-r--r--packages/react-docs/CHANGELOG.md4
-rw-r--r--packages/react-docs/package.json6
-rw-r--r--packages/react-shared/CHANGELOG.json9
-rw-r--r--packages/react-shared/CHANGELOG.md4
-rw-r--r--packages/react-shared/package.json4
-rw-r--r--packages/sol-compiler/CHANGELOG.json18
-rw-r--r--packages/sol-compiler/CHANGELOG.md4
-rw-r--r--packages/sol-compiler/package.json6
-rw-r--r--packages/sol-compiler/src/compiler.ts9
-rw-r--r--packages/sol-cov/CHANGELOG.json9
-rw-r--r--packages/sol-cov/CHANGELOG.md4
-rw-r--r--packages/sol-cov/package.json10
-rw-r--r--packages/sol-doc/CHANGELOG.json9
-rw-r--r--packages/sol-doc/CHANGELOG.md4
-rw-r--r--packages/sol-doc/package.json4
-rw-r--r--packages/sol-resolver/CHANGELOG.json9
-rw-r--r--packages/sol-resolver/src/resolvers/npm_resolver.ts15
-rw-r--r--packages/subproviders/CHANGELOG.json9
-rw-r--r--packages/subproviders/CHANGELOG.md4
-rw-r--r--packages/subproviders/package.json4
-rw-r--r--packages/testnet-faucets/package.json8
-rw-r--r--packages/types/CHANGELOG.json13
-rw-r--r--packages/types/src/index.ts8
-rw-r--r--packages/utils/CHANGELOG.json10
-rw-r--r--packages/utils/package.json5
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/data_type.ts58
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts19
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts40
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts54
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/types/set.ts218
-rw-r--r--packages/utils/src/abi_encoder/calldata/blocks/blob.ts20
-rw-r--r--packages/utils/src/abi_encoder/calldata/blocks/pointer.ts61
-rw-r--r--packages/utils/src/abi_encoder/calldata/blocks/set.ts47
-rw-r--r--packages/utils/src/abi_encoder/calldata/calldata.ts243
-rw-r--r--packages/utils/src/abi_encoder/calldata/calldata_block.ts77
-rw-r--r--packages/utils/src/abi_encoder/calldata/iterator.ts114
-rw-r--r--packages/utils/src/abi_encoder/calldata/raw_calldata.ts82
-rw-r--r--packages/utils/src/abi_encoder/evm_data_type_factory.ts132
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/address.ts49
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/array.ts64
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/bool.ts53
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts72
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/int.ts59
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/method.ts72
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/pointer.ts17
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts78
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/string.ts59
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/tuple.ts24
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/uint.ts58
-rw-r--r--packages/utils/src/abi_encoder/index.ts14
-rw-r--r--packages/utils/src/abi_encoder/utils/constants.ts17
-rw-r--r--packages/utils/src/abi_encoder/utils/math.ts111
-rw-r--r--packages/utils/src/abi_encoder/utils/queue.ts39
-rw-r--r--packages/utils/src/abi_encoder/utils/rules.ts8
-rw-r--r--packages/utils/src/index.ts1
-rw-r--r--packages/utils/test/abi_encoder/abi_samples/method_abis.ts780
-rw-r--r--packages/utils/test/abi_encoder/abi_samples/optimizer_abis.ts340
-rw-r--r--packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts99
-rw-r--r--packages/utils/test/abi_encoder/evm_data_types_test.ts1007
-rw-r--r--packages/utils/test/abi_encoder/methods_test.ts366
-rw-r--r--packages/utils/test/abi_encoder/optimizer_test.ts262
-rw-r--r--packages/utils/test/abi_encoder/return_values_test.ts67
-rw-r--r--packages/utils/test/utils/chai_setup.ts13
-rw-r--r--packages/web3-wrapper/CHANGELOG.json3
-rw-r--r--packages/web3-wrapper/CHANGELOG.md4
-rw-r--r--packages/web3-wrapper/package.json2
-rw-r--r--packages/website/md/docs/smart_contracts/1/introduction.md2
-rw-r--r--packages/website/md/docs/smart_contracts/2/introduction.md2
-rw-r--r--packages/website/package.json14
-rw-r--r--packages/website/public/images/instant/dai_screenshot.pngbin0 -> 105373 bytes
-rw-r--r--packages/website/public/images/instant/feature_1.svg33
-rw-r--r--packages/website/public/images/instant/feature_2.svg15
-rw-r--r--packages/website/public/images/instant/feature_3.svg195
-rw-r--r--packages/website/public/images/instant/gnt_screenshot.pngbin0 -> 106656 bytes
-rw-r--r--packages/website/public/images/instant/gods_screenshot.pngbin0 -> 585005 bytes
-rw-r--r--packages/website/public/images/instant/kitty_screenshot.pngbin0 -> 208899 bytes
-rw-r--r--packages/website/public/images/instant/nmr_screenshot.pngbin0 -> 104794 bytes
-rw-r--r--packages/website/public/images/instant/rep_screenshot.pngbin0 -> 106740 bytes
-rw-r--r--packages/website/ts/components/ui/image.tsx2
-rw-r--r--packages/website/ts/components/ui/text.tsx2
-rw-r--r--packages/website/ts/containers/instant.ts30
-rw-r--r--packages/website/ts/index.tsx2
-rw-r--r--packages/website/ts/pages/instant/configurator.tsx12
-rw-r--r--packages/website/ts/pages/instant/features.tsx146
-rw-r--r--packages/website/ts/pages/instant/instant.tsx87
-rw-r--r--packages/website/ts/pages/instant/introducing_0x_instant.tsx57
-rw-r--r--packages/website/ts/pages/instant/need_more.tsx62
-rw-r--r--packages/website/ts/pages/instant/screenshots.tsx35
-rw-r--r--packages/website/ts/style/colors.ts3
-rw-r--r--packages/website/ts/types.ts1
-rw-r--r--packages/website/tslint.json3
-rwxr-xr-xpython-packages/order_utils/setup.py3
-rw-r--r--python-packages/order_utils/src/zero_ex/json_schemas/__init__.py81
-rw-r--r--python-packages/order_utils/stubs/jsonschema/__init__.pyi10
-rw-r--r--python-packages/order_utils/test/test_doctest.py5
-rw-r--r--python-packages/order_utils/test/test_json_schemas.py23
-rw-r--r--tsconfig.json6
-rw-r--r--yarn.lock402
419 files changed, 11537 insertions, 1691 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 0ab512f58..1ea5aa280 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -40,7 +40,10 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- - run: yarn wsrun test:circleci contracts
+ - run: yarn wsrun test:circleci @0x/contracts-multisig
+ - run: yarn wsrun test:circleci @0x/contracts-utils
+ - run: yarn wsrun test:circleci @0x/contracts-libs
+ - run: yarn wsrun test:circleci @0x/contracts-core
test-contracts-geth:
docker:
- image: circleci/node:9
@@ -52,7 +55,10 @@ jobs:
- repo-{{ .Environment.CIRCLE_SHA1 }}
# HACK(albrow): we need to sleep 10 seconds to ensure the devnet is
# initialized
- - run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test contracts
+ - run: sleep 10 && TEST_PROVIDER=geth yarn wsrun test @0x/contracts-multisig
+ - run: TEST_PROVIDER=geth yarn wsrun test @0x/contracts-utils
+ - run: TEST_PROVIDER=geth yarn wsrun test @0x/contracts-libs
+ - run: TEST_PROVIDER=geth yarn wsrun test @0x/contracts-core
test-publish:
resource_class: medium+
docker:
@@ -81,6 +87,7 @@ jobs:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
+ - run: yarn wsrun test:circleci @0x/contracts-test-utils
- run: yarn wsrun test:circleci @0x/abi-gen
- run: yarn wsrun test:circleci @0x/assert
- run: yarn wsrun test:circleci @0x/base-contract
diff --git a/.gitignore b/.gitignore
index 612b6b28a..db53d8639 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,13 +82,19 @@ packages/react-docs/example/public/bundle*
packages/testnet-faucets/server/
# generated contract artifacts/
-packages/contracts/generated-artifacts/
+contracts/core/generated-artifacts/
+contracts/multisig/generated-artifacts/
+contracts/utils/generated-artifacts/
+contracts/libs/generated-artifacts/
packages/sol-cov/test/fixtures/artifacts/
packages/metacoin/artifacts/
# generated contract wrappers
packages/abi-gen-wrappers/wrappers
-packages/contracts/generated-wrappers/
+contracts/core/generated-wrappers/
+contracts/multisig/generated-wrappers/
+contracts/utils/generated-wrappers/
+contracts/libs/generated-wrappers/
packages/metacoin/src/contract_wrappers
# solc-bin in sol-compiler
diff --git a/.prettierignore b/.prettierignore
index db389bdb9..43c8015fd 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,7 +1,13 @@
lib
.nyc_output
-/packages/contracts/generated-wrappers
-/packages/contracts/generated-artifacts
+/contracts/core/generated-wrappers
+/contracts/core/generated-artifacts
+/contracts/multisig/generated-wrappers
+/contracts/multisig/generated-artifacts
+/contracts/utils/generated-wrappers
+/contracts/utils/generated-artifacts
+/contracts/libs/generated-wrappers
+/contracts/libs/generated-artifacts
/packages/abi-gen-wrappers/src/generated-wrappers
/packages/contract-artifacts/artifacts
/python-packages/order_utils/src/zero_ex/contract_artifacts/artifacts
diff --git a/CODEOWNERS b/CODEOWNERS
index 3cf75fb2d..ca98ec19b 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -14,7 +14,7 @@ packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff
packages/abi-gen/ @LogvinovLeon
packages/base-contract/ @LogvinovLeon
packages/connect/ @fragosti
-packages/contract_templates/ @LogvinovLeon
+packages/abi-gen-templates/ @LogvinovLeon
packages/contract-addresses/ @albrow
packages/contract-artifacts/ @albrow
packages/dev-utils/ @LogvinovLeon @fabioberger
@@ -32,4 +32,4 @@ packages/web3-wrapper/ @LogvinovLeon @fabioberger
python-packages/ @feuGeneA
# Protocol/smart contracts
-packages/contracts/test/ @albrow
+contracts/core/test/ @albrow
diff --git a/README.md b/README.md
index 421299371..5e99b4788 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,7 @@ Visit our [developer portal](https://0xproject.com/docs/order-utils) for a compr
| Package | Description |
| -------------------------------------------------- | ---------------------------------------------------------------- |
-| [`@0x/contracts`](/packages/contracts) | 0x protocol solidity smart contracts & tests |
+| [`@0x/contracts`](/contracts/core) | 0x protocol solidity smart contracts & tests |
| [`@0x/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
| [`@0x/website`](/packages/website) | 0x website |
diff --git a/contracts/TESTING.md b/contracts/TESTING.md
new file mode 100644
index 000000000..750b3c62c
--- /dev/null
+++ b/contracts/TESTING.md
@@ -0,0 +1,48 @@
+# Contracts testing options
+
+## Revert stack traces
+
+If you want to see helpful stack traces (incl. line number, code snippet) for smart contract reverts, run the tests with:
+
+```
+yarn test:trace
+```
+
+**Note:** This currently slows down the test runs and is therefore not enabled by default.
+
+## Backing Ethereum node
+
+By default, our tests run against an in-process [Ganache](https://github.com/trufflesuite/ganache-core) instance. In order to run the tests against [Geth](https://github.com/ethereum/go-ethereum), first follow the instructions in the README for the devnet package to start the devnet Geth node. Then run:
+
+```bash
+TEST_PROVIDER=geth yarn test
+```
+
+## Code coverage
+
+In order to see the Solidity code coverage output generated by `@0x/sol-cov`, run:
+
+```
+yarn test:coverage
+```
+
+## Gas profiler
+
+In order to profile the gas costs for a specific smart contract call/transaction, you can run the tests in `profiler` mode.
+
+**Note:** Traces emitted by ganache have incorrect gas costs so we recommend using Geth for profiling.
+
+```
+TEST_PROVIDER=geth yarn test:profiler
+```
+
+You'll see a warning that you need to explicitly enable and disable the profiler before and after the block of code you want to profile.
+
+```typescript
+import { profiler } from './utils/profiler';
+profiler.start();
+// Some call to a smart contract
+profiler.stop();
+```
+
+Without explicitly starting and stopping the profiler, the profiler output will be too busy, and therefore unusable.
diff --git a/packages/contracts/.solhint.json b/contracts/core/.solhint.json
index 076afe9f3..076afe9f3 100644
--- a/packages/contracts/.solhint.json
+++ b/contracts/core/.solhint.json
diff --git a/packages/contracts/.solhintignore b/contracts/core/.solhintignore
index 1e33ec53b..1e33ec53b 100644
--- a/packages/contracts/.solhintignore
+++ b/contracts/core/.solhintignore
diff --git a/packages/contracts/CHANGELOG.json b/contracts/core/CHANGELOG.json
index 00f94c83b..7dfa06990 100644
--- a/packages/contracts/CHANGELOG.json
+++ b/contracts/core/CHANGELOG.json
@@ -1,5 +1,15 @@
[
{
+ "name": "MultiAssetProxy",
+ "version": "1.0.0",
+ "changes": [
+ {
+ "note": "Add MultiAssetProxy implementation",
+ "pr": 1224
+ }
+ ]
+ },
+ {
"name": "OrderValidator",
"version": "1.0.1",
"changes": [
diff --git a/packages/contracts/README.md b/contracts/core/README.md
index 97a2816ff..0004925c1 100644
--- a/packages/contracts/README.md
+++ b/contracts/core/README.md
@@ -14,8 +14,6 @@ Contracts that make up and interact with version 2.0.0 of the protocol can be fo
* This directory contains example implementations of contracts that interact with the protocol but are _not_ intended for use in production. Examples include [filter](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#filter-contracts) contracts, a [Wallet](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#wallet) contract, and a [Validator](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#validator) contract, among others.
* [tokens](./contracts/tokens)
* This directory contains implementations of different tokens and token standards, including [wETH](https://weth.io/), ZRX, [ERC20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md), and [ERC721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md).
-* [multisig](./contracts/multisig)
- * This directory contains the [Gnosis MultiSigWallet](https://github.com/gnosis/MultiSigWallet) and a custom extension that adds a timelock to transactions within the MultiSigWallet.
* [utils](./contracts/utils)
* This directory contains libraries and utils that are shared across all of the other directories.
* [test](./contracts/test)
@@ -52,13 +50,13 @@ yarn install
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash
-PKG=contracts yarn build
+PKG=@0x/contracts-core yarn build
```
Or continuously rebuild on change:
```bash
-PKG=contracts yarn watch
+PKG=@0x/contracts-core yarn watch
```
### Clean
@@ -81,49 +79,4 @@ yarn test
#### Testing options
-###### Revert stack traces
-
-If you want to see helpful stack traces (incl. line number, code snippet) for smart contract reverts, run the tests with:
-
-```
-yarn test:trace
-```
-
-**Note:** This currently slows down the test runs and is therefore not enabled by default.
-
-###### Backing Ethereum node
-
-By default, our tests run against an in-process [Ganache](https://github.com/trufflesuite/ganache-core) instance. In order to run the tests against [Geth](https://github.com/ethereum/go-ethereum), first follow the instructions in the README for the devnet package to start the devnet Geth node. Then run:
-
-```bash
-TEST_PROVIDER=geth yarn test
-```
-
-###### Code coverage
-
-In order to see the Solidity code coverage output generated by `@0x/sol-cov`, run:
-
-```
-yarn test:coverage
-```
-
-###### Gas profiler
-
-In order to profile the gas costs for a specific smart contract call/transaction, you can run the tests in `profiler` mode.
-
-**Note:** Traces emitted by ganache have incorrect gas costs so we recommend using Geth for profiling.
-
-```
-TEST_PROVIDER=geth yarn test:profiler
-```
-
-You'll see a warning that you need to explicitly enable and disable the profiler before and after the block of code you want to profile.
-
-```typescript
-import { profiler } from './utils/profiler';
-profiler.start();
-// Some call to a smart contract
-profiler.stop();
-```
-
-Without explicitly starting and stopping the profiler, the profiler output will be too busy, and therefore unusable.
+Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
diff --git a/packages/contracts/compiler.json b/contracts/core/compiler.json
index af3980b4e..7e527130a 100644
--- a/packages/contracts/compiler.json
+++ b/contracts/core/compiler.json
@@ -25,6 +25,7 @@
"DummyERC721Token",
"DummyMultipleReturnERC20Token",
"DummyNoReturnERC20Token",
+ "DutchAuction",
"ERC20Proxy",
"ERC20Token",
"ERC721Token",
@@ -38,15 +39,11 @@
"IValidator",
"IWallet",
"MixinAuthorizable",
- "MultiSigWallet",
- "MultiSigWalletWithTimeLock",
+ "MultiAssetProxy",
"OrderValidator",
"ReentrantERC20Token",
"TestAssetProxyOwner",
"TestAssetProxyDispatcher",
- "TestConstants",
- "TestLibBytes",
- "TestLibs",
"TestExchangeInternals",
"TestSignatureValidator",
"TestStaticCallReceiver",
diff --git a/packages/contracts/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol b/contracts/core/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol
index 2fa0e3c5e..ca5a64a26 100644
--- a/packages/contracts/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol
+++ b/contracts/core/contracts/examples/ExchangeWrapper/ExchangeWrapper.sol
@@ -20,7 +20,7 @@ pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
import "../../protocol/Exchange/interfaces/IExchange.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
contract ExchangeWrapper {
diff --git a/packages/contracts/contracts/examples/Validator/Validator.sol b/contracts/core/contracts/examples/Validator/Validator.sol
index 72ed528ba..72ed528ba 100644
--- a/packages/contracts/contracts/examples/Validator/Validator.sol
+++ b/contracts/core/contracts/examples/Validator/Validator.sol
diff --git a/packages/contracts/contracts/examples/Wallet/Wallet.sol b/contracts/core/contracts/examples/Wallet/Wallet.sol
index b75021a31..3738be841 100644
--- a/packages/contracts/contracts/examples/Wallet/Wallet.sol
+++ b/contracts/core/contracts/examples/Wallet/Wallet.sol
@@ -19,7 +19,7 @@
pragma solidity 0.4.24;
import "../../protocol/Exchange/interfaces/IWallet.sol";
-import "../../utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
contract Wallet is
diff --git a/packages/contracts/contracts/examples/Whitelist/Whitelist.sol b/contracts/core/contracts/examples/Whitelist/Whitelist.sol
index e4e25038c..cfcddddd3 100644
--- a/packages/contracts/contracts/examples/Whitelist/Whitelist.sol
+++ b/contracts/core/contracts/examples/Whitelist/Whitelist.sol
@@ -20,8 +20,8 @@ pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
import "../../protocol/Exchange/interfaces/IExchange.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
-import "../../utils/Ownable/Ownable.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol";
contract Whitelist is
diff --git a/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol
new file mode 100644
index 000000000..a40991ae7
--- /dev/null
+++ b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol
@@ -0,0 +1,205 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../protocol/Exchange/interfaces/IExchange.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "../../tokens/ERC20Token/IERC20Token.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol";
+
+
+contract DutchAuction is
+ SafeMath
+{
+ using LibBytes for bytes;
+
+ // solhint-disable var-name-mixedcase
+ IExchange internal EXCHANGE;
+
+ struct AuctionDetails {
+ uint256 beginTimeSeconds; // Auction begin unix timestamp: sellOrder.makerAssetData
+ uint256 endTimeSeconds; // Auction end unix timestamp: sellOrder.expiryTimeSeconds
+ uint256 beginAmount; // Auction begin amount: sellOrder.makerAssetData
+ uint256 endAmount; // Auction end amount: sellOrder.takerAssetAmount
+ uint256 currentAmount; // Calculated amount given block.timestamp
+ uint256 currentTimeSeconds; // block.timestamp
+ }
+
+ constructor (address _exchange)
+ public
+ {
+ EXCHANGE = IExchange(_exchange);
+ }
+
+ /// @dev Matches the buy and sell orders at an amount given the following: the current block time, the auction
+ /// start time and the auction begin amount. The sell order is a an order at the lowest amount
+ /// at the end of the auction. Excess from the match is transferred to the seller.
+ /// Over time the price moves from beginAmount to endAmount given the current block.timestamp.
+ /// sellOrder.expiryTimeSeconds is the end time of the auction.
+ /// sellOrder.takerAssetAmount is the end amount of the auction (lowest possible amount).
+ /// sellOrder.makerAssetData is the ABI encoded Asset Proxy data with the following data appended
+ /// buyOrder.makerAssetData is the buyers bid on the auction, must meet the amount for the current block timestamp
+ /// (uint256 beginTimeSeconds, uint256 beginAmount).
+ /// This function reverts in the following scenarios:
+ /// * Auction has not started (auctionDetails.currentTimeSeconds < auctionDetails.beginTimeSeconds)
+ /// * Auction has expired (auctionDetails.endTimeSeconds < auctionDetails.currentTimeSeconds)
+ /// * Amount is invalid: Buy order amount is too low (buyOrder.makerAssetAmount < auctionDetails.currentAmount)
+ /// * Amount is invalid: Invalid begin amount (auctionDetails.beginAmount > auctionDetails.endAmount)
+ /// * Any failure in the 0x Match Orders
+ /// @param buyOrder The Buyer's order. This order is for the current expected price of the auction.
+ /// @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction).
+ /// @param buySignature Proof that order was created by the buyer.
+ /// @param sellSignature Proof that order was created by the seller.
+ /// @return matchedFillResults amounts filled and fees paid by maker and taker of matched orders.
+ function matchOrders(
+ LibOrder.Order memory buyOrder,
+ LibOrder.Order memory sellOrder,
+ bytes memory buySignature,
+ bytes memory sellSignature
+ )
+ public
+ returns (LibFillResults.MatchedFillResults memory matchedFillResults)
+ {
+ AuctionDetails memory auctionDetails = getAuctionDetails(sellOrder);
+ // Ensure the auction has not yet started
+ require(
+ auctionDetails.currentTimeSeconds >= auctionDetails.beginTimeSeconds,
+ "AUCTION_NOT_STARTED"
+ );
+ // Ensure the auction has not expired. This will fail later in 0x but we can save gas by failing early
+ require(
+ sellOrder.expirationTimeSeconds > auctionDetails.currentTimeSeconds,
+ "AUCTION_EXPIRED"
+ );
+ // Validate the buyer amount is greater than the current auction amount
+ require(
+ buyOrder.makerAssetAmount >= auctionDetails.currentAmount,
+ "INVALID_AMOUNT"
+ );
+ // Match orders, maximally filling `buyOrder`
+ matchedFillResults = EXCHANGE.matchOrders(
+ buyOrder,
+ sellOrder,
+ buySignature,
+ sellSignature
+ );
+ // The difference in sellOrder.takerAssetAmount and current amount is given as spread to the matcher
+ // This may include additional spread from the buyOrder.makerAssetAmount and the currentAmount.
+ // e.g currentAmount is 30, sellOrder.takerAssetAmount is 10 and buyOrder.makerAssetamount is 40.
+ // 10 (40-30) is returned to the buyer, 20 (30-10) sent to the seller and 10 has previously
+ // been transferred to the seller during matchOrders
+ uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount;
+ if (leftMakerAssetSpreadAmount > 0) {
+ // ERC20 Asset data itself is encoded as follows:
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Header | 0 | 4 | function selector |
+ // | Params | | 1 * 32 | function parameters: |
+ // | | 4 | 12 | 1. token address padding |
+ // | | 16 | 20 | 2. token address |
+ bytes memory assetData = sellOrder.takerAssetData;
+ address token = assetData.readAddress(16);
+ // Calculate the excess from the buy order. This can occur if the buyer sends in a higher
+ // amount than the calculated current amount
+ uint256 buyerExcessAmount = safeSub(buyOrder.makerAssetAmount, auctionDetails.currentAmount);
+ uint256 sellerExcessAmount = safeSub(leftMakerAssetSpreadAmount, buyerExcessAmount);
+ // Return the difference between auctionDetails.currentAmount and sellOrder.takerAssetAmount
+ // to the seller
+ if (sellerExcessAmount > 0) {
+ IERC20Token(token).transfer(sellOrder.makerAddress, sellerExcessAmount);
+ }
+ // Return the difference between buyOrder.makerAssetAmount and auctionDetails.currentAmount
+ // to the buyer
+ if (buyerExcessAmount > 0) {
+ IERC20Token(token).transfer(buyOrder.makerAddress, buyerExcessAmount);
+ }
+ }
+ return matchedFillResults;
+ }
+
+ /// @dev Calculates the Auction Details for the given order
+ /// @param order The sell order
+ /// @return AuctionDetails
+ function getAuctionDetails(
+ LibOrder.Order memory order
+ )
+ public
+ returns (AuctionDetails memory auctionDetails)
+ {
+ uint256 makerAssetDataLength = order.makerAssetData.length;
+ // It is unknown the encoded data of makerAssetData, we assume the last 64 bytes
+ // are the Auction Details encoding.
+ // Auction Details is encoded as follows:
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Params | | 2 * 32 | parameters: |
+ // | | -64 | 32 | 1. auction begin unix timestamp |
+ // | | -32 | 32 | 2. auction begin begin amount |
+ // ERC20 asset data length is 4+32, 64 for auction details results in min length 100
+ require(
+ makerAssetDataLength >= 100,
+ "INVALID_ASSET_DATA"
+ );
+ uint256 auctionBeginTimeSeconds = order.makerAssetData.readUint256(makerAssetDataLength - 64);
+ uint256 auctionBeginAmount = order.makerAssetData.readUint256(makerAssetDataLength - 32);
+ // Ensure the auction has a valid begin time
+ require(
+ order.expirationTimeSeconds > auctionBeginTimeSeconds,
+ "INVALID_BEGIN_TIME"
+ );
+ uint256 auctionDurationSeconds = order.expirationTimeSeconds-auctionBeginTimeSeconds;
+ // Ensure the auction goes from high to low
+ uint256 minAmount = order.takerAssetAmount;
+ require(
+ auctionBeginAmount > minAmount,
+ "INVALID_AMOUNT"
+ );
+ uint256 amountDelta = auctionBeginAmount-minAmount;
+ // solhint-disable-next-line not-rely-on-time
+ uint256 timestamp = block.timestamp;
+ auctionDetails.beginTimeSeconds = auctionBeginTimeSeconds;
+ auctionDetails.endTimeSeconds = order.expirationTimeSeconds;
+ auctionDetails.beginAmount = auctionBeginAmount;
+ auctionDetails.endAmount = minAmount;
+ auctionDetails.currentTimeSeconds = timestamp;
+
+ uint256 remainingDurationSeconds = order.expirationTimeSeconds-timestamp;
+ if (timestamp < auctionBeginTimeSeconds) {
+ // If the auction has not yet begun the current amount is the auctionBeginAmount
+ auctionDetails.currentAmount = auctionBeginAmount;
+ } else if (timestamp >= order.expirationTimeSeconds) {
+ // If the auction has ended the current amount is the minAmount.
+ // Auction end time is guaranteed by 0x Exchange due to the order expiration
+ auctionDetails.currentAmount = minAmount;
+ } else {
+ auctionDetails.currentAmount = safeAdd(
+ minAmount,
+ safeDiv(
+ safeMul(remainingDurationSeconds, amountDelta),
+ auctionDurationSeconds
+ )
+ );
+ }
+ return auctionDetails;
+ }
+}
diff --git a/packages/contracts/contracts/extensions/Forwarder/Forwarder.sol b/contracts/core/contracts/extensions/Forwarder/Forwarder.sol
index 94dec40ed..94dec40ed 100644
--- a/packages/contracts/contracts/extensions/Forwarder/Forwarder.sol
+++ b/contracts/core/contracts/extensions/Forwarder/Forwarder.sol
diff --git a/packages/contracts/contracts/extensions/Forwarder/MixinAssets.sol b/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol
index 43efb5ff3..5f5f3456d 100644
--- a/packages/contracts/contracts/extensions/Forwarder/MixinAssets.sol
+++ b/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol
@@ -18,8 +18,8 @@
pragma solidity 0.4.24;
-import "../../utils/LibBytes/LibBytes.sol";
-import "../../utils/Ownable/Ownable.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol";
import "../../tokens/ERC20Token/IERC20Token.sol";
import "../../tokens/ERC721Token/IERC721Token.sol";
import "./libs/LibConstants.sol";
diff --git a/packages/contracts/contracts/extensions/Forwarder/MixinExchangeWrapper.sol b/contracts/core/contracts/extensions/Forwarder/MixinExchangeWrapper.sol
index 4991c0ea5..210eb14c2 100644
--- a/packages/contracts/contracts/extensions/Forwarder/MixinExchangeWrapper.sol
+++ b/contracts/core/contracts/extensions/Forwarder/MixinExchangeWrapper.sol
@@ -21,10 +21,10 @@ pragma experimental ABIEncoderV2;
import "./libs/LibConstants.sol";
import "./mixins/MExchangeWrapper.sol";
-import "../../protocol/Exchange/libs/LibAbiEncoder.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
-import "../../protocol/Exchange/libs/LibFillResults.sol";
-import "../../protocol/Exchange/libs/LibMath.sol";
+import "@0x/contracts-libs/contracts/libs/LibAbiEncoder.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibMath.sol";
contract MixinExchangeWrapper is
diff --git a/packages/contracts/contracts/extensions/Forwarder/MixinForwarderCore.sol b/contracts/core/contracts/extensions/Forwarder/MixinForwarderCore.sol
index 54487f726..bab78d79b 100644
--- a/packages/contracts/contracts/extensions/Forwarder/MixinForwarderCore.sol
+++ b/contracts/core/contracts/extensions/Forwarder/MixinForwarderCore.sol
@@ -24,10 +24,10 @@ import "./mixins/MWeth.sol";
import "./mixins/MAssets.sol";
import "./mixins/MExchangeWrapper.sol";
import "./interfaces/IForwarderCore.sol";
-import "../../utils/LibBytes/LibBytes.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
-import "../../protocol/Exchange/libs/LibFillResults.sol";
-import "../../protocol/Exchange/libs/LibMath.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibMath.sol";
contract MixinForwarderCore is
diff --git a/packages/contracts/contracts/extensions/Forwarder/MixinWeth.sol b/contracts/core/contracts/extensions/Forwarder/MixinWeth.sol
index d2814a49b..2a281f3ae 100644
--- a/packages/contracts/contracts/extensions/Forwarder/MixinWeth.sol
+++ b/contracts/core/contracts/extensions/Forwarder/MixinWeth.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../protocol/Exchange/libs/LibMath.sol";
+import "@0x/contracts-libs/contracts/libs/LibMath.sol";
import "./libs/LibConstants.sol";
import "./mixins/MWeth.sol";
diff --git a/packages/contracts/contracts/extensions/Forwarder/interfaces/IAssets.sol b/contracts/core/contracts/extensions/Forwarder/interfaces/IAssets.sol
index 1e034c003..1e034c003 100644
--- a/packages/contracts/contracts/extensions/Forwarder/interfaces/IAssets.sol
+++ b/contracts/core/contracts/extensions/Forwarder/interfaces/IAssets.sol
diff --git a/packages/contracts/contracts/extensions/Forwarder/interfaces/IForwarder.sol b/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarder.sol
index f5a26e2ba..f5a26e2ba 100644
--- a/packages/contracts/contracts/extensions/Forwarder/interfaces/IForwarder.sol
+++ b/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarder.sol
diff --git a/packages/contracts/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol b/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol
index 74c7da01d..eede20bb8 100644
--- a/packages/contracts/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol
+++ b/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol
@@ -19,8 +19,8 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../../../protocol/Exchange/libs/LibOrder.sol";
-import "../../../protocol/Exchange/libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
contract IForwarderCore {
diff --git a/packages/contracts/contracts/extensions/Forwarder/libs/LibConstants.sol b/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol
index 704e42ce3..0f98ae595 100644
--- a/packages/contracts/contracts/extensions/Forwarder/libs/LibConstants.sol
+++ b/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../../utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
import "../../../protocol/Exchange/interfaces/IExchange.sol";
import "../../../tokens/EtherToken/IEtherToken.sol";
import "../../../tokens/ERC20Token/IERC20Token.sol";
diff --git a/packages/contracts/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol b/contracts/core/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol
index fb3ade1db..fb3ade1db 100644
--- a/packages/contracts/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol
+++ b/contracts/core/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol
diff --git a/packages/contracts/contracts/extensions/Forwarder/mixins/MAssets.sol b/contracts/core/contracts/extensions/Forwarder/mixins/MAssets.sol
index 9e7f80d97..9e7f80d97 100644
--- a/packages/contracts/contracts/extensions/Forwarder/mixins/MAssets.sol
+++ b/contracts/core/contracts/extensions/Forwarder/mixins/MAssets.sol
diff --git a/packages/contracts/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol b/contracts/core/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol
index 13c26b03a..d9e71786a 100644
--- a/packages/contracts/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol
+++ b/contracts/core/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol
@@ -19,8 +19,8 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../../../protocol/Exchange/libs/LibOrder.sol";
-import "../../../protocol/Exchange/libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
contract MExchangeWrapper {
diff --git a/packages/contracts/contracts/extensions/Forwarder/mixins/MWeth.sol b/contracts/core/contracts/extensions/Forwarder/mixins/MWeth.sol
index 88e77be4e..88e77be4e 100644
--- a/packages/contracts/contracts/extensions/Forwarder/mixins/MWeth.sol
+++ b/contracts/core/contracts/extensions/Forwarder/mixins/MWeth.sol
diff --git a/packages/contracts/contracts/extensions/OrderValidator/OrderValidator.sol b/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol
index 3385d35ef..9e9e63e9b 100644
--- a/packages/contracts/contracts/extensions/OrderValidator/OrderValidator.sol
+++ b/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol
@@ -20,10 +20,10 @@ pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
import "../../protocol/Exchange/interfaces/IExchange.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
import "../../tokens/ERC20Token/IERC20Token.sol";
import "../../tokens/ERC721Token/IERC721Token.sol";
-import "../../utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
contract OrderValidator {
diff --git a/packages/contracts/contracts/protocol/AssetProxy/ERC20Proxy.sol b/contracts/core/contracts/protocol/AssetProxy/ERC20Proxy.sol
index 258443bca..258443bca 100644
--- a/packages/contracts/contracts/protocol/AssetProxy/ERC20Proxy.sol
+++ b/contracts/core/contracts/protocol/AssetProxy/ERC20Proxy.sol
diff --git a/packages/contracts/contracts/protocol/AssetProxy/ERC721Proxy.sol b/contracts/core/contracts/protocol/AssetProxy/ERC721Proxy.sol
index 65b664b8b..65b664b8b 100644
--- a/packages/contracts/contracts/protocol/AssetProxy/ERC721Proxy.sol
+++ b/contracts/core/contracts/protocol/AssetProxy/ERC721Proxy.sol
diff --git a/packages/contracts/contracts/protocol/AssetProxy/MixinAuthorizable.sol b/contracts/core/contracts/protocol/AssetProxy/MixinAuthorizable.sol
index fe9bbf848..08f9b94dc 100644
--- a/packages/contracts/contracts/protocol/AssetProxy/MixinAuthorizable.sol
+++ b/contracts/core/contracts/protocol/AssetProxy/MixinAuthorizable.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../utils/Ownable/Ownable.sol";
+import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol";
import "./mixins/MAuthorizable.sol";
diff --git a/contracts/core/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/contracts/core/contracts/protocol/AssetProxy/MultiAssetProxy.sol
new file mode 100644
index 000000000..42231e73b
--- /dev/null
+++ b/contracts/core/contracts/protocol/AssetProxy/MultiAssetProxy.sol
@@ -0,0 +1,300 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+import "../Exchange/MixinAssetProxyDispatcher.sol";
+import "./MixinAuthorizable.sol";
+
+
+contract MultiAssetProxy is
+ MixinAssetProxyDispatcher,
+ MixinAuthorizable
+{
+ // Id of this proxy.
+ bytes4 constant internal PROXY_ID = bytes4(keccak256("MultiAsset(uint256[],bytes[])"));
+
+ // solhint-disable-next-line payable-fallback
+ function ()
+ external
+ {
+ assembly {
+ // The first 4 bytes of calldata holds the function selector
+ let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
+
+ // `transferFrom` will be called with the following parameters:
+ // assetData Encoded byte array.
+ // from Address to transfer asset from.
+ // to Address to transfer asset to.
+ // amount Amount of asset to transfer.
+ // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
+ if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
+
+ // To lookup a value in a mapping, we load from the storage location keccak256(k, p),
+ // where k is the key left padded to 32 bytes and p is the storage slot
+ mstore(0, caller)
+ mstore(32, authorized_slot)
+
+ // Revert if authorized[msg.sender] == false
+ if iszero(sload(keccak256(0, 64))) {
+ // Revert with `Error("SENDER_NOT_AUTHORIZED")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+
+ // `transferFrom`.
+ // The function is marked `external`, so no abi decoding is done for
+ // us. Instead, we expect the `calldata` memory to contain the
+ // following:
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Header | 0 | 4 | function selector |
+ // | Params | | 4 * 32 | function parameters: |
+ // | | 4 | | 1. offset to assetData (*) |
+ // | | 36 | | 2. from |
+ // | | 68 | | 3. to |
+ // | | 100 | | 4. amount |
+ // | Data | | | assetData: |
+ // | | 132 | 32 | assetData Length |
+ // | | 164 | ** | assetData Contents |
+ //
+ // (*): offset is computed from start of function parameters, so offset
+ // by an additional 4 bytes in the calldata.
+ //
+ // (**): see table below to compute length of assetData Contents
+ //
+ // WARNING: The ABIv2 specification allows additional padding between
+ // the Params and Data section. This will result in a larger
+ // offset to assetData.
+
+ // Load offset to `assetData`
+ let assetDataOffset := calldataload(4)
+
+ // Asset data itself is encoded as follows:
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|-------------|---------|-------------------------------------|
+ // | Header | 0 | 4 | assetProxyId |
+ // | Params | | 2 * 32 | function parameters: |
+ // | | 4 | | 1. offset to amounts (*) |
+ // | | 36 | | 2. offset to nestedAssetData (*) |
+ // | Data | | | amounts: |
+ // | | 68 | 32 | amounts Length |
+ // | | 100 | a | amounts Contents |
+ // | | | | nestedAssetData: |
+ // | | 100 + a | 32 | nestedAssetData Length |
+ // | | 132 + a | b | nestedAssetData Contents (offsets) |
+ // | | 132 + a + b | | nestedAssetData[0, ..., len] |
+
+ // In order to find the offset to `amounts`, we must add:
+ // 4 (function selector)
+ // + assetDataOffset
+ // + 32 (assetData len)
+ // + 4 (assetProxyId)
+ let amountsOffset := calldataload(add(assetDataOffset, 40))
+
+ // In order to find the offset to `nestedAssetData`, we must add:
+ // 4 (function selector)
+ // + assetDataOffset
+ // + 32 (assetData len)
+ // + 4 (assetProxyId)
+ // + 32 (amounts offset)
+ let nestedAssetDataOffset := calldataload(add(assetDataOffset, 72))
+
+ // In order to find the start of the `amounts` contents, we must add:
+ // 4 (function selector)
+ // + assetDataOffset
+ // + 32 (assetData len)
+ // + 4 (assetProxyId)
+ // + amountsOffset
+ // + 32 (amounts len)
+ let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 72))
+
+ // Load number of elements in `amounts`
+ let amountsLen := calldataload(sub(amountsContentsStart, 32))
+
+ // In order to find the start of the `nestedAssetData` contents, we must add:
+ // 4 (function selector)
+ // + assetDataOffset
+ // + 32 (assetData len)
+ // + 4 (assetProxyId)
+ // + nestedAssetDataOffset
+ // + 32 (nestedAssetData len)
+ let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 72))
+
+ // Load number of elements in `nestedAssetData`
+ let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32))
+
+ // Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData`
+ if iszero(eq(amountsLen, nestedAssetDataLen)) {
+ // Revert with `Error("LENGTH_MISMATCH")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000000f4c454e4754485f4d49534d4154434800000000000000000000000000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+
+ // Copy `transferFrom` selector, offset to `assetData`, `from`, and `to` from calldata to memory
+ calldatacopy(
+ 0, // memory can safely be overwritten from beginning
+ 0, // start of calldata
+ 100 // length of selector (4) and 3 params (32 * 3)
+ )
+
+ // Overwrite existing offset to `assetData` with our own
+ mstore(4, 128)
+
+ // Load `amount`
+ let amount := calldataload(100)
+
+ // Calculate number of bytes in `amounts` contents
+ let amountsByteLen := mul(amountsLen, 32)
+
+ // Initialize `assetProxyId` and `assetProxy` to 0
+ let assetProxyId := 0
+ let assetProxy := 0
+
+ // Loop through `amounts` and `nestedAssetData`, calling `transferFrom` for each respective element
+ for {let i := 0} lt(i, amountsByteLen) {i := add(i, 32)} {
+
+ // Calculate the total amount
+ let amountsElement := calldataload(add(amountsContentsStart, i))
+ let totalAmount := mul(amountsElement, amount)
+
+ // Revert if multiplication resulted in an overflow
+ if iszero(eq(div(totalAmount, amount), amountsElement)) {
+ // Revert with `Error("UINT256_OVERFLOW")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+
+ // Write `totalAmount` to memory
+ mstore(100, totalAmount)
+
+ // Load offset to `nestedAssetData[i]`
+ let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i))
+
+ // In order to find the start of the `nestedAssetData[i]` contents, we must add:
+ // 4 (function selector)
+ // + assetDataOffset
+ // + 32 (assetData len)
+ // + 4 (assetProxyId)
+ // + nestedAssetDataOffset
+ // + 32 (nestedAssetData len)
+ // + nestedAssetDataElementOffset
+ // + 32 (nestedAssetDataElement len)
+ let nestedAssetDataElementContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, add(nestedAssetDataElementOffset, 104)))
+
+ // Load length of `nestedAssetData[i]`
+ let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32)
+ let nestedAssetDataElementLen := calldataload(nestedAssetDataElementLenStart)
+
+ // Revert if the `nestedAssetData` does not contain a 4 byte `assetProxyId`
+ if lt(nestedAssetDataElementLen, 4) {
+ // Revert with `Error("LENGTH_GREATER_THAN_3_REQUIRED")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000001e4c454e4754485f475245415445525f5448414e5f335f524551554952)
+ mstore(96, 0x4544000000000000000000000000000000000000000000000000000000000000)
+ revert(0, 100)
+ }
+
+ // Load AssetProxy id
+ let currentAssetProxyId := and(
+ calldataload(nestedAssetDataElementContentsStart),
+ 0xffffffff00000000000000000000000000000000000000000000000000000000
+ )
+
+ // Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId`
+ // We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0
+ if iszero(eq(currentAssetProxyId, assetProxyId)) {
+ // Update `assetProxyId`
+ assetProxyId := currentAssetProxyId
+ // To lookup a value in a mapping, we load from the storage location keccak256(k, p),
+ // where k is the key left padded to 32 bytes and p is the storage slot
+ mstore(132, assetProxyId)
+ mstore(164, assetProxies_slot)
+ assetProxy := sload(keccak256(132, 64))
+ }
+
+ // Revert if AssetProxy with given id does not exist
+ if iszero(assetProxy) {
+ // Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000001a41535345545f50524f58595f444f45535f4e4f545f45584953540000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+
+ // Copy `nestedAssetData[i]` from calldata to memory
+ calldatacopy(
+ 132, // memory slot after `amounts[i]`
+ nestedAssetDataElementLenStart, // location of `nestedAssetData[i]` in calldata
+ add(nestedAssetDataElementLen, 32) // `nestedAssetData[i].length` plus 32 byte length
+ )
+
+ // call `assetProxy.transferFrom`
+ let success := call(
+ gas, // forward all gas
+ assetProxy, // call address of asset proxy
+ 0, // don't send any ETH
+ 0, // pointer to start of input
+ add(164, nestedAssetDataElementLen), // length of input
+ 0, // write output over memory that won't be reused
+ 0 // don't copy output to memory
+ )
+
+ // Revert with reason given by AssetProxy if `transferFrom` call failed
+ if iszero(success) {
+ returndatacopy(
+ 0, // copy to memory at 0
+ 0, // copy from return data at 0
+ returndatasize() // copy all return data
+ )
+ revert(0, returndatasize())
+ }
+ }
+
+ // Return if no `transferFrom` calls reverted
+ return(0, 0)
+ }
+
+ // Revert if undefined function is called
+ revert(0, 0)
+ }
+ }
+
+ /// @dev Gets the proxy id associated with the proxy address.
+ /// @return Proxy id.
+ function getProxyId()
+ external
+ pure
+ returns (bytes4)
+ {
+ return PROXY_ID;
+ }
+}
diff --git a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetData.sol
index 3e76e38dd..e2da68919 100644
--- a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol
+++ b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetData.sol
@@ -18,6 +18,7 @@
// solhint-disable
pragma solidity 0.4.24;
+pragma experimental ABIEncoderV2;
// @dev Interface of the asset proxy's assetData.
@@ -26,15 +27,18 @@ pragma solidity 0.4.24;
interface IAssetData {
function ERC20Token(address tokenContract)
- external
- pure;
+ external;
function ERC721Token(
address tokenContract,
- uint256 tokenId,
- bytes receiverData
+ uint256 tokenId
)
- external
- pure;
+ external;
+
+ function MultiAsset(
+ uint256[] amounts,
+ bytes[] nestedAssetData
+ )
+ external;
}
diff --git a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol
index b25d2d75a..b25d2d75a 100644
--- a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol
+++ b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAssetProxy.sol
diff --git a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol
index ba1d4aa77..96ee05dee 100644
--- a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol
+++ b/contracts/core/contracts/protocol/AssetProxy/interfaces/IAuthorizable.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../../utils/Ownable/IOwnable.sol";
+import "@0x/contracts-utils/contracts/utils/Ownable/IOwnable.sol";
contract IAuthorizable is
diff --git a/packages/contracts/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol b/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol
index d63fb7f6d..d63fb7f6d 100644
--- a/packages/contracts/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol
+++ b/contracts/core/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol
diff --git a/packages/contracts/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol b/contracts/core/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol
index edb788fab..bfc7b5a66 100644
--- a/packages/contracts/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol
+++ b/contracts/core/contracts/protocol/AssetProxyOwner/AssetProxyOwner.sol
@@ -18,8 +18,8 @@
pragma solidity 0.4.24;
-import "../../multisig/MultiSigWalletWithTimeLock.sol";
-import "../../utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-multisig/contracts/multisig/MultiSigWalletWithTimeLock.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
contract AssetProxyOwner is
diff --git a/packages/contracts/contracts/protocol/Exchange/Exchange.sol b/contracts/core/contracts/protocol/Exchange/Exchange.sol
index ead36009f..65ca742ea 100644
--- a/packages/contracts/contracts/protocol/Exchange/Exchange.sol
+++ b/contracts/core/contracts/protocol/Exchange/Exchange.sol
@@ -19,7 +19,7 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "./libs/LibConstants.sol";
+import "@0x/contracts-libs/contracts/libs/LibConstants.sol";
import "./MixinExchangeCore.sol";
import "./MixinSignatureValidator.sol";
import "./MixinWrapperFunctions.sol";
diff --git a/packages/contracts/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol b/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol
index 87b09b6b3..02aeb4a13 100644
--- a/packages/contracts/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol
+++ b/contracts/core/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../utils/Ownable/Ownable.sol";
+import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol";
import "./mixins/MAssetProxyDispatcher.sol";
import "../AssetProxy/interfaces/IAssetProxy.sol";
diff --git a/packages/contracts/contracts/protocol/Exchange/MixinExchangeCore.sol b/contracts/core/contracts/protocol/Exchange/MixinExchangeCore.sol
index 736dcd0b1..68d6a3897 100644
--- a/packages/contracts/contracts/protocol/Exchange/MixinExchangeCore.sol
+++ b/contracts/core/contracts/protocol/Exchange/MixinExchangeCore.sol
@@ -19,11 +19,11 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
-import "./libs/LibConstants.sol";
-import "./libs/LibFillResults.sol";
-import "./libs/LibOrder.sol";
-import "./libs/LibMath.sol";
+import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol";
+import "@0x/contracts-libs/contracts/libs/LibConstants.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibMath.sol";
import "./mixins/MExchangeCore.sol";
import "./mixins/MSignatureValidator.sol";
import "./mixins/MTransactions.sol";
diff --git a/packages/contracts/contracts/protocol/Exchange/MixinMatchOrders.sol b/contracts/core/contracts/protocol/Exchange/MixinMatchOrders.sol
index b4f6bdb26..fc6d73482 100644
--- a/packages/contracts/contracts/protocol/Exchange/MixinMatchOrders.sol
+++ b/contracts/core/contracts/protocol/Exchange/MixinMatchOrders.sol
@@ -14,11 +14,11 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
-import "./libs/LibConstants.sol";
-import "./libs/LibMath.sol";
-import "./libs/LibOrder.sol";
-import "./libs/LibFillResults.sol";
+import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol";
+import "@0x/contracts-libs/contracts/libs/LibConstants.sol";
+import "@0x/contracts-libs/contracts/libs/LibMath.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
import "./mixins/MExchangeCore.sol";
import "./mixins/MMatchOrders.sol";
import "./mixins/MTransactions.sol";
diff --git a/packages/contracts/contracts/protocol/Exchange/MixinSignatureValidator.sol b/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol
index 176e28351..711535aa8 100644
--- a/packages/contracts/contracts/protocol/Exchange/MixinSignatureValidator.sol
+++ b/contracts/core/contracts/protocol/Exchange/MixinSignatureValidator.sol
@@ -18,8 +18,8 @@
pragma solidity 0.4.24;
-import "../../utils/LibBytes/LibBytes.sol";
-import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol";
import "./mixins/MSignatureValidator.sol";
import "./mixins/MTransactions.sol";
import "./interfaces/IWallet.sol";
@@ -239,18 +239,18 @@ contract MixinSignatureValidator is
view
returns (bool isValid)
{
- bytes memory calldata = abi.encodeWithSelector(
+ bytes memory callData = abi.encodeWithSelector(
IWallet(walletAddress).isValidSignature.selector,
hash,
signature
);
assembly {
- let cdStart := add(calldata, 32)
+ let cdStart := add(callData, 32)
let success := staticcall(
gas, // forward all gas
walletAddress, // address of Wallet contract
cdStart, // pointer to start of input
- mload(calldata), // length of input
+ mload(callData), // length of input
cdStart, // write output over input
32 // output size is 32 bytes
)
@@ -288,19 +288,19 @@ contract MixinSignatureValidator is
view
returns (bool isValid)
{
- bytes memory calldata = abi.encodeWithSelector(
+ bytes memory callData = abi.encodeWithSelector(
IValidator(signerAddress).isValidSignature.selector,
hash,
signerAddress,
signature
);
assembly {
- let cdStart := add(calldata, 32)
+ let cdStart := add(callData, 32)
let success := staticcall(
gas, // forward all gas
validatorAddress, // address of Validator contract
cdStart, // pointer to start of input
- mload(calldata), // length of input
+ mload(callData), // length of input
cdStart, // write output over input
32 // output size is 32 bytes
)
diff --git a/packages/contracts/contracts/protocol/Exchange/MixinTransactions.sol b/contracts/core/contracts/protocol/Exchange/MixinTransactions.sol
index 3a76ca202..87c614382 100644
--- a/packages/contracts/contracts/protocol/Exchange/MixinTransactions.sol
+++ b/contracts/core/contracts/protocol/Exchange/MixinTransactions.sol
@@ -17,10 +17,10 @@
*/
pragma solidity 0.4.24;
-import "./libs/LibExchangeErrors.sol";
+import "@0x/contracts-libs/contracts/libs/LibExchangeErrors.sol";
import "./mixins/MSignatureValidator.sol";
import "./mixins/MTransactions.sol";
-import "./libs/LibEIP712.sol";
+import "@0x/contracts-libs/contracts/libs/LibEIP712.sol";
contract MixinTransactions is
diff --git a/packages/contracts/contracts/protocol/Exchange/MixinWrapperFunctions.sol b/contracts/core/contracts/protocol/Exchange/MixinWrapperFunctions.sol
index cddff0e5f..2d43432ff 100644
--- a/packages/contracts/contracts/protocol/Exchange/MixinWrapperFunctions.sol
+++ b/contracts/core/contracts/protocol/Exchange/MixinWrapperFunctions.sol
@@ -19,11 +19,11 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../../utils/ReentrancyGuard/ReentrancyGuard.sol";
-import "./libs/LibMath.sol";
-import "./libs/LibOrder.sol";
-import "./libs/LibFillResults.sol";
-import "./libs/LibAbiEncoder.sol";
+import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol";
+import "@0x/contracts-libs/contracts/libs/LibMath.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibAbiEncoder.sol";
import "./mixins/MExchangeCore.sol";
import "./mixins/MWrapperFunctions.sol";
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
index 8db8d6f6c..8db8d6f6c 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/IExchange.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IExchange.sol
index b92abba04..b92abba04 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/IExchange.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/IExchange.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/IExchangeCore.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IExchangeCore.sol
index 9995e0385..0da73529c 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/IExchangeCore.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/IExchangeCore.sol
@@ -19,8 +19,8 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../libs/LibOrder.sol";
-import "../libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
contract IExchangeCore {
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/IMatchOrders.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IMatchOrders.sol
index 73447f3ae..b88e158c3 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/IMatchOrders.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/IMatchOrders.sol
@@ -18,8 +18,8 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../libs/LibOrder.sol";
-import "../libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
contract IMatchOrders {
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol b/contracts/core/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol
index 1fd0eccf0..1fd0eccf0 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/ISignatureValidator.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/ITransactions.sol b/contracts/core/contracts/protocol/Exchange/interfaces/ITransactions.sol
index 4446c55ce..4446c55ce 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/ITransactions.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/ITransactions.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/IValidator.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IValidator.sol
index 2dd69100c..2dd69100c 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/IValidator.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/IValidator.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/IWallet.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IWallet.sol
index c97161ca6..c97161ca6 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/IWallet.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/IWallet.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol b/contracts/core/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol
index 56a533646..833bb7e88 100644
--- a/packages/contracts/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol
+++ b/contracts/core/contracts/protocol/Exchange/interfaces/IWrapperFunctions.sol
@@ -19,8 +19,8 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../libs/LibOrder.sol";
-import "../libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
contract IWrapperFunctions {
diff --git a/packages/contracts/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
index 0ddfca270..0ddfca270 100644
--- a/packages/contracts/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
+++ b/contracts/core/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/mixins/MExchangeCore.sol b/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol
index 742499568..099bdcc33 100644
--- a/packages/contracts/contracts/protocol/Exchange/mixins/MExchangeCore.sol
+++ b/contracts/core/contracts/protocol/Exchange/mixins/MExchangeCore.sol
@@ -19,8 +19,8 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../libs/LibOrder.sol";
-import "../libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
import "../interfaces/IExchangeCore.sol";
diff --git a/packages/contracts/contracts/protocol/Exchange/mixins/MMatchOrders.sol b/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol
index 96fa34bc0..bb285de03 100644
--- a/packages/contracts/contracts/protocol/Exchange/mixins/MMatchOrders.sol
+++ b/contracts/core/contracts/protocol/Exchange/mixins/MMatchOrders.sol
@@ -18,8 +18,8 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../libs/LibOrder.sol";
-import "../libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
import "../interfaces/IMatchOrders.sol";
diff --git a/packages/contracts/contracts/protocol/Exchange/mixins/MSignatureValidator.sol b/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol
index 1fe88b908..1fe88b908 100644
--- a/packages/contracts/contracts/protocol/Exchange/mixins/MSignatureValidator.sol
+++ b/contracts/core/contracts/protocol/Exchange/mixins/MSignatureValidator.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/mixins/MTransactions.sol b/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol
index 4f61a4945..4f61a4945 100644
--- a/packages/contracts/contracts/protocol/Exchange/mixins/MTransactions.sol
+++ b/contracts/core/contracts/protocol/Exchange/mixins/MTransactions.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol b/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol
index 4adfbde01..2d21bf057 100644
--- a/packages/contracts/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol
+++ b/contracts/core/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol
@@ -19,8 +19,8 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../libs/LibOrder.sol";
-import "../libs/LibFillResults.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
import "../interfaces/IWrapperFunctions.sol";
diff --git a/packages/contracts/contracts/test/DummyERC20Token/DummyERC20Token.sol b/contracts/core/contracts/test/DummyERC20Token/DummyERC20Token.sol
index 412c5d1ad..33028db0c 100644
--- a/packages/contracts/contracts/test/DummyERC20Token/DummyERC20Token.sol
+++ b/contracts/core/contracts/test/DummyERC20Token/DummyERC20Token.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../utils/Ownable/Ownable.sol";
+import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol";
import "../../tokens/ERC20Token/MintableERC20Token.sol";
diff --git a/packages/contracts/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol b/contracts/core/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol
index 733d4437e..733d4437e 100644
--- a/packages/contracts/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol
+++ b/contracts/core/contracts/test/DummyERC20Token/DummyMultipleReturnERC20Token.sol
diff --git a/packages/contracts/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol b/contracts/core/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol
index e16825a16..e16825a16 100644
--- a/packages/contracts/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol
+++ b/contracts/core/contracts/test/DummyERC20Token/DummyNoReturnERC20Token.sol
diff --git a/packages/contracts/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol b/contracts/core/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol
index 6c8371559..6c8371559 100644
--- a/packages/contracts/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol
+++ b/contracts/core/contracts/test/DummyERC721Receiver/DummyERC721Receiver.sol
diff --git a/packages/contracts/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol b/contracts/core/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol
index 309633bf5..309633bf5 100644
--- a/packages/contracts/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol
+++ b/contracts/core/contracts/test/DummyERC721Receiver/InvalidERC721Receiver.sol
diff --git a/packages/contracts/contracts/test/DummyERC721Token/DummyERC721Token.sol b/contracts/core/contracts/test/DummyERC721Token/DummyERC721Token.sol
index ac9068d1d..4c978b2df 100644
--- a/packages/contracts/contracts/test/DummyERC721Token/DummyERC721Token.sol
+++ b/contracts/core/contracts/test/DummyERC721Token/DummyERC721Token.sol
@@ -19,7 +19,7 @@
pragma solidity 0.4.24;
import "../../tokens/ERC721Token/MintableERC721Token.sol";
-import "../../utils/Ownable/Ownable.sol";
+import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol";
// solhint-disable no-empty-blocks
diff --git a/packages/contracts/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol b/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol
index 99dd47a78..8e077e3e8 100644
--- a/packages/contracts/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol
+++ b/contracts/core/contracts/test/ReentrantERC20Token/ReentrantERC20Token.sol
@@ -19,10 +19,10 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../../utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
import "../../tokens/ERC20Token/ERC20Token.sol";
import "../../protocol/Exchange/interfaces/IExchange.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
+import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
// solhint-disable no-unused-vars
@@ -92,53 +92,53 @@ contract ReentrantERC20Token is
LibOrder.Order[] memory orders;
uint256[] memory takerAssetFillAmounts;
bytes[] memory signatures;
- bytes memory calldata;
+ bytes memory callData;
- // Create calldata for function that corresponds to currentFunctionId
+ // Create callData for function that corresponds to currentFunctionId
if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.fillOrder.selector,
order,
0,
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.fillOrKillOrder.selector,
order,
0,
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.batchFillOrders.selector,
orders,
takerAssetFillAmounts,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.batchFillOrKillOrders.selector,
orders,
takerAssetFillAmounts,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.marketBuyOrders.selector,
orders,
0,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.marketSellOrders.selector,
orders,
0,
signatures
);
} else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.matchOrders.selector,
order,
order,
@@ -146,22 +146,22 @@ contract ReentrantERC20Token is
signature
);
} else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDER)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.cancelOrder.selector,
order
);
} else if (currentFunctionId == uint8(ExchangeFunction.BATCH_CANCEL_ORDERS)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.batchCancelOrders.selector,
orders
);
} else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.cancelOrdersUpTo.selector,
0
);
} else if (currentFunctionId == uint8(ExchangeFunction.SET_SIGNATURE_VALIDATOR_APPROVAL)) {
- calldata = abi.encodeWithSelector(
+ callData = abi.encodeWithSelector(
EXCHANGE.setSignatureValidatorApproval.selector,
address(0),
false
@@ -169,7 +169,7 @@ contract ReentrantERC20Token is
}
// Call Exchange function, swallow error
- address(EXCHANGE).call(calldata);
+ address(EXCHANGE).call(callData);
// Revert reason is 100 bytes
bytes memory returnData = new bytes(100);
diff --git a/packages/contracts/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol b/contracts/core/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
index ad71fc9a1..ad71fc9a1 100644
--- a/packages/contracts/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
+++ b/contracts/core/contracts/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
diff --git a/packages/contracts/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol b/contracts/core/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol
index 52c66cb56..52c66cb56 100644
--- a/packages/contracts/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol
+++ b/contracts/core/contracts/test/TestAssetProxyOwner/TestAssetProxyOwner.sol
diff --git a/packages/contracts/contracts/test/TestExchangeInternals/TestExchangeInternals.sol b/contracts/core/contracts/test/TestExchangeInternals/TestExchangeInternals.sol
index 27187f8f8..27187f8f8 100644
--- a/packages/contracts/contracts/test/TestExchangeInternals/TestExchangeInternals.sol
+++ b/contracts/core/contracts/test/TestExchangeInternals/TestExchangeInternals.sol
diff --git a/packages/contracts/contracts/test/TestSignatureValidator/TestSignatureValidator.sol b/contracts/core/contracts/test/TestSignatureValidator/TestSignatureValidator.sol
index ea3e2de59..ea3e2de59 100644
--- a/packages/contracts/contracts/test/TestSignatureValidator/TestSignatureValidator.sol
+++ b/contracts/core/contracts/test/TestSignatureValidator/TestSignatureValidator.sol
diff --git a/packages/contracts/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol b/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol
index 41aab01c8..41aab01c8 100644
--- a/packages/contracts/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol
+++ b/contracts/core/contracts/test/TestStaticCallReceiver/TestStaticCallReceiver.sol
diff --git a/packages/contracts/contracts/tokens/ERC20Token/ERC20Token.sol b/contracts/core/contracts/tokens/ERC20Token/ERC20Token.sol
index 725d304df..725d304df 100644
--- a/packages/contracts/contracts/tokens/ERC20Token/ERC20Token.sol
+++ b/contracts/core/contracts/tokens/ERC20Token/ERC20Token.sol
diff --git a/packages/contracts/contracts/tokens/ERC20Token/IERC20Token.sol b/contracts/core/contracts/tokens/ERC20Token/IERC20Token.sol
index 258d47393..258d47393 100644
--- a/packages/contracts/contracts/tokens/ERC20Token/IERC20Token.sol
+++ b/contracts/core/contracts/tokens/ERC20Token/IERC20Token.sol
diff --git a/packages/contracts/contracts/tokens/ERC20Token/MintableERC20Token.sol b/contracts/core/contracts/tokens/ERC20Token/MintableERC20Token.sol
index 9dc924422..58bccb5a1 100644
--- a/packages/contracts/contracts/tokens/ERC20Token/MintableERC20Token.sol
+++ b/contracts/core/contracts/tokens/ERC20Token/MintableERC20Token.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../utils/SafeMath/SafeMath.sol";
+import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol";
import "./UnlimitedAllowanceERC20Token.sol";
diff --git a/packages/contracts/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol b/contracts/core/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol
index 2e5bd4348..2e5bd4348 100644
--- a/packages/contracts/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol
+++ b/contracts/core/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol
diff --git a/packages/contracts/contracts/tokens/ERC721Token/ERC721Token.sol b/contracts/core/contracts/tokens/ERC721Token/ERC721Token.sol
index 530f080c0..600cee1ab 100644
--- a/packages/contracts/contracts/tokens/ERC721Token/ERC721Token.sol
+++ b/contracts/core/contracts/tokens/ERC721Token/ERC721Token.sol
@@ -20,7 +20,7 @@ pragma solidity 0.4.24;
import "./IERC721Token.sol";
import "./IERC721Receiver.sol";
-import "../../utils/SafeMath/SafeMath.sol";
+import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol";
contract ERC721Token is
diff --git a/packages/contracts/contracts/tokens/ERC721Token/IERC721Receiver.sol b/contracts/core/contracts/tokens/ERC721Token/IERC721Receiver.sol
index 8e0e32ab2..8e0e32ab2 100644
--- a/packages/contracts/contracts/tokens/ERC721Token/IERC721Receiver.sol
+++ b/contracts/core/contracts/tokens/ERC721Token/IERC721Receiver.sol
diff --git a/packages/contracts/contracts/tokens/ERC721Token/IERC721Token.sol b/contracts/core/contracts/tokens/ERC721Token/IERC721Token.sol
index ac992c80d..ac992c80d 100644
--- a/packages/contracts/contracts/tokens/ERC721Token/IERC721Token.sol
+++ b/contracts/core/contracts/tokens/ERC721Token/IERC721Token.sol
diff --git a/packages/contracts/contracts/tokens/ERC721Token/MintableERC721Token.sol b/contracts/core/contracts/tokens/ERC721Token/MintableERC721Token.sol
index bc5cd2cc2..bc5cd2cc2 100644
--- a/packages/contracts/contracts/tokens/ERC721Token/MintableERC721Token.sol
+++ b/contracts/core/contracts/tokens/ERC721Token/MintableERC721Token.sol
diff --git a/packages/contracts/contracts/tokens/EtherToken/IEtherToken.sol b/contracts/core/contracts/tokens/EtherToken/IEtherToken.sol
index 9e2e68766..9e2e68766 100644
--- a/packages/contracts/contracts/tokens/EtherToken/IEtherToken.sol
+++ b/contracts/core/contracts/tokens/EtherToken/IEtherToken.sol
diff --git a/packages/contracts/contracts/tokens/EtherToken/WETH9.sol b/contracts/core/contracts/tokens/EtherToken/WETH9.sol
index 17876b86d..17876b86d 100644
--- a/packages/contracts/contracts/tokens/EtherToken/WETH9.sol
+++ b/contracts/core/contracts/tokens/EtherToken/WETH9.sol
diff --git a/packages/contracts/contracts/tokens/ZRXToken/ERC20Token_v1.sol b/contracts/core/contracts/tokens/ZRXToken/ERC20Token_v1.sol
index 4920c4aac..4920c4aac 100644
--- a/packages/contracts/contracts/tokens/ZRXToken/ERC20Token_v1.sol
+++ b/contracts/core/contracts/tokens/ZRXToken/ERC20Token_v1.sol
diff --git a/packages/contracts/contracts/tokens/ZRXToken/Token_v1.sol b/contracts/core/contracts/tokens/ZRXToken/Token_v1.sol
index de619fb7e..de619fb7e 100644
--- a/packages/contracts/contracts/tokens/ZRXToken/Token_v1.sol
+++ b/contracts/core/contracts/tokens/ZRXToken/Token_v1.sol
diff --git a/packages/contracts/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol b/contracts/core/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol
index bf1b0335a..bf1b0335a 100644
--- a/packages/contracts/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol
+++ b/contracts/core/contracts/tokens/ZRXToken/UnlimitedAllowanceToken_v1.sol
diff --git a/packages/contracts/contracts/tokens/ZRXToken/ZRXToken.sol b/contracts/core/contracts/tokens/ZRXToken/ZRXToken.sol
index 831e1822c..831e1822c 100644
--- a/packages/contracts/contracts/tokens/ZRXToken/ZRXToken.sol
+++ b/contracts/core/contracts/tokens/ZRXToken/ZRXToken.sol
diff --git a/packages/contracts/package.json b/contracts/core/package.json
index 25445c4f8..43fa9370e 100644
--- a/packages/contracts/package.json
+++ b/contracts/core/package.json
@@ -1,7 +1,7 @@
{
"private": true,
- "name": "contracts",
- "version": "2.1.55",
+ "name": "@0x/contracts-core",
+ "version": "2.1.56",
"engines": {
"node": ">=6.12"
},
@@ -19,7 +19,8 @@
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
- "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
+ "run_mocha":
+ "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler --contracts-dir contracts",
"clean": "shx rm -rf lib generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
@@ -32,27 +33,26 @@
"lint-contracts": "solhint contracts/**/**/**/**/*.sol"
},
"config": {
- "abis": "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json"
+ "abis": "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|DutchAuction|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiAssetProxy|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json"
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/0x-monorepo.git"
},
- "author": "Amir Bandeali",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/0xProject/0x-monorepo/issues"
},
- "homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md",
+ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/core/README.md",
"devDependencies": {
+ "@0x/contracts-test-utils": "^1.0.0",
"@0x/abi-gen": "^1.0.17",
- "@0x/dev-utils": "^1.0.18",
- "@0x/sol-compiler": "^1.1.13",
- "@0x/sol-cov": "^2.1.13",
- "@0x/subproviders": "^2.1.5",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/sol-compiler": "^1.1.14",
+ "@0x/sol-cov": "^2.1.14",
+ "@0x/subproviders": "^2.1.6",
"@0x/tslint-config": "^1.0.10",
"@types/bn.js": "^4.11.0",
- "@types/ethereumjs-abi": "^0.6.0",
"@types/lodash": "4.14.104",
"@types/node": "*",
"@types/yargs": "^10.0.0",
@@ -61,6 +61,7 @@
"chai-bignumber": "^2.0.1",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
+ "ethereumjs-abi": "0.6.5",
"mocha": "^4.1.0",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
@@ -71,19 +72,19 @@
"yargs": "^10.0.3"
},
"dependencies": {
- "@0x/base-contract": "^3.0.7",
- "@0x/order-utils": "^3.0.3",
+ "@0x/base-contract": "^3.0.8",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/contracts-multisig": "^1.0.0",
+ "@0x/contracts-utils": "^1.0.0",
+ "@0x/contracts-libs": "^1.0.0",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@types/js-combinatorics": "^0.5.29",
"bn.js": "^4.11.8",
"ethereum-types": "^1.1.2",
- "ethereumjs-abi": "0.6.5",
"ethereumjs-util": "^5.1.1",
- "ethers": "~4.0.4",
- "js-combinatorics": "^0.5.3",
"lodash": "^4.17.5"
},
"publishConfig": {
diff --git a/packages/contracts/src/artifacts/index.ts b/contracts/core/src/artifacts/index.ts
index c30972a91..d578c36fe 100644
--- a/packages/contracts/src/artifacts/index.ts
+++ b/contracts/core/src/artifacts/index.ts
@@ -6,6 +6,7 @@ import * as DummyERC721Receiver from '../../generated-artifacts/DummyERC721Recei
import * as DummyERC721Token from '../../generated-artifacts/DummyERC721Token.json';
import * as DummyMultipleReturnERC20Token from '../../generated-artifacts/DummyMultipleReturnERC20Token.json';
import * as DummyNoReturnERC20Token from '../../generated-artifacts/DummyNoReturnERC20Token.json';
+import * as DutchAuction from '../../generated-artifacts/DutchAuction.json';
import * as ERC20Proxy from '../../generated-artifacts/ERC20Proxy.json';
import * as ERC20Token from '../../generated-artifacts/ERC20Token.json';
import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json';
@@ -19,16 +20,12 @@ import * as InvalidERC721Receiver from '../../generated-artifacts/InvalidERC721R
import * as IValidator from '../../generated-artifacts/IValidator.json';
import * as IWallet from '../../generated-artifacts/IWallet.json';
import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json';
-import * as MultiSigWallet from '../../generated-artifacts/MultiSigWallet.json';
-import * as MultiSigWalletWithTimeLock from '../../generated-artifacts/MultiSigWalletWithTimeLock.json';
+import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json';
import * as OrderValidator from '../../generated-artifacts/OrderValidator.json';
import * as ReentrantERC20Token from '../../generated-artifacts/ReentrantERC20Token.json';
import * as TestAssetProxyDispatcher from '../../generated-artifacts/TestAssetProxyDispatcher.json';
import * as TestAssetProxyOwner from '../../generated-artifacts/TestAssetProxyOwner.json';
-import * as TestConstants from '../../generated-artifacts/TestConstants.json';
import * as TestExchangeInternals from '../../generated-artifacts/TestExchangeInternals.json';
-import * as TestLibBytes from '../../generated-artifacts/TestLibBytes.json';
-import * as TestLibs from '../../generated-artifacts/TestLibs.json';
import * as TestSignatureValidator from '../../generated-artifacts/TestSignatureValidator.json';
import * as TestStaticCallReceiver from '../../generated-artifacts/TestStaticCallReceiver.json';
import * as Validator from '../../generated-artifacts/Validator.json';
@@ -44,6 +41,7 @@ export const artifacts = {
DummyERC721Token: DummyERC721Token as ContractArtifact,
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
DummyNoReturnERC20Token: DummyNoReturnERC20Token as ContractArtifact,
+ DutchAuction: DutchAuction as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC20Token: ERC20Token as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
@@ -57,16 +55,12 @@ export const artifacts = {
IWallet: IWallet as ContractArtifact,
InvalidERC721Receiver: InvalidERC721Receiver as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
- MultiSigWallet: MultiSigWallet as ContractArtifact,
- MultiSigWalletWithTimeLock: MultiSigWalletWithTimeLock as ContractArtifact,
+ MultiAssetProxy: MultiAssetProxy as ContractArtifact,
OrderValidator: OrderValidator as ContractArtifact,
ReentrantERC20Token: ReentrantERC20Token as ContractArtifact,
TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact,
TestAssetProxyOwner: TestAssetProxyOwner as ContractArtifact,
- TestConstants: TestConstants as ContractArtifact,
TestExchangeInternals: TestExchangeInternals as ContractArtifact,
- TestLibBytes: TestLibBytes as ContractArtifact,
- TestLibs: TestLibs as ContractArtifact,
TestSignatureValidator: TestSignatureValidator as ContractArtifact,
TestStaticCallReceiver: TestStaticCallReceiver as ContractArtifact,
Validator: Validator as ContractArtifact,
diff --git a/packages/contracts/src/wrappers/index.ts b/contracts/core/src/wrappers/index.ts
index 9ca676b56..ed9d8ef47 100644
--- a/packages/contracts/src/wrappers/index.ts
+++ b/contracts/core/src/wrappers/index.ts
@@ -4,6 +4,7 @@ export * from '../../generated-wrappers/dummy_erc721_receiver';
export * from '../../generated-wrappers/dummy_erc721_token';
export * from '../../generated-wrappers/dummy_multiple_return_erc20_token';
export * from '../../generated-wrappers/dummy_no_return_erc20_token';
+export * from '../../generated-wrappers/dutch_auction';
export * from '../../generated-wrappers/erc20_proxy';
export * from '../../generated-wrappers/erc721_proxy';
export * from '../../generated-wrappers/erc20_token';
@@ -15,16 +16,11 @@ export * from '../../generated-wrappers/i_asset_data';
export * from '../../generated-wrappers/i_asset_proxy';
export * from '../../generated-wrappers/invalid_erc721_receiver';
export * from '../../generated-wrappers/mixin_authorizable';
-export * from '../../generated-wrappers/multi_sig_wallet';
-export * from '../../generated-wrappers/multi_sig_wallet_with_time_lock';
export * from '../../generated-wrappers/order_validator';
export * from '../../generated-wrappers/reentrant_erc20_token';
export * from '../../generated-wrappers/test_asset_proxy_dispatcher';
export * from '../../generated-wrappers/test_asset_proxy_owner';
-export * from '../../generated-wrappers/test_constants';
export * from '../../generated-wrappers/test_exchange_internals';
-export * from '../../generated-wrappers/test_lib_bytes';
-export * from '../../generated-wrappers/test_libs';
export * from '../../generated-wrappers/test_signature_validator';
export * from '../../generated-wrappers/test_static_call_receiver';
export * from '../../generated-wrappers/validator';
diff --git a/packages/contracts/test/asset_proxy/authorizable.ts b/contracts/core/test/asset_proxy/authorizable.ts
index e21af9b81..853d18be0 100644
--- a/packages/contracts/test/asset_proxy/authorizable.ts
+++ b/contracts/core/test/asset_proxy/authorizable.ts
@@ -1,3 +1,11 @@
+import {
+ chaiSetup,
+ constants,
+ expectTransactionFailedAsync,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -6,10 +14,6 @@ import * as _ from 'lodash';
import { MixinAuthorizableContract } from '../../generated-wrappers/mixin_authorizable';
import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/contracts/core/test/asset_proxy/proxies.ts b/contracts/core/test/asset_proxy/proxies.ts
new file mode 100644
index 000000000..2527b0fbf
--- /dev/null
+++ b/contracts/core/test/asset_proxy/proxies.ts
@@ -0,0 +1,1251 @@
+import {
+ chaiSetup,
+ constants,
+ expectTransactionFailedAsync,
+ expectTransactionFailedWithoutReasonAsync,
+ LogDecoder,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
+import { BlockchainLifecycle } from '@0x/dev-utils';
+import { assetDataUtils } from '@0x/order-utils';
+import { RevertReason } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import * as chai from 'chai';
+import * as _ from 'lodash';
+
+import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token';
+import { DummyERC721ReceiverContract } from '../../generated-wrappers/dummy_erc721_receiver';
+import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token';
+import { DummyMultipleReturnERC20TokenContract } from '../../generated-wrappers/dummy_multiple_return_erc20_token';
+import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_no_return_erc20_token';
+import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy';
+import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy';
+import { IAssetDataContract } from '../../generated-wrappers/i_asset_data';
+import { IAssetProxyContract } from '../../generated-wrappers/i_asset_proxy';
+import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy';
+import { artifacts } from '../../src/artifacts';
+import { ERC20Wrapper } from '../utils/erc20_wrapper';
+import { ERC721Wrapper } from '../utils/erc721_wrapper';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+const assetProxyInterface = new IAssetProxyContract(
+ artifacts.IAssetProxy.compilerOutput.abi,
+ constants.NULL_ADDRESS,
+ provider,
+);
+const assetDataInterface = new IAssetDataContract(
+ artifacts.IAssetData.compilerOutput.abi,
+ constants.NULL_ADDRESS,
+ provider,
+);
+
+// tslint:disable:no-unnecessary-type-assertion
+describe('Asset Transfer Proxies', () => {
+ let owner: string;
+ let notAuthorized: string;
+ let authorized: string;
+ let fromAddress: string;
+ let toAddress: string;
+
+ let erc20TokenA: DummyERC20TokenContract;
+ let erc20TokenB: DummyERC20TokenContract;
+ let erc721TokenA: DummyERC721TokenContract;
+ let erc721TokenB: DummyERC721TokenContract;
+ let erc721Receiver: DummyERC721ReceiverContract;
+ let erc20Proxy: ERC20ProxyContract;
+ let erc721Proxy: ERC721ProxyContract;
+ let noReturnErc20Token: DummyNoReturnERC20TokenContract;
+ let multipleReturnErc20Token: DummyMultipleReturnERC20TokenContract;
+ let multiAssetProxy: MultiAssetProxyContract;
+
+ let erc20Wrapper: ERC20Wrapper;
+ let erc721Wrapper: ERC721Wrapper;
+ let erc721AFromTokenId: BigNumber;
+ let erc721BFromTokenId: BigNumber;
+
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ const usedAddresses = ([owner, notAuthorized, authorized, fromAddress, toAddress] = _.slice(accounts, 0, 5));
+
+ erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
+ erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
+
+ // Deploy AssetProxies
+ erc20Proxy = await erc20Wrapper.deployProxyAsync();
+ erc721Proxy = await erc721Wrapper.deployProxyAsync();
+ multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
+ artifacts.MultiAssetProxy,
+ provider,
+ txDefaults,
+ );
+
+ // Configure ERC20Proxy
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ // Configure ERC721Proxy
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ // Configure MultiAssetProxy
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(authorized, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ // Deploy and configure ERC20 tokens
+ const numDummyErc20ToDeploy = 2;
+ [erc20TokenA, erc20TokenB] = await erc20Wrapper.deployDummyTokensAsync(
+ numDummyErc20ToDeploy,
+ constants.DUMMY_TOKEN_DECIMALS,
+ );
+ noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
+ artifacts.DummyNoReturnERC20Token,
+ provider,
+ txDefaults,
+ constants.DUMMY_TOKEN_NAME,
+ constants.DUMMY_TOKEN_SYMBOL,
+ constants.DUMMY_TOKEN_DECIMALS,
+ constants.DUMMY_TOKEN_TOTAL_SUPPLY,
+ );
+ multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
+ artifacts.DummyMultipleReturnERC20Token,
+ provider,
+ txDefaults,
+ constants.DUMMY_TOKEN_NAME,
+ constants.DUMMY_TOKEN_SYMBOL,
+ constants.DUMMY_TOKEN_DECIMALS,
+ constants.DUMMY_TOKEN_TOTAL_SUPPLY,
+ );
+
+ await erc20Wrapper.setBalancesAndAllowancesAsync();
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await noReturnErc20Token.setBalance.sendTransactionAsync(fromAddress, constants.INITIAL_ERC20_BALANCE),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await noReturnErc20Token.approve.sendTransactionAsync(
+ erc20Proxy.address,
+ constants.INITIAL_ERC20_ALLOWANCE,
+ { from: fromAddress },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multipleReturnErc20Token.setBalance.sendTransactionAsync(
+ fromAddress,
+ constants.INITIAL_ERC20_BALANCE,
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multipleReturnErc20Token.approve.sendTransactionAsync(
+ erc20Proxy.address,
+ constants.INITIAL_ERC20_ALLOWANCE,
+ { from: fromAddress },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ // Deploy and configure ERC721 tokens and receiver
+ [erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync();
+ erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
+ artifacts.DummyERC721Receiver,
+ provider,
+ txDefaults,
+ );
+
+ await erc721Wrapper.setBalancesAndAllowancesAsync();
+ const erc721Balances = await erc721Wrapper.getBalancesAsync();
+ erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0];
+ erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0];
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+
+ describe('ERC20Proxy', () => {
+ it('should revert if undefined function is called', async () => {
+ const undefinedSelector = '0x01020304';
+ await expectTransactionFailedWithoutReasonAsync(
+ web3Wrapper.sendTransactionAsync({
+ from: owner,
+ to: erc20Proxy.address,
+ value: constants.ZERO_AMOUNT,
+ data: undefinedSelector,
+ }),
+ );
+ });
+ it('should have an id of 0xf47261b0', async () => {
+ const proxyId = await erc20Proxy.getProxyId.callAsync();
+ const expectedProxyId = '0xf47261b0';
+ expect(proxyId).to.equal(expectedProxyId);
+ });
+ describe('transferFrom', () => {
+ it('should successfully transfer tokens', async () => {
+ // Construct ERC20 asset data
+ const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ // Perform a transfer from fromAddress to toAddress
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ const amount = new BigNumber(10);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: erc20Proxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // Verify transfer was successful
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenA.address].minus(amount),
+ );
+ expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenA.address].add(amount),
+ );
+ });
+
+ it('should successfully transfer tokens that do not return a value', async () => {
+ // Construct ERC20 asset data
+ const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address);
+ // Perform a transfer from fromAddress to toAddress
+ const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
+ const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress);
+ const amount = new BigNumber(10);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: erc20Proxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // Verify transfer was successful
+ const newFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
+ const newToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress);
+ expect(newFromBalance).to.be.bignumber.equal(initialFromBalance.minus(amount));
+ expect(newToBalance).to.be.bignumber.equal(initialToBalance.plus(amount));
+ });
+
+ it('should successfully transfer tokens and ignore extra assetData', async () => {
+ // Construct ERC20 asset data
+ const extraData = '0102030405060708';
+ const encodedAssetData = `${assetDataUtils.encodeERC20AssetData(erc20TokenA.address)}${extraData}`;
+ // Perform a transfer from fromAddress to toAddress
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ const amount = new BigNumber(10);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: erc20Proxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // Verify transfer was successful
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenA.address].minus(amount),
+ );
+ expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenA.address].add(amount),
+ );
+ });
+
+ it('should do nothing if transferring 0 amount of a token', async () => {
+ // Construct ERC20 asset data
+ const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ // Perform a transfer from fromAddress to toAddress
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ const amount = new BigNumber(0);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: erc20Proxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // Verify transfer was successful
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenA.address],
+ );
+ expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenA.address],
+ );
+ });
+
+ it('should revert if allowances are too low', async () => {
+ // Construct ERC20 asset data
+ const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ // Create allowance less than transfer amount. Set allowance on proxy.
+ const allowance = new BigNumber(0);
+ const amount = new BigNumber(10);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
+ from: fromAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ // Perform a transfer; expect this to fail.
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: erc20Proxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.TransferFailed,
+ );
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(newBalances).to.deep.equal(erc20Balances);
+ });
+
+ it('should revert if allowances are too low and token does not return a value', async () => {
+ // Construct ERC20 asset data
+ const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address);
+ // Create allowance less than transfer amount. Set allowance on proxy.
+ const allowance = new BigNumber(0);
+ const amount = new BigNumber(10);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await noReturnErc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
+ from: fromAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
+ const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress);
+ // Perform a transfer; expect this to fail.
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: erc20Proxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.TransferFailed,
+ );
+ const newFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress);
+ const newToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress);
+ expect(newFromBalance).to.be.bignumber.equal(initialFromBalance);
+ expect(newToBalance).to.be.bignumber.equal(initialToBalance);
+ });
+
+ it('should revert if caller is not authorized', async () => {
+ // Construct ERC20 asset data
+ const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ // Perform a transfer from fromAddress to toAddress
+ const amount = new BigNumber(10);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: erc20Proxy.address,
+ data,
+ from: notAuthorized,
+ }),
+ RevertReason.SenderNotAuthorized,
+ );
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(newBalances).to.deep.equal(erc20Balances);
+ });
+
+ it('should revert if token returns more than 32 bytes', async () => {
+ // Construct ERC20 asset data
+ const encodedAssetData = assetDataUtils.encodeERC20AssetData(multipleReturnErc20Token.address);
+ const amount = new BigNumber(10);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ const initialFromBalance = await multipleReturnErc20Token.balanceOf.callAsync(fromAddress);
+ const initialToBalance = await multipleReturnErc20Token.balanceOf.callAsync(toAddress);
+ // Perform a transfer; expect this to fail.
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: erc20Proxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.TransferFailed,
+ );
+ const newFromBalance = await multipleReturnErc20Token.balanceOf.callAsync(fromAddress);
+ const newToBalance = await multipleReturnErc20Token.balanceOf.callAsync(toAddress);
+ expect(newFromBalance).to.be.bignumber.equal(initialFromBalance);
+ expect(newToBalance).to.be.bignumber.equal(initialToBalance);
+ });
+ });
+ });
+
+ describe('ERC721Proxy', () => {
+ it('should revert if undefined function is called', async () => {
+ const undefinedSelector = '0x01020304';
+ await expectTransactionFailedWithoutReasonAsync(
+ web3Wrapper.sendTransactionAsync({
+ from: owner,
+ to: erc721Proxy.address,
+ value: constants.ZERO_AMOUNT,
+ data: undefinedSelector,
+ }),
+ );
+ });
+ it('should have an id of 0x02571792', async () => {
+ const proxyId = await erc721Proxy.getProxyId.callAsync();
+ const expectedProxyId = '0x02571792';
+ expect(proxyId).to.equal(expectedProxyId);
+ });
+ describe('transferFrom', () => {
+ it('should successfully transfer tokens', async () => {
+ // Construct ERC721 asset data
+ const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ // Verify pre-condition
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ // Perform a transfer from fromAddress to toAddress
+ const amount = new BigNumber(1);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: erc721Proxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // Verify transfer was successful
+ const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwnerFromAsset).to.be.bignumber.equal(toAddress);
+ });
+
+ it('should successfully transfer tokens and ignore extra assetData', async () => {
+ // Construct ERC721 asset data
+ const extraData = '0102030405060708';
+ const encodedAssetData = `${assetDataUtils.encodeERC721AssetData(
+ erc721TokenA.address,
+ erc721AFromTokenId,
+ )}${extraData}`;
+ // Verify pre-condition
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ // Perform a transfer from fromAddress to toAddress
+ const amount = new BigNumber(1);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: erc721Proxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // Verify transfer was successful
+ const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwnerFromAsset).to.be.bignumber.equal(toAddress);
+ });
+
+ it('should not call onERC721Received when transferring to a smart contract', async () => {
+ // Construct ERC721 asset data
+ const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ // Verify pre-condition
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ // Perform a transfer from fromAddress to toAddress
+ const amount = new BigNumber(1);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ erc721Receiver.address,
+ amount,
+ );
+ const logDecoder = new LogDecoder(web3Wrapper, artifacts);
+ const tx = await logDecoder.getTxWithDecodedLogsAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: erc721Proxy.address,
+ data,
+ from: authorized,
+ gas: constants.MAX_TRANSFER_FROM_GAS,
+ }),
+ );
+ // Verify that no log was emitted by erc721 receiver
+ expect(tx.logs.length).to.be.equal(1);
+ // Verify transfer was successful
+ const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwnerFromAsset).to.be.bignumber.equal(erc721Receiver.address);
+ });
+
+ it('should revert if transferring 0 amount of a token', async () => {
+ // Construct ERC721 asset data
+ const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ // Verify pre-condition
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ // Perform a transfer from fromAddress to toAddress
+ const amount = new BigNumber(0);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: erc721Proxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.InvalidAmount,
+ );
+ const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwner).to.be.equal(ownerFromAsset);
+ });
+
+ it('should revert if transferring > 1 amount of a token', async () => {
+ // Construct ERC721 asset data
+ const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ // Verify pre-condition
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ // Perform a transfer from fromAddress to toAddress
+ const amount = new BigNumber(500);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: erc721Proxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.InvalidAmount,
+ );
+ const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwner).to.be.equal(ownerFromAsset);
+ });
+
+ it('should revert if allowances are too low', async () => {
+ // Construct ERC721 asset data
+ const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ // Verify pre-condition
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ // Remove transfer approval for fromAddress.
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc721TokenA.approve.sendTransactionAsync(constants.NULL_ADDRESS, erc721AFromTokenId, {
+ from: fromAddress,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ // Perform a transfer; expect this to fail.
+ const amount = new BigNumber(1);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: erc721Proxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.TransferFailed,
+ );
+ const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwner).to.be.equal(ownerFromAsset);
+ });
+
+ it('should revert if caller is not authorized', async () => {
+ // Construct ERC721 asset data
+ const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ // Verify pre-condition
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ // Perform a transfer from fromAddress to toAddress
+ const amount = new BigNumber(1);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ encodedAssetData,
+ fromAddress,
+ toAddress,
+ amount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: erc721Proxy.address,
+ data,
+ from: notAuthorized,
+ }),
+ RevertReason.SenderNotAuthorized,
+ );
+ const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwner).to.be.equal(ownerFromAsset);
+ });
+ });
+ });
+ describe('MultiAssetProxy', () => {
+ it('should revert if undefined function is called', async () => {
+ const undefinedSelector = '0x01020304';
+ await expectTransactionFailedWithoutReasonAsync(
+ web3Wrapper.sendTransactionAsync({
+ from: owner,
+ to: multiAssetProxy.address,
+ value: constants.ZERO_AMOUNT,
+ data: undefinedSelector,
+ }),
+ );
+ });
+ it('should have an id of 0x94cfcdd7', async () => {
+ const proxyId = await multiAssetProxy.getProxyId.callAsync();
+ // first 4 bytes of `keccak256('MultiAsset(uint256[],bytes[])')`
+ const expectedProxyId = '0x94cfcdd7';
+ expect(proxyId).to.equal(expectedProxyId);
+ });
+ describe('transferFrom', () => {
+ it('should transfer a single ERC20 token', async () => {
+ const inputAmount = new BigNumber(1);
+ const erc20Amount = new BigNumber(10);
+ const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const amounts = [erc20Amount];
+ const nestedAssetData = [erc20AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ 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].add(totalAmount),
+ );
+ });
+ it('should successfully transfer multiple of the same ERC20 token', async () => {
+ const inputAmount = new BigNumber(1);
+ const erc20Amount1 = new BigNumber(10);
+ const erc20Amount2 = new BigNumber(20);
+ const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const amounts = [erc20Amount1, erc20Amount2];
+ const nestedAssetData = [erc20AssetData1, erc20AssetData2];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const totalAmount = inputAmount.times(erc20Amount1).plus(inputAmount.times(erc20Amount2));
+ 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].add(totalAmount),
+ );
+ });
+ it('should successfully transfer multiple different ERC20 tokens', async () => {
+ const inputAmount = new BigNumber(1);
+ const erc20Amount1 = new BigNumber(10);
+ const erc20Amount2 = new BigNumber(20);
+ const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
+ const amounts = [erc20Amount1, erc20Amount2];
+ const nestedAssetData = [erc20AssetData1, erc20AssetData2];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const totalErc20AAmount = inputAmount.times(erc20Amount1);
+ const totalErc20BAmount = inputAmount.times(erc20Amount2);
+ expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount),
+ );
+ expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount),
+ );
+ expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount),
+ );
+ expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount),
+ );
+ });
+ it('should transfer a single ERC721 token', async () => {
+ const inputAmount = new BigNumber(1);
+ const erc721Amount = new BigNumber(1);
+ const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ const amounts = [erc721Amount];
+ const nestedAssetData = [erc721AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwnerFromAsset).to.be.equal(toAddress);
+ });
+ it('should successfully transfer multiple of the same ERC721 token', async () => {
+ const erc721Balances = await erc721Wrapper.getBalancesAsync();
+ const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
+ const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(
+ erc721TokenA.address,
+ erc721AFromTokenId2,
+ );
+ const inputAmount = new BigNumber(1);
+ const erc721Amount = new BigNumber(1);
+ const amounts = [erc721Amount, erc721Amount];
+ const nestedAssetData = [erc721AssetData1, erc721AssetData2];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset1).to.be.equal(fromAddress);
+ const ownerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2);
+ expect(ownerFromAsset2).to.be.equal(fromAddress);
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ gas: constants.MAX_TRANSFER_FROM_GAS,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ const newOwnerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2);
+ expect(newOwnerFromAsset1).to.be.equal(toAddress);
+ expect(newOwnerFromAsset2).to.be.equal(toAddress);
+ });
+ it('should successfully transfer multiple different ERC721 tokens', async () => {
+ const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId);
+ const inputAmount = new BigNumber(1);
+ const erc721Amount = new BigNumber(1);
+ const amounts = [erc721Amount, erc721Amount];
+ const nestedAssetData = [erc721AssetData1, erc721AssetData2];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset1).to.be.equal(fromAddress);
+ const ownerFromAsset2 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId);
+ expect(ownerFromAsset2).to.be.equal(fromAddress);
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ gas: constants.MAX_TRANSFER_FROM_GAS,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ const newOwnerFromAsset2 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId);
+ expect(newOwnerFromAsset1).to.be.equal(toAddress);
+ expect(newOwnerFromAsset2).to.be.equal(toAddress);
+ });
+ it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {
+ 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 amounts = [erc20Amount, erc721Amount];
+ const nestedAssetData = [erc20AssetData, erc721AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ 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].add(totalAmount),
+ );
+ const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwnerFromAsset).to.be.equal(toAddress);
+ });
+ it('should successfully transfer tokens and ignore extra assetData', async () => {
+ 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 amounts = [erc20Amount, erc721Amount];
+ const nestedAssetData = [erc20AssetData, erc721AssetData];
+ const extraData = '0102030405060708';
+ const assetData = `${assetDataInterface.MultiAsset.getABIEncodedTransactionData(
+ amounts,
+ nestedAssetData,
+ )}${extraData}`;
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset).to.be.equal(fromAddress);
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ 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].add(totalAmount),
+ );
+ const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(newOwnerFromAsset).to.be.equal(toAddress);
+ });
+ it('should successfully transfer correct amounts when the `amount` > 1', async () => {
+ const inputAmount = new BigNumber(100);
+ const erc20Amount1 = new BigNumber(10);
+ const erc20Amount2 = new BigNumber(20);
+ const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
+ const amounts = [erc20Amount1, erc20Amount2];
+ const nestedAssetData = [erc20AssetData1, erc20AssetData2];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const totalErc20AAmount = inputAmount.times(erc20Amount1);
+ const totalErc20BAmount = inputAmount.times(erc20Amount2);
+ expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount),
+ );
+ expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount),
+ );
+ expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount),
+ );
+ expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount),
+ );
+ });
+ it('should successfully transfer a large amount of tokens', async () => {
+ const inputAmount = new BigNumber(1);
+ const erc20Amount1 = new BigNumber(10);
+ const erc20Amount2 = new BigNumber(20);
+ const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
+ const erc721Amount = new BigNumber(1);
+ const erc721Balances = await erc721Wrapper.getBalancesAsync();
+ const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1];
+ const erc721BFromTokenId2 = erc721Balances[fromAddress][erc721TokenB.address][1];
+ const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(
+ erc721TokenA.address,
+ erc721AFromTokenId2,
+ );
+ const erc721AssetData3 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId);
+ const erc721AssetData4 = assetDataUtils.encodeERC721AssetData(
+ erc721TokenB.address,
+ erc721BFromTokenId2,
+ );
+ const amounts = [erc721Amount, erc20Amount1, erc721Amount, erc20Amount2, erc721Amount, erc721Amount];
+ const nestedAssetData = [
+ erc721AssetData1,
+ erc20AssetData1,
+ erc721AssetData2,
+ erc20AssetData2,
+ erc721AssetData3,
+ erc721AssetData4,
+ ];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ expect(ownerFromAsset1).to.be.equal(fromAddress);
+ const ownerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2);
+ expect(ownerFromAsset2).to.be.equal(fromAddress);
+ const ownerFromAsset3 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId);
+ expect(ownerFromAsset3).to.be.equal(fromAddress);
+ const ownerFromAsset4 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId2);
+ expect(ownerFromAsset4).to.be.equal(fromAddress);
+ const erc20Balances = await erc20Wrapper.getBalancesAsync();
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId);
+ const newOwnerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2);
+ const newOwnerFromAsset3 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId);
+ const newOwnerFromAsset4 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId2);
+ expect(newOwnerFromAsset1).to.be.equal(toAddress);
+ expect(newOwnerFromAsset2).to.be.equal(toAddress);
+ expect(newOwnerFromAsset3).to.be.equal(toAddress);
+ expect(newOwnerFromAsset4).to.be.equal(toAddress);
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const totalErc20AAmount = inputAmount.times(erc20Amount1);
+ const totalErc20BAmount = inputAmount.times(erc20Amount2);
+ expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount),
+ );
+ expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount),
+ );
+ expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal(
+ erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount),
+ );
+ expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal(
+ erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount),
+ );
+ });
+ it('should revert if a single transfer fails', async () => {
+ const inputAmount = new BigNumber(1);
+ const erc20Amount = new BigNumber(10);
+ const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ // 2 is an invalid erc721 amount
+ const erc721Amount = new BigNumber(2);
+ const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ const amounts = [erc20Amount, erc721Amount];
+ const nestedAssetData = [erc20AssetData, erc721AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.InvalidAmount,
+ );
+ });
+ it('should revert if an AssetProxy is not registered', async () => {
+ 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 invalidProxyId = '0x12345678';
+ const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`;
+ const amounts = [erc20Amount, erc721Amount];
+ const nestedAssetData = [erc20AssetData, invalidErc721AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.AssetProxyDoesNotExist,
+ );
+ });
+ it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => {
+ const inputAmount = new BigNumber(1);
+ const erc20Amount = new BigNumber(10);
+ const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
+ const amounts = [erc20Amount];
+ const nestedAssetData = [erc20AssetData, erc721AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.LengthMismatch,
+ );
+ });
+ it('should revert if amounts multiplication results in an overflow', async () => {
+ const inputAmount = new BigNumber(2).pow(128);
+ const erc20Amount = new BigNumber(2).pow(128);
+ const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const amounts = [erc20Amount];
+ const nestedAssetData = [erc20AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.Uint256Overflow,
+ );
+ });
+ it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => {
+ const inputAmount = new BigNumber(1);
+ const erc20Amount = new BigNumber(10);
+ const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
+ const erc721Amount = new BigNumber(1);
+ const erc721AssetData = '0x123456';
+ const amounts = [erc20Amount, erc721Amount];
+ const nestedAssetData = [erc20AssetData, erc721AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: authorized,
+ }),
+ RevertReason.LengthGreaterThan3Required,
+ );
+ });
+ it('should revert if caller is not authorized', async () => {
+ 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 amounts = [erc20Amount, erc721Amount];
+ const nestedAssetData = [erc20AssetData, erc721AssetData];
+ const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
+ assetData,
+ fromAddress,
+ toAddress,
+ inputAmount,
+ );
+ await expectTransactionFailedAsync(
+ web3Wrapper.sendTransactionAsync({
+ to: multiAssetProxy.address,
+ data,
+ from: notAuthorized,
+ }),
+ RevertReason.SenderNotAuthorized,
+ );
+ });
+ });
+ });
+});
+// tslint:enable:no-unnecessary-type-assertion
+// tslint:disable:max-file-line-count
diff --git a/packages/contracts/test/exchange/core.ts b/contracts/core/test/exchange/core.ts
index fc8dc5346..fd6b9ee6b 100644
--- a/packages/contracts/test/exchange/core.ts
+++ b/contracts/core/test/exchange/core.ts
@@ -1,3 +1,16 @@
+import {
+ chaiSetup,
+ constants,
+ ERC20BalancesByOwner,
+ expectTransactionFailedAsync,
+ getLatestBlockTimestampAsync,
+ increaseTimeAndMineBlockAsync,
+ OrderFactory,
+ OrderStatus,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
@@ -14,23 +27,23 @@ import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_
import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy';
import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy';
import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated-wrappers/exchange';
+import { IAssetDataContract } from '../../generated-wrappers/i_asset_data';
+import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy';
import { ReentrantERC20TokenContract } from '../../generated-wrappers/reentrant_erc20_token';
import { TestStaticCallReceiverContract } from '../../generated-wrappers/test_static_call_receiver';
import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync } from '../utils/assertions';
-import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ERC721Wrapper } from '../utils/erc721_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
-import { OrderFactory } from '../utils/order_factory';
-import { ERC20BalancesByOwner, OrderStatus } from '../utils/types';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+const assetDataInterface = new IAssetDataContract(
+ artifacts.IAssetData.compilerOutput.abi,
+ constants.NULL_ADDRESS,
+ provider,
+);
// tslint:disable:no-unnecessary-type-assertion
describe('Exchange core', () => {
let makerAddress: string;
@@ -47,6 +60,7 @@ describe('Exchange core', () => {
let exchange: ExchangeContract;
let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract;
+ let multiAssetProxy: MultiAssetProxyContract;
let maliciousWallet: TestStaticCallReceiverContract;
let maliciousValidator: TestStaticCallReceiverContract;
@@ -76,31 +90,39 @@ describe('Exchange core', () => {
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
+ // Deploy AssetProxies, Exchange, tokens, and malicious contracts
+ erc20Proxy = await erc20Wrapper.deployProxyAsync();
+ erc721Proxy = await erc721Wrapper.deployProxyAsync();
+ multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync(
+ artifacts.MultiAssetProxy,
+ provider,
+ txDefaults,
+ );
const numDummyErc20ToDeploy = 3;
[erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
numDummyErc20ToDeploy,
constants.DUMMY_TOKEN_DECIMALS,
);
- erc20Proxy = await erc20Wrapper.deployProxyAsync();
- await erc20Wrapper.setBalancesAndAllowancesAsync();
-
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
- erc721Proxy = await erc721Wrapper.deployProxyAsync();
- await erc721Wrapper.setBalancesAndAllowancesAsync();
- const erc721Balances = await erc721Wrapper.getBalancesAsync();
- erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
- erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address];
-
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
artifacts.Exchange,
provider,
txDefaults,
assetDataUtils.encodeERC20AssetData(zrxToken.address),
);
- exchangeWrapper = new ExchangeWrapper(exchange, provider);
- await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
- await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
+ maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
+ artifacts.TestStaticCallReceiver,
+ provider,
+ txDefaults,
+ );
+ reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
+ artifacts.ReentrantERC20Token,
+ provider,
+ txDefaults,
+ exchange.address,
+ );
+ // Configure ERC20Proxy
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
from: owner,
@@ -108,27 +130,64 @@ describe('Exchange core', () => {
constants.AWAIT_TRANSACTION_MINED_MS,
);
await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ // Configure ERC721Proxy
+ await web3Wrapper.awaitTransactionSuccessAsync(
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
from: owner,
}),
constants.AWAIT_TRANSACTION_MINED_MS,
);
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
- maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
- artifacts.TestStaticCallReceiver,
- provider,
- txDefaults,
+ // Configure MultiAssetProxy
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
- reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
- artifacts.ReentrantERC20Token,
- provider,
- txDefaults,
- exchange.address,
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, {
+ from: owner,
+ }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
);
+ // Configure Exchange
+ exchangeWrapper = new ExchangeWrapper(exchange, provider);
+ await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
+ await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
+ await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, owner);
+
+ // Configure ERC20 tokens
+ await erc20Wrapper.setBalancesAndAllowancesAsync();
+
+ // Configure ERC721 tokens
+ await erc721Wrapper.setBalancesAndAllowancesAsync();
+ const erc721Balances = await erc721Wrapper.getBalancesAsync();
+ erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
+ erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address];
+
+ // Configure order defaults
defaultMakerAssetAddress = erc20TokenA.address;
defaultTakerAssetAddress = erc20TokenB.address;
-
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
exchangeAddress: exchange.address,
@@ -707,6 +766,292 @@ describe('Exchange core', () => {
});
});
+ describe('Testing exchange of multiple assets', () => {
+ it('should allow multiple assets to be exchanged for a single asset', async () => {
+ const makerAmounts = [new BigNumber(10), new BigNumber(20)];
+ const makerNestedAssetData = [
+ assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
+ assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
+ ];
+ const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
+ makerAmounts,
+ makerNestedAssetData,
+ );
+ const makerAssetAmount = new BigNumber(1);
+ const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
+ const takerAssetAmount = new BigNumber(10);
+ signedOrder = await orderFactory.newSignedOrderAsync({
+ makerAssetData,
+ takerAssetData,
+ makerAssetAmount,
+ takerAssetAmount,
+ makerFee: constants.ZERO_AMOUNT,
+ takerFee: constants.ZERO_AMOUNT,
+ });
+
+ const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress);
+ const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress);
+ const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+
+ await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
+
+ const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress);
+ const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress);
+ const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+
+ expect(finalMakerBalanceA).to.be.bignumber.equal(
+ initialMakerBalanceA.minus(makerAmounts[0].times(makerAssetAmount)),
+ );
+ expect(finalMakerBalanceB).to.be.bignumber.equal(
+ initialMakerBalanceB.minus(makerAmounts[1].times(makerAssetAmount)),
+ );
+ expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.plus(takerAssetAmount));
+ expect(finalTakerBalanceA).to.be.bignumber.equal(
+ initialTakerBalanceA.plus(makerAmounts[0].times(makerAssetAmount)),
+ );
+ expect(finalTakerBalanceB).to.be.bignumber.equal(
+ initialTakerBalanceB.plus(makerAmounts[1].times(makerAssetAmount)),
+ );
+ expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(takerAssetAmount));
+ });
+ it('should allow multiple assets to be exchanged for multiple assets', async () => {
+ const makerAmounts = [new BigNumber(10), new BigNumber(20)];
+ const makerNestedAssetData = [
+ assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
+ assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
+ ];
+ const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
+ makerAmounts,
+ makerNestedAssetData,
+ );
+ const makerAssetAmount = new BigNumber(1);
+ const takerAmounts = [new BigNumber(10), new BigNumber(1)];
+ const takerAssetId = erc721TakerAssetIds[0];
+ const takerNestedAssetData = [
+ assetDataUtils.encodeERC20AssetData(zrxToken.address),
+ assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
+ ];
+ const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
+ takerAmounts,
+ takerNestedAssetData,
+ );
+ const takerAssetAmount = new BigNumber(1);
+ signedOrder = await orderFactory.newSignedOrderAsync({
+ makerAssetData,
+ takerAssetData,
+ makerAssetAmount,
+ takerAssetAmount,
+ makerFee: constants.ZERO_AMOUNT,
+ takerFee: constants.ZERO_AMOUNT,
+ });
+
+ const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress);
+ const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress);
+ const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+ const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId);
+ expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
+
+ await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
+
+ const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress);
+ const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress);
+ const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+ const finalOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId);
+
+ expect(finalMakerBalanceA).to.be.bignumber.equal(
+ initialMakerBalanceA.minus(makerAmounts[0].times(makerAssetAmount)),
+ );
+ expect(finalMakerBalanceB).to.be.bignumber.equal(
+ initialMakerBalanceB.minus(makerAmounts[1].times(makerAssetAmount)),
+ );
+ expect(finalMakerZrxBalance).to.be.bignumber.equal(
+ initialMakerZrxBalance.plus(takerAmounts[0].times(takerAssetAmount)),
+ );
+ expect(finalTakerBalanceA).to.be.bignumber.equal(
+ initialTakerBalanceA.plus(makerAmounts[0].times(makerAssetAmount)),
+ );
+ expect(finalTakerBalanceB).to.be.bignumber.equal(
+ initialTakerBalanceB.plus(makerAmounts[1].times(makerAssetAmount)),
+ );
+ expect(finalTakerZrxBalance).to.be.bignumber.equal(
+ initialTakerZrxBalance.minus(takerAmounts[0].times(takerAssetAmount)),
+ );
+ expect(finalOwnerTakerAsset).to.be.equal(makerAddress);
+ });
+ it('should allow an order selling multiple assets to be partially filled', async () => {
+ const makerAmounts = [new BigNumber(10), new BigNumber(20)];
+ const makerNestedAssetData = [
+ assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
+ assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
+ ];
+ const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
+ makerAmounts,
+ makerNestedAssetData,
+ );
+ const makerAssetAmount = new BigNumber(30);
+ const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
+ const takerAssetAmount = new BigNumber(10);
+ signedOrder = await orderFactory.newSignedOrderAsync({
+ makerAssetData,
+ takerAssetData,
+ makerAssetAmount,
+ takerAssetAmount,
+ makerFee: constants.ZERO_AMOUNT,
+ takerFee: constants.ZERO_AMOUNT,
+ });
+
+ const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress);
+ const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress);
+ const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+
+ const takerAssetFillAmount = takerAssetAmount.dividedToIntegerBy(2);
+ await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
+ takerAssetFillAmount,
+ });
+
+ const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress);
+ const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress);
+ const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+
+ expect(finalMakerBalanceA).to.be.bignumber.equal(
+ initialMakerBalanceA.minus(
+ makerAmounts[0].times(
+ makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ ),
+ );
+ expect(finalMakerBalanceB).to.be.bignumber.equal(
+ initialMakerBalanceB.minus(
+ makerAmounts[1].times(
+ makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ ),
+ );
+ expect(finalMakerZrxBalance).to.be.bignumber.equal(
+ initialMakerZrxBalance.plus(
+ takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ );
+ expect(finalTakerBalanceA).to.be.bignumber.equal(
+ initialTakerBalanceA.plus(
+ makerAmounts[0].times(
+ makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ ),
+ );
+ expect(finalTakerBalanceB).to.be.bignumber.equal(
+ initialTakerBalanceB.plus(
+ makerAmounts[1].times(
+ makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ ),
+ );
+ expect(finalTakerZrxBalance).to.be.bignumber.equal(
+ initialTakerZrxBalance.minus(
+ takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ );
+ });
+ it('should allow an order buying multiple assets to be partially filled', async () => {
+ const takerAmounts = [new BigNumber(10), new BigNumber(20)];
+ const takerNestedAssetData = [
+ assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
+ assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
+ ];
+ const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
+ takerAmounts,
+ takerNestedAssetData,
+ );
+ const takerAssetAmount = new BigNumber(30);
+ const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
+ const makerAssetAmount = new BigNumber(10);
+ signedOrder = await orderFactory.newSignedOrderAsync({
+ makerAssetData,
+ takerAssetData,
+ makerAssetAmount,
+ takerAssetAmount,
+ makerFee: constants.ZERO_AMOUNT,
+ takerFee: constants.ZERO_AMOUNT,
+ });
+
+ const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress);
+ const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress);
+ const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+
+ const takerAssetFillAmount = takerAssetAmount.dividedToIntegerBy(2);
+ await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
+ takerAssetFillAmount,
+ });
+
+ const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress);
+ const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress);
+ const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+
+ expect(finalMakerBalanceA).to.be.bignumber.equal(
+ initialMakerBalanceA.plus(
+ takerAmounts[0].times(
+ takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ ),
+ );
+ expect(finalMakerBalanceB).to.be.bignumber.equal(
+ initialMakerBalanceB.plus(
+ takerAmounts[1].times(
+ takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ ),
+ );
+ expect(finalMakerZrxBalance).to.be.bignumber.equal(
+ initialMakerZrxBalance.minus(
+ makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ );
+ expect(finalTakerBalanceA).to.be.bignumber.equal(
+ initialTakerBalanceA.minus(
+ takerAmounts[0].times(
+ takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ ),
+ );
+ expect(finalTakerBalanceB).to.be.bignumber.equal(
+ initialTakerBalanceB.minus(
+ takerAmounts[1].times(
+ takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ ),
+ );
+ expect(finalTakerZrxBalance).to.be.bignumber.equal(
+ initialTakerZrxBalance.plus(
+ makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount),
+ ),
+ );
+ });
+ });
+
describe('getOrderInfo', () => {
beforeEach(async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
diff --git a/packages/contracts/test/exchange/dispatcher.ts b/contracts/core/test/exchange/dispatcher.ts
index 3d3aa42c2..9bc5cbcce 100644
--- a/packages/contracts/test/exchange/dispatcher.ts
+++ b/contracts/core/test/exchange/dispatcher.ts
@@ -1,3 +1,12 @@
+import {
+ chaiSetup,
+ constants,
+ expectTransactionFailedAsync,
+ LogDecoder,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId, RevertReason } from '@0x/types';
@@ -14,13 +23,8 @@ import {
TestAssetProxyDispatcherContract,
} from '../../generated-wrappers/test_asset_proxy_dispatcher';
import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ERC721Wrapper } from '../utils/erc721_wrapper';
-import { LogDecoder } from '../utils/log_decoder';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
@@ -145,7 +149,7 @@ describe('AssetProxyDispatcher', () => {
});
it('should log an event with correct arguments when an asset proxy is registered', async () => {
- const logDecoder = new LogDecoder(web3Wrapper);
+ const logDecoder = new LogDecoder(web3Wrapper, artifacts);
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
);
diff --git a/packages/contracts/test/exchange/fill_order.ts b/contracts/core/test/exchange/fill_order.ts
index 37efaad2b..2bdbe4855 100644
--- a/packages/contracts/test/exchange/fill_order.ts
+++ b/contracts/core/test/exchange/fill_order.ts
@@ -1,23 +1,25 @@
-import { BlockchainLifecycle } from '@0x/dev-utils';
-import * as _ from 'lodash';
-
-import { chaiSetup } from '../utils/chai_setup';
-import {
- FillOrderCombinatorialUtils,
- fillOrderCombinatorialUtilsFactoryAsync,
-} from '../utils/fill_order_combinatorial_utils';
import {
AllowanceAmountScenario,
AssetDataScenario,
BalanceAmountScenario,
+ chaiSetup,
ExpirationTimeSecondsScenario,
FeeRecipientAddressScenario,
FillScenario,
OrderAssetAmountScenario,
+ provider,
TakerAssetFillAmountScenario,
TakerScenario,
-} from '../utils/types';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
+import { BlockchainLifecycle } from '@0x/dev-utils';
+import * as _ from 'lodash';
+
+import {
+ FillOrderCombinatorialUtils,
+ fillOrderCombinatorialUtilsFactoryAsync,
+} from '../utils/fill_order_combinatorial_utils';
chaiSetup.configure();
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
diff --git a/packages/contracts/test/exchange/internal.ts b/contracts/core/test/exchange/internal.ts
index 109be29c6..972f5efb6 100644
--- a/packages/contracts/test/exchange/internal.ts
+++ b/contracts/core/test/exchange/internal.ts
@@ -1,3 +1,15 @@
+import {
+ bytes32Values,
+ chaiSetup,
+ constants,
+ FillResults,
+ getRevertReasonOrErrorMessageForSendTransactionAsync,
+ provider,
+ testCombinatoriallyWithReferenceFuncAsync,
+ txDefaults,
+ uint256Values,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { Order, RevertReason, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -6,12 +18,6 @@ import * as _ from 'lodash';
import { TestExchangeInternalsContract } from '../../generated-wrappers/test_exchange_internals';
import { artifacts } from '../../src/artifacts';
-import { getRevertReasonOrErrorMessageForSendTransactionAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { bytes32Values, testCombinatoriallyWithReferenceFuncAsync, uint256Values } from '../utils/combinatorial_utils';
-import { constants } from '../utils/constants';
-import { FillResults } from '../utils/types';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/packages/contracts/test/exchange/match_orders.ts b/contracts/core/test/exchange/match_orders.ts
index eea9992d9..0e841b359 100644
--- a/packages/contracts/test/exchange/match_orders.ts
+++ b/contracts/core/test/exchange/match_orders.ts
@@ -1,3 +1,14 @@
+import {
+ chaiSetup,
+ constants,
+ ERC20BalancesByOwner,
+ ERC721TokenIdsByOwner,
+ expectTransactionFailedAsync,
+ OrderFactory,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { RevertReason } from '@0x/types';
@@ -14,16 +25,10 @@ import { ExchangeContract } from '../../generated-wrappers/exchange';
import { ReentrantERC20TokenContract } from '../../generated-wrappers/reentrant_erc20_token';
import { TestExchangeInternalsContract } from '../../generated-wrappers/test_exchange_internals';
import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ERC721Wrapper } from '../utils/erc721_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
import { MatchOrderTester } from '../utils/match_order_tester';
-import { OrderFactory } from '../utils/order_factory';
-import { ERC20BalancesByOwner, ERC721TokenIdsByOwner } from '../utils/types';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
chaiSetup.configure();
diff --git a/packages/contracts/test/exchange/signature_validator.ts b/contracts/core/test/exchange/signature_validator.ts
index 756c72766..b84a488a1 100644
--- a/packages/contracts/test/exchange/signature_validator.ts
+++ b/contracts/core/test/exchange/signature_validator.ts
@@ -1,3 +1,14 @@
+import {
+ addressUtils,
+ chaiSetup,
+ constants,
+ expectContractCallFailedAsync,
+ LogDecoder,
+ OrderFactory,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils, signatureUtils } from '@0x/order-utils';
import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
@@ -13,13 +24,6 @@ import { TestStaticCallReceiverContract } from '../../generated-wrappers/test_st
import { ValidatorContract } from '../../generated-wrappers/validator';
import { WalletContract } from '../../generated-wrappers/wallet';
import { artifacts } from '../../src/artifacts';
-import { addressUtils } from '../utils/address_utils';
-import { expectContractCallFailedAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { LogDecoder } from '../utils/log_decoder';
-import { OrderFactory } from '../utils/order_factory';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
@@ -73,7 +77,7 @@ describe('MixinSignatureValidator', () => {
provider,
txDefaults,
);
- signatureValidatorLogDecoder = new LogDecoder(web3Wrapper);
+ signatureValidatorLogDecoder = new LogDecoder(web3Wrapper, artifacts);
await web3Wrapper.awaitTransactionSuccessAsync(
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(testValidator.address, true, {
from: signerAddress,
diff --git a/packages/contracts/test/exchange/transactions.ts b/contracts/core/test/exchange/transactions.ts
index 1b5eef295..c4086d9be 100644
--- a/packages/contracts/test/exchange/transactions.ts
+++ b/contracts/core/test/exchange/transactions.ts
@@ -1,3 +1,16 @@
+import {
+ chaiSetup,
+ constants,
+ ERC20BalancesByOwner,
+ expectTransactionFailedAsync,
+ OrderFactory,
+ orderUtils,
+ provider,
+ SignedTransaction,
+ TransactionFactory,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
import { OrderWithoutExchangeAddress, RevertReason, SignedOrder } from '@0x/types';
@@ -11,16 +24,8 @@ import { ExchangeContract } from '../../generated-wrappers/exchange';
import { ExchangeWrapperContract } from '../../generated-wrappers/exchange_wrapper';
import { WhitelistContract } from '../../generated-wrappers/whitelist';
import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
-import { OrderFactory } from '../utils/order_factory';
-import { orderUtils } from '../utils/order_utils';
-import { TransactionFactory } from '../utils/transaction_factory';
-import { ERC20BalancesByOwner, SignedTransaction } from '../utils/types';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/packages/contracts/test/exchange/wrapper.ts b/contracts/core/test/exchange/wrapper.ts
index 6b660aac5..17cb7a3bb 100644
--- a/packages/contracts/test/exchange/wrapper.ts
+++ b/contracts/core/test/exchange/wrapper.ts
@@ -1,3 +1,16 @@
+import {
+ chaiSetup,
+ constants,
+ ERC20BalancesByOwner,
+ expectTransactionFailedAsync,
+ getLatestBlockTimestampAsync,
+ increaseTimeAndMineBlockAsync,
+ OrderFactory,
+ OrderStatus,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { RevertReason, SignedOrder } from '@0x/types';
@@ -13,16 +26,9 @@ import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy';
import { ExchangeContract } from '../../generated-wrappers/exchange';
import { ReentrantERC20TokenContract } from '../../generated-wrappers/reentrant_erc20_token';
import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync } from '../utils/assertions';
-import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ERC721Wrapper } from '../utils/erc721_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
-import { OrderFactory } from '../utils/order_factory';
-import { ERC20BalancesByOwner, OrderStatus } from '../utils/types';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/contracts/core/test/extensions/dutch_auction.ts b/contracts/core/test/extensions/dutch_auction.ts
new file mode 100644
index 000000000..54e6092d7
--- /dev/null
+++ b/contracts/core/test/extensions/dutch_auction.ts
@@ -0,0 +1,452 @@
+import {
+ chaiSetup,
+ constants,
+ ContractName,
+ ERC20BalancesByOwner,
+ expectTransactionFailedAsync,
+ getLatestBlockTimestampAsync,
+ OrderFactory,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
+import { BlockchainLifecycle } from '@0x/dev-utils';
+import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
+import { RevertReason, SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
+import * as chai from 'chai';
+import ethAbi = require('ethereumjs-abi');
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token';
+import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token';
+import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction';
+import { ExchangeContract } from '../../generated-wrappers/exchange';
+import { WETH9Contract } from '../../generated-wrappers/weth9';
+import { artifacts } from '../../src/artifacts';
+import { ERC20Wrapper } from '../utils/erc20_wrapper';
+import { ERC721Wrapper } from '../utils/erc721_wrapper';
+import { ExchangeWrapper } from '../utils/exchange_wrapper';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+const DECIMALS_DEFAULT = 18;
+
+describe(ContractName.DutchAuction, () => {
+ let makerAddress: string;
+ let owner: string;
+ let takerAddress: string;
+ let feeRecipientAddress: string;
+ let defaultMakerAssetAddress: string;
+
+ let zrxToken: DummyERC20TokenContract;
+ let erc20TokenA: DummyERC20TokenContract;
+ let erc721Token: DummyERC721TokenContract;
+ let dutchAuctionContract: DutchAuctionContract;
+ let wethContract: WETH9Contract;
+
+ let sellerOrderFactory: OrderFactory;
+ let buyerOrderFactory: OrderFactory;
+ let erc20Wrapper: ERC20Wrapper;
+ let erc20Balances: ERC20BalancesByOwner;
+ let currentBlockTimestamp: number;
+ let auctionBeginTimeSeconds: BigNumber;
+ let auctionEndTimeSeconds: BigNumber;
+ let auctionBeginAmount: BigNumber;
+ let auctionEndAmount: BigNumber;
+ let sellOrder: SignedOrder;
+ let buyOrder: SignedOrder;
+ let erc721MakerAssetIds: BigNumber[];
+ const tenMinutesInSeconds = 10 * 60;
+
+ function extendMakerAssetData(makerAssetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string {
+ return ethUtil.bufferToHex(
+ Buffer.concat([
+ ethUtil.toBuffer(makerAssetData),
+ ethUtil.toBuffer(
+ (ethAbi as any).rawEncode(
+ ['uint256', 'uint256'],
+ [beginTimeSeconds.toString(), beginAmount.toString()],
+ ),
+ ),
+ ]),
+ );
+ }
+
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts);
+
+ erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
+
+ const numDummyErc20ToDeploy = 2;
+ [erc20TokenA, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
+ numDummyErc20ToDeploy,
+ constants.DUMMY_TOKEN_DECIMALS,
+ );
+ const erc20Proxy = await erc20Wrapper.deployProxyAsync();
+ await erc20Wrapper.setBalancesAndAllowancesAsync();
+
+ const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
+ [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
+ const erc721Proxy = await erc721Wrapper.deployProxyAsync();
+ await erc721Wrapper.setBalancesAndAllowancesAsync();
+ const erc721Balances = await erc721Wrapper.getBalancesAsync();
+ erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
+
+ wethContract = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, txDefaults);
+ erc20Wrapper.addDummyTokenContract(wethContract as any);
+
+ const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
+ const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
+ artifacts.Exchange,
+ provider,
+ txDefaults,
+ zrxAssetData,
+ );
+ const exchangeWrapper = new ExchangeWrapper(exchangeInstance, provider);
+ await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
+ await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
+
+ await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
+ from: owner,
+ });
+ await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
+ from: owner,
+ });
+
+ const dutchAuctionInstance = await DutchAuctionContract.deployFrom0xArtifactAsync(
+ artifacts.DutchAuction,
+ provider,
+ txDefaults,
+ exchangeInstance.address,
+ );
+ dutchAuctionContract = new DutchAuctionContract(
+ dutchAuctionInstance.abi,
+ dutchAuctionInstance.address,
+ provider,
+ );
+
+ defaultMakerAssetAddress = erc20TokenA.address;
+ const defaultTakerAssetAddress = wethContract.address;
+
+ // Set up taker WETH balance and allowance
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await wethContract.deposit.sendTransactionAsync({
+ from: takerAddress,
+ value: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), DECIMALS_DEFAULT),
+ }),
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await wethContract.approve.sendTransactionAsync(
+ erc20Proxy.address,
+ constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
+ { from: takerAddress },
+ ),
+ );
+ web3Wrapper.abiDecoder.addABI(exchangeInstance.abi);
+ web3Wrapper.abiDecoder.addABI(zrxToken.abi);
+ erc20Wrapper.addTokenOwnerAddress(dutchAuctionContract.address);
+
+ currentBlockTimestamp = await getLatestBlockTimestampAsync();
+ // Default auction begins 10 minutes ago
+ auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp).minus(tenMinutesInSeconds);
+ // Default auction ends 10 from now
+ auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp).plus(tenMinutesInSeconds);
+ auctionBeginAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT);
+ auctionEndAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT);
+
+ // Default sell order and buy order are exact mirrors
+ const sellerDefaultOrderParams = {
+ salt: generatePseudoRandomSalt(),
+ exchangeAddress: exchangeInstance.address,
+ makerAddress,
+ feeRecipientAddress,
+ // taker address or sender address should be set to the ducth auction contract
+ takerAddress: dutchAuctionContract.address,
+ makerAssetData: extendMakerAssetData(
+ assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ auctionBeginTimeSeconds,
+ auctionBeginAmount,
+ ),
+ takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS_DEFAULT),
+ takerAssetAmount: auctionEndAmount,
+ expirationTimeSeconds: auctionEndTimeSeconds,
+ makerFee: constants.ZERO_AMOUNT,
+ takerFee: constants.ZERO_AMOUNT,
+ };
+ // Default buy order is for the auction begin price
+ const buyerDefaultOrderParams = {
+ ...sellerDefaultOrderParams,
+ makerAddress: takerAddress,
+ makerAssetData: sellerDefaultOrderParams.takerAssetData,
+ takerAssetData: sellerDefaultOrderParams.makerAssetData,
+ makerAssetAmount: auctionBeginAmount,
+ takerAssetAmount: sellerDefaultOrderParams.makerAssetAmount,
+ };
+ const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
+ const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
+ sellerOrderFactory = new OrderFactory(makerPrivateKey, sellerDefaultOrderParams);
+ buyerOrderFactory = new OrderFactory(takerPrivateKey, buyerDefaultOrderParams);
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync();
+ buyOrder = await buyerOrderFactory.newSignedOrderAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ describe('matchOrders', () => {
+ it('should be worth the begin price at the begining of the auction', async () => {
+ auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2);
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
+ makerAssetData: extendMakerAssetData(
+ assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ auctionBeginTimeSeconds,
+ auctionBeginAmount,
+ ),
+ });
+ const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
+ expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount);
+ expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
+ });
+ it('should be be worth the end price at the end of the auction', async () => {
+ auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
+ auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
+ makerAssetData: extendMakerAssetData(
+ assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ auctionBeginTimeSeconds,
+ auctionBeginAmount,
+ ),
+ expirationTimeSeconds: auctionEndTimeSeconds,
+ });
+ const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
+ expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionEndAmount);
+ expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount);
+ });
+ it('should match orders at current amount and send excess to buyer', async () => {
+ const beforeAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
+ buyOrder = await buyerOrderFactory.newSignedOrderAsync({
+ makerAssetAmount: beforeAuctionDetails.currentAmount.times(2),
+ });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ ),
+ );
+ const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(newBalances[dutchAuctionContract.address][wethContract.address]).to.be.bignumber.equal(
+ constants.ZERO_AMOUNT,
+ );
+ // HACK gte used here due to a bug in ganache where the timestamp can change
+ // between multiple calls to the same block. Which can move the amount in our case
+ // ref: https://github.com/trufflesuite/ganache-core/issues/111
+ expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
+ erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
+ );
+ expect(newBalances[takerAddress][wethContract.address]).to.be.bignumber.gte(
+ erc20Balances[takerAddress][wethContract.address].minus(beforeAuctionDetails.currentAmount),
+ );
+ });
+ it('maker fees on sellOrder are paid to the fee receipient', async () => {
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
+ makerFee: new BigNumber(1),
+ });
+ const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(txHash);
+ const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
+ erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
+ );
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(sellOrder.makerFee),
+ );
+ });
+ it('maker fees on buyOrder are paid to the fee receipient', async () => {
+ buyOrder = await buyerOrderFactory.newSignedOrderAsync({
+ makerFee: new BigNumber(1),
+ });
+ const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(txHash);
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
+ expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
+ erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
+ );
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].plus(buyOrder.makerFee),
+ );
+ });
+ it('should revert when auction expires', async () => {
+ auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2);
+ auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
+ expirationTimeSeconds: auctionEndTimeSeconds,
+ makerAssetData: extendMakerAssetData(
+ assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ auctionBeginTimeSeconds,
+ auctionBeginAmount,
+ ),
+ });
+ return expectTransactionFailedAsync(
+ dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ ),
+ RevertReason.AuctionExpired,
+ );
+ });
+ it('cannot be filled for less than the current price', async () => {
+ buyOrder = await buyerOrderFactory.newSignedOrderAsync({
+ makerAssetAmount: sellOrder.takerAssetAmount,
+ });
+ return expectTransactionFailedAsync(
+ dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ ),
+ RevertReason.AuctionInvalidAmount,
+ );
+ });
+ it('auction begin amount must be higher than final amount ', async () => {
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
+ takerAssetAmount: auctionBeginAmount.plus(1),
+ });
+ return expectTransactionFailedAsync(
+ dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ ),
+ RevertReason.AuctionInvalidAmount,
+ );
+ });
+ it('begin time is less than end time', async () => {
+ auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds);
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
+ expirationTimeSeconds: auctionEndTimeSeconds,
+ makerAssetData: extendMakerAssetData(
+ assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ auctionBeginTimeSeconds,
+ auctionBeginAmount,
+ ),
+ });
+ return expectTransactionFailedAsync(
+ dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ ),
+ RevertReason.AuctionInvalidBeginTime,
+ );
+ });
+ it('asset data contains auction parameters', async () => {
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
+ makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
+ });
+ return expectTransactionFailedAsync(
+ dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ ),
+ RevertReason.InvalidAssetData,
+ );
+ });
+ describe('ERC721', () => {
+ it('should match orders when ERC721', async () => {
+ const makerAssetId = erc721MakerAssetIds[0];
+ sellOrder = await sellerOrderFactory.newSignedOrderAsync({
+ makerAssetAmount: new BigNumber(1),
+ makerAssetData: extendMakerAssetData(
+ assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
+ auctionBeginTimeSeconds,
+ auctionBeginAmount,
+ ),
+ });
+ buyOrder = await buyerOrderFactory.newSignedOrderAsync({
+ takerAssetAmount: new BigNumber(1),
+ takerAssetData: sellOrder.makerAssetData,
+ });
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await dutchAuctionContract.matchOrders.sendTransactionAsync(
+ buyOrder,
+ sellOrder,
+ buyOrder.signature,
+ sellOrder.signature,
+ {
+ from: takerAddress,
+ },
+ ),
+ );
+ const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ // HACK gte used here due to a bug in ganache where the timestamp can change
+ // between multiple calls to the same block. Which can move the amount in our case
+ // ref: https://github.com/trufflesuite/ganache-core/issues/111
+ expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte(
+ erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount),
+ );
+ const newOwner = await erc721Token.ownerOf.callAsync(makerAssetId);
+ expect(newOwner).to.be.bignumber.equal(takerAddress);
+ });
+ });
+ });
+});
diff --git a/packages/contracts/test/extensions/forwarder.ts b/contracts/core/test/extensions/forwarder.ts
index c006be0fe..44ad4d6ff 100644
--- a/packages/contracts/test/extensions/forwarder.ts
+++ b/contracts/core/test/extensions/forwarder.ts
@@ -1,3 +1,16 @@
+import {
+ chaiSetup,
+ constants,
+ ContractName,
+ ERC20BalancesByOwner,
+ expectContractCreationFailedAsync,
+ expectTransactionFailedAsync,
+ OrderFactory,
+ provider,
+ sendTransactionResult,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { RevertReason, SignedOrder } from '@0x/types';
@@ -12,20 +25,10 @@ import { ExchangeContract } from '../../generated-wrappers/exchange';
import { ForwarderContract } from '../../generated-wrappers/forwarder';
import { WETH9Contract } from '../../generated-wrappers/weth9';
import { artifacts } from '../../src/artifacts';
-import {
- expectContractCreationFailedAsync,
- expectTransactionFailedAsync,
- sendTransactionResult,
-} from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ERC721Wrapper } from '../utils/erc721_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
import { ForwarderWrapper } from '../utils/forwarder_wrapper';
-import { OrderFactory } from '../utils/order_factory';
-import { ContractName, ERC20BalancesByOwner } from '../utils/types';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/packages/contracts/test/extensions/order_validator.ts b/contracts/core/test/extensions/order_validator.ts
index 37d7c4c5a..3dbe76f6e 100644
--- a/packages/contracts/test/extensions/order_validator.ts
+++ b/contracts/core/test/extensions/order_validator.ts
@@ -1,3 +1,12 @@
+import {
+ chaiSetup,
+ constants,
+ OrderFactory,
+ OrderStatus,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
@@ -12,14 +21,9 @@ import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy';
import { ExchangeContract } from '../../generated-wrappers/exchange';
import { OrderValidatorContract } from '../../generated-wrappers/order_validator';
import { artifacts } from '../../src/artifacts';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ERC721Wrapper } from '../utils/erc721_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
-import { OrderFactory } from '../utils/order_factory';
-import { OrderStatus } from '../utils/types';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/contracts/core/test/global_hooks.ts b/contracts/core/test/global_hooks.ts
new file mode 100644
index 000000000..f8ace376a
--- /dev/null
+++ b/contracts/core/test/global_hooks.ts
@@ -0,0 +1,17 @@
+import { env, EnvVars } from '@0x/dev-utils';
+
+import { coverage, profiler, provider } from '@0x/contracts-test-utils';
+before('start web3 provider', () => {
+ provider.start();
+});
+after('generate coverage report', async () => {
+ if (env.parseBoolean(EnvVars.SolidityCoverage)) {
+ const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
+ await coverageSubprovider.writeCoverageAsync();
+ }
+ if (env.parseBoolean(EnvVars.SolidityProfiler)) {
+ const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
+ await profilerSubprovider.writeProfilerOutputAsync();
+ }
+ provider.stop();
+});
diff --git a/packages/contracts/test/multisig/asset_proxy_owner.ts b/contracts/core/test/multisig/asset_proxy_owner.ts
index 087152316..daebfb7fb 100644
--- a/packages/contracts/test/multisig/asset_proxy_owner.ts
+++ b/contracts/core/test/multisig/asset_proxy_owner.ts
@@ -1,3 +1,16 @@
+import {
+ chaiSetup,
+ constants,
+ expectContractCallFailedAsync,
+ expectContractCreationFailedAsync,
+ expectTransactionFailedAsync,
+ expectTransactionFailedWithoutReasonAsync,
+ increaseTimeAndMineBlockAsync,
+ provider,
+ sendTransactionResult,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -14,18 +27,7 @@ import {
import { MixinAuthorizableContract } from '../../generated-wrappers/mixin_authorizable';
import { TestAssetProxyOwnerContract } from '../../generated-wrappers/test_asset_proxy_owner';
import { artifacts } from '../../src/artifacts';
-import {
- expectContractCallFailedAsync,
- expectContractCreationFailedAsync,
- expectTransactionFailedAsync,
- expectTransactionFailedWithoutReasonAsync,
- sendTransactionResult,
-} from '../utils/assertions';
-import { increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { MultiSigWrapper } from '../utils/multi_sig_wrapper';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
+import { AssetProxyOwnerWrapper } from '../utils/asset_proxy_owner_wrapper';
chaiSetup.configure();
const expect = chai.expect;
@@ -41,7 +43,7 @@ describe('AssetProxyOwner', () => {
let erc20Proxy: MixinAuthorizableContract;
let erc721Proxy: MixinAuthorizableContract;
let testAssetProxyOwner: TestAssetProxyOwnerContract;
- let multiSigWrapper: MultiSigWrapper;
+ let assetProxyOwnerWrapper: AssetProxyOwnerWrapper;
before(async () => {
await blockchainLifecycle.startAsync();
@@ -75,7 +77,7 @@ describe('AssetProxyOwner', () => {
REQUIRED_APPROVALS,
SECONDS_TIME_LOCKED,
);
- multiSigWrapper = new MultiSigWrapper(testAssetProxyOwner, provider);
+ assetProxyOwnerWrapper = new AssetProxyOwnerWrapper(testAssetProxyOwner, provider);
await web3Wrapper.awaitTransactionSuccessAsync(
await erc20Proxy.transferOwnership.sendTransactionAsync(testAssetProxyOwner.address, {
from: initialOwner,
@@ -172,7 +174,7 @@ describe('AssetProxyOwner', () => {
addressToRegister,
isRegistered,
);
- const submitTxRes = await multiSigWrapper.submitTransactionAsync(
+ const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync(
testAssetProxyOwner.address,
registerAssetProxyData,
owners[0],
@@ -181,10 +183,10 @@ describe('AssetProxyOwner', () => {
const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
- await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]);
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
- const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]);
+ const executeTxRes = await assetProxyOwnerWrapper.executeTransactionAsync(txId, owners[0]);
const registerLog = executeTxRes.logs[0] as LogWithDecodedArgs<
AssetProxyOwnerAssetProxyRegistrationEventArgs
>;
@@ -204,7 +206,7 @@ describe('AssetProxyOwner', () => {
addressToRegister,
isRegistered,
);
- const submitTxRes = await multiSigWrapper.submitTransactionAsync(
+ const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync(
testAssetProxyOwner.address,
registerAssetProxyData,
owners[0],
@@ -212,10 +214,10 @@ describe('AssetProxyOwner', () => {
const log = submitTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
- await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]);
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
- const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]);
+ const executeTxRes = await assetProxyOwnerWrapper.executeTransactionAsync(txId, owners[0]);
const failureLog = executeTxRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerExecutionFailureEventArgs>;
expect(failureLog.args.transactionId).to.be.bignumber.equal(txId);
@@ -237,7 +239,7 @@ describe('AssetProxyOwner', () => {
addressToRegister,
isRegistered,
);
- const registerAssetProxySubmitRes = await multiSigWrapper.submitTransactionAsync(
+ const registerAssetProxySubmitRes = await assetProxyOwnerWrapper.submitTransactionAsync(
testAssetProxyOwner.address,
registerAssetProxyData,
owners[0],
@@ -247,12 +249,12 @@ describe('AssetProxyOwner', () => {
>;
const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(authorized);
- const erc20AddAuthorizedAddressSubmitRes = await multiSigWrapper.submitTransactionAsync(
+ const erc20AddAuthorizedAddressSubmitRes = await assetProxyOwnerWrapper.submitTransactionAsync(
erc20Proxy.address,
addAuthorizedAddressData,
owners[0],
);
- const erc721AddAuthorizedAddressSubmitRes = await multiSigWrapper.submitTransactionAsync(
+ const erc721AddAuthorizedAddressSubmitRes = await assetProxyOwnerWrapper.submitTransactionAsync(
erc721Proxy.address,
addAuthorizedAddressData,
owners[0],
@@ -267,15 +269,15 @@ describe('AssetProxyOwner', () => {
const erc20AddAuthorizedAddressTxId = erc20AddAuthorizedAddressSubmitLog.args.transactionId;
const erc721AddAuthorizedAddressTxId = erc721AddAuthorizedAddressSubmitLog.args.transactionId;
- await multiSigWrapper.confirmTransactionAsync(registerAssetProxyTxId, owners[1]);
- await multiSigWrapper.confirmTransactionAsync(erc20AddAuthorizedAddressTxId, owners[1]);
- await multiSigWrapper.confirmTransactionAsync(erc721AddAuthorizedAddressTxId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(registerAssetProxyTxId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(erc20AddAuthorizedAddressTxId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(erc721AddAuthorizedAddressTxId, owners[1]);
await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber());
- await multiSigWrapper.executeTransactionAsync(registerAssetProxyTxId, owners[0]);
- await multiSigWrapper.executeTransactionAsync(erc20AddAuthorizedAddressTxId, owners[0], {
+ await assetProxyOwnerWrapper.executeTransactionAsync(registerAssetProxyTxId, owners[0]);
+ await assetProxyOwnerWrapper.executeTransactionAsync(erc20AddAuthorizedAddressTxId, owners[0], {
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
});
- await multiSigWrapper.executeTransactionAsync(erc721AddAuthorizedAddressTxId, owners[0], {
+ await assetProxyOwnerWrapper.executeTransactionAsync(erc721AddAuthorizedAddressTxId, owners[0], {
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
});
});
@@ -285,7 +287,7 @@ describe('AssetProxyOwner', () => {
const notRemoveAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(
authorized,
);
- const submitTxRes = await multiSigWrapper.submitTransactionAsync(
+ const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync(
erc20Proxy.address,
notRemoveAuthorizedAddressData,
owners[0],
@@ -303,7 +305,7 @@ describe('AssetProxyOwner', () => {
authorized,
erc20Index,
);
- const submitTxRes = await multiSigWrapper.submitTransactionAsync(
+ const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync(
erc20Proxy.address,
removeAuthorizedAddressAtIndexData,
owners[0],
@@ -321,7 +323,7 @@ describe('AssetProxyOwner', () => {
authorized,
erc721Index,
);
- const submitTxRes = await multiSigWrapper.submitTransactionAsync(
+ const submitTxRes = await assetProxyOwnerWrapper.submitTransactionAsync(
erc721Proxy.address,
removeAuthorizedAddressAtIndexData,
owners[0],
@@ -341,7 +343,7 @@ describe('AssetProxyOwner', () => {
authorized,
erc20Index,
);
- const res = await multiSigWrapper.submitTransactionAsync(
+ const res = await assetProxyOwnerWrapper.submitTransactionAsync(
erc20Proxy.address,
removeAuthorizedAddressAtIndexData,
owners[0],
@@ -362,7 +364,7 @@ describe('AssetProxyOwner', () => {
authorized,
erc721Index,
);
- const res = await multiSigWrapper.submitTransactionAsync(
+ const res = await assetProxyOwnerWrapper.submitTransactionAsync(
erc721Proxy.address,
removeAuthorizedAddressAtIndexData,
owners[0],
@@ -370,7 +372,7 @@ describe('AssetProxyOwner', () => {
const log = res.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
- await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]);
return expectTransactionFailedAsync(
testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, {
@@ -385,7 +387,7 @@ describe('AssetProxyOwner', () => {
const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(
newAuthorized,
);
- const res = await multiSigWrapper.submitTransactionAsync(
+ const res = await assetProxyOwnerWrapper.submitTransactionAsync(
erc20Proxy.address,
addAuthorizedAddressData,
owners[0],
@@ -393,7 +395,7 @@ describe('AssetProxyOwner', () => {
const log = res.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = log.args.transactionId;
- await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]);
return expectTransactionFailedAsync(
testAssetProxyOwner.executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, {
@@ -411,7 +413,7 @@ describe('AssetProxyOwner', () => {
authorized,
erc20Index,
);
- const submitRes = await multiSigWrapper.submitTransactionAsync(
+ const submitRes = await assetProxyOwnerWrapper.submitTransactionAsync(
erc20Proxy.address,
removeAuthorizedAddressAtIndexData,
owners[0],
@@ -419,9 +421,12 @@ describe('AssetProxyOwner', () => {
const submitLog = submitRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = submitLog.args.transactionId;
- await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]);
- const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, owners[0]);
+ const execRes = await assetProxyOwnerWrapper.executeRemoveAuthorizedAddressAtIndexAsync(
+ txId,
+ owners[0],
+ );
const execLog = execRes.logs[1] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
@@ -441,7 +446,7 @@ describe('AssetProxyOwner', () => {
authorized,
erc20Index,
);
- const submitRes = await multiSigWrapper.submitTransactionAsync(
+ const submitRes = await assetProxyOwnerWrapper.submitTransactionAsync(
erc20Proxy.address,
removeAuthorizedAddressAtIndexData,
owners[0],
@@ -449,9 +454,9 @@ describe('AssetProxyOwner', () => {
const submitLog = submitRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = submitLog.args.transactionId;
- await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]);
- const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, notOwner);
+ const execRes = await assetProxyOwnerWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, notOwner);
const execLog = execRes.logs[1] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
@@ -468,7 +473,7 @@ describe('AssetProxyOwner', () => {
authorized,
erc20Index,
);
- const submitRes = await multiSigWrapper.submitTransactionAsync(
+ const submitRes = await assetProxyOwnerWrapper.submitTransactionAsync(
erc20Proxy.address,
removeAuthorizedAddressAtIndexData,
owners[0],
@@ -476,9 +481,12 @@ describe('AssetProxyOwner', () => {
const submitLog = submitRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerSubmissionEventArgs>;
const txId = submitLog.args.transactionId;
- await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await assetProxyOwnerWrapper.confirmTransactionAsync(txId, owners[1]);
- const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, owners[0]);
+ const execRes = await assetProxyOwnerWrapper.executeRemoveAuthorizedAddressAtIndexAsync(
+ txId,
+ owners[0],
+ );
const execLog = execRes.logs[1] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
@@ -495,4 +503,4 @@ describe('AssetProxyOwner', () => {
});
});
});
-// tslint:enable:no-unnecessary-type-assertion
+// tslint:disable-line max-file-line-count
diff --git a/packages/contracts/test/tokens/erc721_token.ts b/contracts/core/test/tokens/erc721_token.ts
index 72407748f..3b0a5f001 100644
--- a/packages/contracts/test/tokens/erc721_token.ts
+++ b/contracts/core/test/tokens/erc721_token.ts
@@ -1,3 +1,13 @@
+import {
+ chaiSetup,
+ constants,
+ expectTransactionFailedAsync,
+ expectTransactionFailedWithoutReasonAsync,
+ LogDecoder,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -14,11 +24,6 @@ import {
} from '../../generated-wrappers/dummy_erc721_token';
import { InvalidERC721ReceiverContract } from '../../generated-wrappers/invalid_erc721_receiver';
import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { LogDecoder } from '../utils/log_decoder';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
@@ -53,7 +58,7 @@ describe('ERC721Token', () => {
provider,
txDefaults,
);
- logDecoder = new LogDecoder(web3Wrapper);
+ logDecoder = new LogDecoder(web3Wrapper, artifacts);
await web3Wrapper.awaitTransactionSuccessAsync(
await token.mint.sendTransactionAsync(owner, tokenId, { from: owner }),
constants.AWAIT_TRANSACTION_MINED_MS,
diff --git a/packages/contracts/test/tokens/unlimited_allowance_token.ts b/contracts/core/test/tokens/unlimited_allowance_token.ts
index ea5a50522..c3e4072c5 100644
--- a/packages/contracts/test/tokens/unlimited_allowance_token.ts
+++ b/contracts/core/test/tokens/unlimited_allowance_token.ts
@@ -1,3 +1,11 @@
+import {
+ chaiSetup,
+ constants,
+ expectContractCallFailedAsync,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -5,10 +13,6 @@ import * as chai from 'chai';
import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token';
import { artifacts } from '../../src/artifacts';
-import { expectContractCallFailedAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/packages/contracts/test/tokens/weth9.ts b/contracts/core/test/tokens/weth9.ts
index 9a31dc3f2..225481904 100644
--- a/packages/contracts/test/tokens/weth9.ts
+++ b/contracts/core/test/tokens/weth9.ts
@@ -1,3 +1,12 @@
+import {
+ chaiSetup,
+ constants,
+ expectInsufficientFundsAsync,
+ expectTransactionFailedWithoutReasonAsync,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
@@ -5,10 +14,6 @@ import * as chai from 'chai';
import { WETH9Contract } from '../../generated-wrappers/weth9';
import { artifacts } from '../../src/artifacts';
-import { expectInsufficientFundsAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/packages/contracts/test/tokens/zrx_token.ts b/contracts/core/test/tokens/zrx_token.ts
index cab62c205..6bc5e164c 100644
--- a/packages/contracts/test/tokens/zrx_token.ts
+++ b/contracts/core/test/tokens/zrx_token.ts
@@ -1,3 +1,4 @@
+import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
@@ -5,9 +6,6 @@ import * as chai from 'chai';
import { ZRXTokenContract } from '../../generated-wrappers/zrx_token';
import { artifacts } from '../../src/artifacts';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/packages/contracts/test/tutorials/arbitrage.ts b/contracts/core/test/tutorials/arbitrage.ts
index 78e0bc238..78e0bc238 100644
--- a/packages/contracts/test/tutorials/arbitrage.ts
+++ b/contracts/core/test/tutorials/arbitrage.ts
diff --git a/contracts/core/test/utils/asset_proxy_owner_wrapper.ts b/contracts/core/test/utils/asset_proxy_owner_wrapper.ts
new file mode 100644
index 000000000..d5aaaf519
--- /dev/null
+++ b/contracts/core/test/utils/asset_proxy_owner_wrapper.ts
@@ -0,0 +1,69 @@
+import { LogDecoder } from '@0x/contracts-test-utils';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
+import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { AssetProxyOwnerContract } from '../../generated-wrappers/asset_proxy_owner';
+import { artifacts } from '../../src/artifacts';
+
+export class AssetProxyOwnerWrapper {
+ private readonly _assetProxyOwner: AssetProxyOwnerContract;
+ private readonly _web3Wrapper: Web3Wrapper;
+ private readonly _logDecoder: LogDecoder;
+ constructor(assetproxyOwnerContract: AssetProxyOwnerContract, provider: Provider) {
+ this._assetProxyOwner = assetproxyOwnerContract;
+ this._web3Wrapper = new Web3Wrapper(provider);
+ this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
+ }
+ public async submitTransactionAsync(
+ destination: string,
+ data: string,
+ from: string,
+ opts: { value?: BigNumber } = {},
+ ): Promise<TransactionReceiptWithDecodedLogs> {
+ const value = _.isUndefined(opts.value) ? new BigNumber(0) : opts.value;
+ const txHash = await this._assetProxyOwner.submitTransaction.sendTransactionAsync(destination, value, data, {
+ from,
+ });
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
+ public async confirmTransactionAsync(txId: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
+ const txHash = await this._assetProxyOwner.confirmTransaction.sendTransactionAsync(txId, { from });
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
+ public async revokeConfirmationAsync(txId: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
+ const txHash = await this._assetProxyOwner.revokeConfirmation.sendTransactionAsync(txId, { from });
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
+ public async executeTransactionAsync(
+ txId: BigNumber,
+ from: string,
+ opts: { gas?: number } = {},
+ ): Promise<TransactionReceiptWithDecodedLogs> {
+ const txHash = await this._assetProxyOwner.executeTransaction.sendTransactionAsync(txId, {
+ from,
+ gas: opts.gas,
+ });
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
+ public async executeRemoveAuthorizedAddressAtIndexAsync(
+ txId: BigNumber,
+ from: string,
+ ): Promise<TransactionReceiptWithDecodedLogs> {
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ const txHash = await (this
+ ._assetProxyOwner as AssetProxyOwnerContract).executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(
+ txId,
+ {
+ from,
+ },
+ );
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
+}
diff --git a/packages/contracts/test/utils/asset_wrapper.ts b/contracts/core/test/utils/asset_wrapper.ts
index 4e7696066..e4090ad74 100644
--- a/packages/contracts/test/utils/asset_wrapper.ts
+++ b/contracts/core/test/utils/asset_wrapper.ts
@@ -1,10 +1,9 @@
+import { AbstractAssetWrapper, constants } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { AssetProxyId } from '@0x/types';
import { BigNumber, errorUtils } from '@0x/utils';
import * as _ from 'lodash';
-import { AbstractAssetWrapper } from './abstract_asset_wrapper';
-import { constants } from './constants';
import { ERC20Wrapper } from './erc20_wrapper';
import { ERC721Wrapper } from './erc721_wrapper';
diff --git a/packages/contracts/test/utils/erc20_wrapper.ts b/contracts/core/test/utils/erc20_wrapper.ts
index c281a2abf..d6210646c 100644
--- a/packages/contracts/test/utils/erc20_wrapper.ts
+++ b/contracts/core/test/utils/erc20_wrapper.ts
@@ -1,3 +1,4 @@
+import { constants, ERC20BalancesByOwner, txDefaults } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
@@ -8,10 +9,6 @@ import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_to
import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy';
import { artifacts } from '../../src/artifacts';
-import { constants } from './constants';
-import { ERC20BalancesByOwner } from './types';
-import { txDefaults } from './web3_wrapper';
-
export class ERC20Wrapper {
private readonly _tokenOwnerAddresses: string[];
private readonly _contractOwnerAddress: string;
diff --git a/packages/contracts/test/utils/erc721_wrapper.ts b/contracts/core/test/utils/erc721_wrapper.ts
index 3ef4e701d..b5ae34e60 100644
--- a/packages/contracts/test/utils/erc721_wrapper.ts
+++ b/contracts/core/test/utils/erc721_wrapper.ts
@@ -1,3 +1,4 @@
+import { constants, ERC721TokenIdsByOwner, txDefaults } from '@0x/contracts-test-utils';
import { generatePseudoRandomSalt } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
@@ -8,10 +9,6 @@ import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_
import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy';
import { artifacts } from '../../src/artifacts';
-import { constants } from './constants';
-import { ERC721TokenIdsByOwner } from './types';
-import { txDefaults } from './web3_wrapper';
-
export class ERC721Wrapper {
private readonly _tokenOwnerAddresses: string[];
private readonly _contractOwnerAddress: string;
@@ -29,7 +26,8 @@ export class ERC721Wrapper {
this._contractOwnerAddress = contractOwnerAddress;
}
public async deployDummyTokensAsync(): Promise<DummyERC721TokenContract[]> {
- for (let i = 0; i < constants.NUM_DUMMY_ERC721_TO_DEPLOY; i++) {
+ // tslint:disable-next-line:no-unused-variable
+ for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) {
this._dummyTokenContracts.push(
await DummyERC721TokenContract.deployFrom0xArtifactAsync(
artifacts.DummyERC721Token,
@@ -61,7 +59,8 @@ export class ERC721Wrapper {
this._initialTokenIdsByOwner = {};
for (const dummyTokenContract of this._dummyTokenContracts) {
for (const tokenOwnerAddress of this._tokenOwnerAddresses) {
- for (let i = 0; i < constants.NUM_ERC721_TOKENS_TO_MINT; i++) {
+ // tslint:disable-next-line:no-unused-variable
+ for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) {
const tokenId = generatePseudoRandomSalt();
await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress);
if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress])) {
diff --git a/packages/contracts/test/utils/exchange_wrapper.ts b/contracts/core/test/utils/exchange_wrapper.ts
index c28989d3f..2a24b880f 100644
--- a/packages/contracts/test/utils/exchange_wrapper.ts
+++ b/contracts/core/test/utils/exchange_wrapper.ts
@@ -1,14 +1,18 @@
+import {
+ FillResults,
+ formatters,
+ LogDecoder,
+ OrderInfo,
+ orderUtils,
+ SignedTransaction,
+} from '@0x/contracts-test-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { ExchangeContract } from '../../generated-wrappers/exchange';
-
-import { formatters } from './formatters';
-import { LogDecoder } from './log_decoder';
-import { orderUtils } from './order_utils';
-import { FillResults, OrderInfo, SignedTransaction } from './types';
+import { artifacts } from '../../src/artifacts';
export class ExchangeWrapper {
private readonly _exchange: ExchangeContract;
@@ -17,7 +21,7 @@ export class ExchangeWrapper {
constructor(exchangeContract: ExchangeContract, provider: Provider) {
this._exchange = exchangeContract;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper);
+ this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
}
public async fillOrderAsync(
signedOrder: SignedOrder,
diff --git a/packages/contracts/test/utils/fill_order_combinatorial_utils.ts b/contracts/core/test/utils/fill_order_combinatorial_utils.ts
index 8046771f9..5d0ea07a8 100644
--- a/packages/contracts/test/utils/fill_order_combinatorial_utils.ts
+++ b/contracts/core/test/utils/fill_order_combinatorial_utils.ts
@@ -1,3 +1,21 @@
+import { artifacts as libsArtifacts, TestLibsContract } from '@0x/contracts-libs';
+import {
+ AllowanceAmountScenario,
+ AssetDataScenario,
+ BalanceAmountScenario,
+ chaiSetup,
+ constants,
+ expectTransactionFailedAsync,
+ ExpirationTimeSecondsScenario,
+ FeeRecipientAddressScenario,
+ FillScenario,
+ OrderAssetAmountScenario,
+ orderUtils,
+ signingUtils,
+ TakerAssetFillAmountScenario,
+ TakerScenario,
+ TraderStateScenario,
+} from '@0x/contracts-test-utils';
import {
assetDataUtils,
BalanceAndProxyAllowanceLazyStore,
@@ -15,33 +33,15 @@ import * as _ from 'lodash';
import 'make-promises-safe';
import { ExchangeContract, ExchangeFillEventArgs } from '../../generated-wrappers/exchange';
-import { TestLibsContract } from '../../generated-wrappers/test_libs';
import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync } from './assertions';
import { AssetWrapper } from './asset_wrapper';
-import { chaiSetup } from './chai_setup';
-import { constants } from './constants';
import { ERC20Wrapper } from './erc20_wrapper';
import { ERC721Wrapper } from './erc721_wrapper';
import { ExchangeWrapper } from './exchange_wrapper';
import { OrderFactoryFromScenario } from './order_factory_from_scenario';
-import { orderUtils } from './order_utils';
-import { signingUtils } from './signing_utils';
import { SimpleAssetBalanceAndProxyAllowanceFetcher } from './simple_asset_balance_and_proxy_allowance_fetcher';
import { SimpleOrderFilledCancelledFetcher } from './simple_order_filled_cancelled_fetcher';
-import {
- AllowanceAmountScenario,
- AssetDataScenario,
- BalanceAmountScenario,
- ExpirationTimeSecondsScenario,
- FeeRecipientAddressScenario,
- FillScenario,
- OrderAssetAmountScenario,
- TakerAssetFillAmountScenario,
- TakerScenario,
- TraderStateScenario,
-} from './types';
chaiSetup.configure();
const expect = chai.expect;
@@ -131,7 +131,11 @@ export async function fillOrderCombinatorialUtilsFactoryAsync(
exchangeContract.address,
);
- const testLibsContract = await TestLibsContract.deployFrom0xArtifactAsync(artifacts.TestLibs, provider, txDefaults);
+ const testLibsContract = await TestLibsContract.deployFrom0xArtifactAsync(
+ libsArtifacts.TestLibs,
+ provider,
+ txDefaults,
+ );
const fillOrderCombinatorialUtils = new FillOrderCombinatorialUtils(
orderFactory,
diff --git a/packages/contracts/test/utils/forwarder_wrapper.ts b/contracts/core/test/utils/forwarder_wrapper.ts
index a0bfcfe1d..9c5560381 100644
--- a/packages/contracts/test/utils/forwarder_wrapper.ts
+++ b/contracts/core/test/utils/forwarder_wrapper.ts
@@ -1,3 +1,4 @@
+import { constants, formatters, LogDecoder, MarketSellOrders } from '@0x/contracts-test-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
@@ -5,11 +6,7 @@ import { Provider, TransactionReceiptWithDecodedLogs, TxDataPayable } from 'ethe
import * as _ from 'lodash';
import { ForwarderContract } from '../../generated-wrappers/forwarder';
-
-import { constants } from './constants';
-import { formatters } from './formatters';
-import { LogDecoder } from './log_decoder';
-import { MarketSellOrders } from './types';
+import { artifacts } from '../../src/artifacts';
export class ForwarderWrapper {
private readonly _web3Wrapper: Web3Wrapper;
@@ -61,7 +58,7 @@ export class ForwarderWrapper {
constructor(contractInstance: ForwarderContract, provider: Provider) {
this._forwarderContract = contractInstance;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper);
+ this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
}
public async marketSellOrdersWithEthAsync(
orders: SignedOrder[],
diff --git a/packages/contracts/test/utils/match_order_tester.ts b/contracts/core/test/utils/match_order_tester.ts
index 6c2c84959..8f574704e 100644
--- a/packages/contracts/test/utils/match_order_tester.ts
+++ b/contracts/core/test/utils/match_order_tester.ts
@@ -1,3 +1,12 @@
+import {
+ chaiSetup,
+ ERC20BalancesByOwner,
+ ERC721TokenIdsByOwner,
+ OrderInfo,
+ OrderStatus,
+ TransferAmountsByMatchOrders as TransferAmounts,
+ TransferAmountsLoggedByMatchOrders as LoggedTransferAmounts,
+} from '@0x/contracts-test-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { AssetProxyId, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -6,18 +15,9 @@ import * as _ from 'lodash';
import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types';
-import { chaiSetup } from './chai_setup';
import { ERC20Wrapper } from './erc20_wrapper';
import { ERC721Wrapper } from './erc721_wrapper';
import { ExchangeWrapper } from './exchange_wrapper';
-import {
- ERC20BalancesByOwner,
- ERC721TokenIdsByOwner,
- OrderInfo,
- OrderStatus,
- TransferAmountsByMatchOrders as TransferAmounts,
- TransferAmountsLoggedByMatchOrders as LoggedTransferAmounts,
-} from './types';
chaiSetup.configure();
const expect = chai.expect;
@@ -270,18 +270,14 @@ export class MatchOrderTester {
const leftExpectedStatus = expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker)
? OrderStatus.FULLY_FILLED
: OrderStatus.FILLABLE;
- expect(leftOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for left order').to.be.equal(
- leftExpectedStatus,
- );
+ expect(leftOrderInfo.orderStatus, 'Checking exchange status for left order').to.be.equal(leftExpectedStatus);
// Assert right order status
const maxAmountBoughtByRightMaker = signedOrderRight.takerAssetAmount.minus(initialRightOrderFilledAmount);
const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight);
const rightExpectedStatus = expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker)
? OrderStatus.FULLY_FILLED
: OrderStatus.FILLABLE;
- expect(rightOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for right order').to.be.equal(
- rightExpectedStatus,
- );
+ expect(rightOrderInfo.orderStatus, 'Checking exchange status for right order').to.be.equal(rightExpectedStatus);
}
/// @dev Asserts account balances after matching orders.
/// @param signedOrderLeft First matched order.
diff --git a/packages/contracts/test/utils/order_factory_from_scenario.ts b/contracts/core/test/utils/order_factory_from_scenario.ts
index 60c8606c4..1cc962020 100644
--- a/packages/contracts/test/utils/order_factory_from_scenario.ts
+++ b/contracts/core/test/utils/order_factory_from_scenario.ts
@@ -1,19 +1,18 @@
-import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
-import { Order } from '@0x/types';
-import { BigNumber, errorUtils } from '@0x/utils';
-
-import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token';
-
-import { constants } from './constants';
import {
AssetDataScenario,
+ constants,
ERC721TokenIdsByOwner,
ExpirationTimeSecondsScenario,
FeeRecipientAddressScenario,
OrderAssetAmountScenario,
OrderScenario,
TakerScenario,
-} from './types';
+} from '@0x/contracts-test-utils';
+import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
+import { Order } from '@0x/types';
+import { BigNumber, errorUtils } from '@0x/utils';
+
+import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token';
const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10_000_000_000_000_000_000);
const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(5_000_000_000_000_000_000);
diff --git a/packages/contracts/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts b/contracts/core/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts
index 64b7dedbe..64b7dedbe 100644
--- a/packages/contracts/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts
+++ b/contracts/core/test/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts
diff --git a/packages/contracts/test/utils/simple_order_filled_cancelled_fetcher.ts b/contracts/core/test/utils/simple_order_filled_cancelled_fetcher.ts
index af959e00e..af959e00e 100644
--- a/packages/contracts/test/utils/simple_order_filled_cancelled_fetcher.ts
+++ b/contracts/core/test/utils/simple_order_filled_cancelled_fetcher.ts
diff --git a/packages/contracts/tsconfig.json b/contracts/core/tsconfig.json
index 8b29365cc..f2f3c4e97 100644
--- a/packages/contracts/tsconfig.json
+++ b/contracts/core/tsconfig.json
@@ -13,6 +13,7 @@
"./generated-artifacts/DummyERC721Token.json",
"./generated-artifacts/DummyMultipleReturnERC20Token.json",
"./generated-artifacts/DummyNoReturnERC20Token.json",
+ "./generated-artifacts/DutchAuction.json",
"./generated-artifacts/ERC20Proxy.json",
"./generated-artifacts/ERC20Token.json",
"./generated-artifacts/ERC721Proxy.json",
@@ -26,16 +27,12 @@
"./generated-artifacts/IWallet.json",
"./generated-artifacts/InvalidERC721Receiver.json",
"./generated-artifacts/MixinAuthorizable.json",
- "./generated-artifacts/MultiSigWallet.json",
- "./generated-artifacts/MultiSigWalletWithTimeLock.json",
+ "./generated-artifacts/MultiAssetProxy.json",
"./generated-artifacts/OrderValidator.json",
"./generated-artifacts/ReentrantERC20Token.json",
"./generated-artifacts/TestAssetProxyDispatcher.json",
"./generated-artifacts/TestAssetProxyOwner.json",
- "./generated-artifacts/TestConstants.json",
"./generated-artifacts/TestExchangeInternals.json",
- "./generated-artifacts/TestLibBytes.json",
- "./generated-artifacts/TestLibs.json",
"./generated-artifacts/TestSignatureValidator.json",
"./generated-artifacts/TestStaticCallReceiver.json",
"./generated-artifacts/Validator.json",
diff --git a/packages/contracts/tslint.json b/contracts/core/tslint.json
index 1bb3ac2a2..1bb3ac2a2 100644
--- a/packages/contracts/tslint.json
+++ b/contracts/core/tslint.json
diff --git a/contracts/libs/.solhint.json b/contracts/libs/.solhint.json
new file mode 100644
index 000000000..076afe9f3
--- /dev/null
+++ b/contracts/libs/.solhint.json
@@ -0,0 +1,20 @@
+{
+ "extends": "default",
+ "rules": {
+ "avoid-low-level-calls": false,
+ "avoid-tx-origin": "warn",
+ "bracket-align": false,
+ "code-complexity": false,
+ "const-name-snakecase": "error",
+ "expression-indent": "error",
+ "function-max-lines": false,
+ "func-order": "error",
+ "indent": ["error", 4],
+ "max-line-length": ["warn", 160],
+ "no-inline-assembly": false,
+ "quotes": ["error", "double"],
+ "separate-by-one-line-in-contract": "error",
+ "space-after-comma": "error",
+ "statement-indent": "error"
+ }
+}
diff --git a/contracts/libs/README.md b/contracts/libs/README.md
new file mode 100644
index 000000000..66eedf6be
--- /dev/null
+++ b/contracts/libs/README.md
@@ -0,0 +1,70 @@
+## Contracts libs
+
+Smart contracts libs used in the 0x protocol.
+
+## Usage
+
+Contracts can be found in the [contracts](./contracts) directory. The contents of this directory are broken down into the following subdirectories:
+
+* [libs](./contracts/protocol)
+ * This directory contains the libs.
+* [test](./contracts/test)
+ * This directory contains mocks and other contracts that are used solely for testing contracts within the other directories.
+
+## Contributing
+
+We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
+
+For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install Dependencies
+
+If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
+
+```bash
+yarn config set workspaces-experimental true
+```
+
+Then install dependencies
+
+```bash
+yarn install
+```
+
+### Build
+
+To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
+
+```bash
+PKG=@0x/contracts-libs yarn build
+```
+
+Or continuously rebuild on change:
+
+```bash
+PKG=@0x/contracts-libs yarn watch
+```
+
+### Clean
+
+```bash
+yarn clean
+```
+
+### Lint
+
+```bash
+yarn lint
+```
+
+### Run Tests
+
+```bash
+yarn test
+```
+
+#### Testing options
+
+Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
diff --git a/contracts/libs/compiler.json b/contracts/libs/compiler.json
new file mode 100644
index 000000000..cf7c52a73
--- /dev/null
+++ b/contracts/libs/compiler.json
@@ -0,0 +1,22 @@
+{
+ "artifactsDir": "./generated-artifacts",
+ "contractsDir": "./contracts",
+ "compilerSettings": {
+ "optimizer": {
+ "enabled": true,
+ "runs": 1000000
+ },
+ "outputSelection": {
+ "*": {
+ "*": [
+ "abi",
+ "evm.bytecode.object",
+ "evm.bytecode.sourceMap",
+ "evm.deployedBytecode.object",
+ "evm.deployedBytecode.sourceMap"
+ ]
+ }
+ }
+ },
+ "contracts": ["TestLibs", "LibOrder", "LibMath", "LibFillResults", "LibAbiEncoder", "LibEIP712"]
+}
diff --git a/packages/contracts/contracts/protocol/Exchange/libs/LibAbiEncoder.sol b/contracts/libs/contracts/libs/LibAbiEncoder.sol
index 4aad37709..4aad37709 100644
--- a/packages/contracts/contracts/protocol/Exchange/libs/LibAbiEncoder.sol
+++ b/contracts/libs/contracts/libs/LibAbiEncoder.sol
diff --git a/packages/contracts/contracts/protocol/AssetProxy/libs/LibAssetProxyErrors.sol b/contracts/libs/contracts/libs/LibAssetProxyErrors.sol
index 1d9a70cc1..1d9a70cc1 100644
--- a/packages/contracts/contracts/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
+++ b/contracts/libs/contracts/libs/LibAssetProxyErrors.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/libs/LibConstants.sol b/contracts/libs/contracts/libs/LibConstants.sol
index 8d2732cd3..8d2732cd3 100644
--- a/packages/contracts/contracts/protocol/Exchange/libs/LibConstants.sol
+++ b/contracts/libs/contracts/libs/LibConstants.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/libs/LibEIP712.sol b/contracts/libs/contracts/libs/LibEIP712.sol
index 203edc1fd..203edc1fd 100644
--- a/packages/contracts/contracts/protocol/Exchange/libs/LibEIP712.sol
+++ b/contracts/libs/contracts/libs/LibEIP712.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/libs/LibExchangeErrors.sol b/contracts/libs/contracts/libs/LibExchangeErrors.sol
index a0f75bc06..a0f75bc06 100644
--- a/packages/contracts/contracts/protocol/Exchange/libs/LibExchangeErrors.sol
+++ b/contracts/libs/contracts/libs/LibExchangeErrors.sol
diff --git a/packages/contracts/contracts/protocol/Exchange/libs/LibFillResults.sol b/contracts/libs/contracts/libs/LibFillResults.sol
index 659ae9a69..fbd9950bf 100644
--- a/packages/contracts/contracts/protocol/Exchange/libs/LibFillResults.sol
+++ b/contracts/libs/contracts/libs/LibFillResults.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../../utils/SafeMath/SafeMath.sol";
+import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol";
contract LibFillResults is
diff --git a/packages/contracts/contracts/protocol/Exchange/libs/LibMath.sol b/contracts/libs/contracts/libs/LibMath.sol
index c0b85ea10..b24876a9c 100644
--- a/packages/contracts/contracts/protocol/Exchange/libs/LibMath.sol
+++ b/contracts/libs/contracts/libs/LibMath.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../../utils/SafeMath/SafeMath.sol";
+import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol";
contract LibMath is
diff --git a/packages/contracts/contracts/protocol/Exchange/libs/LibOrder.sol b/contracts/libs/contracts/libs/LibOrder.sol
index 0fe7c2161..0fe7c2161 100644
--- a/packages/contracts/contracts/protocol/Exchange/libs/LibOrder.sol
+++ b/contracts/libs/contracts/libs/LibOrder.sol
diff --git a/packages/contracts/contracts/test/TestLibs/TestLibs.sol b/contracts/libs/contracts/test/TestLibs/TestLibs.sol
index a10f981fc..bd5f9f9da 100644
--- a/packages/contracts/contracts/test/TestLibs/TestLibs.sol
+++ b/contracts/libs/contracts/test/TestLibs/TestLibs.sol
@@ -19,10 +19,10 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../../protocol/Exchange/libs/LibMath.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
-import "../../protocol/Exchange/libs/LibFillResults.sol";
-import "../../protocol/Exchange/libs/LibAbiEncoder.sol";
+import "../../libs/LibMath.sol";
+import "../../libs/LibOrder.sol";
+import "../../libs/LibFillResults.sol";
+import "../../libs/LibAbiEncoder.sol";
contract TestLibs is
diff --git a/contracts/libs/package.json b/contracts/libs/package.json
new file mode 100644
index 000000000..74288be76
--- /dev/null
+++ b/contracts/libs/package.json
@@ -0,0 +1,92 @@
+{
+ "private": true,
+ "name": "@0x/contracts-libs",
+ "version": "1.0.0",
+ "engines": {
+ "node": ">=6.12"
+ },
+ "description": "Smart contract libs of 0x protocol",
+ "main": "lib/src/index.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "build": "yarn pre_build && tsc -b",
+ "build:ci": "yarn build",
+ "pre_build": "run-s compile generate_contract_wrappers",
+ "test": "yarn run_mocha",
+ "rebuild_and_test": "run-s build test",
+ "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
+ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
+ "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
+ "run_mocha":
+ "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
+ "compile": "sol-compiler --contracts-dir contracts",
+ "clean": "shx rm -rf lib generated-artifacts generated-wrappers",
+ "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
+ "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
+ "coverage:report:text": "istanbul report text",
+ "coverage:report:html": "istanbul report html && open coverage/index.html",
+ "profiler:report:html": "istanbul report html && open coverage/index.html",
+ "coverage:report:lcov": "istanbul report lcov",
+ "test:circleci": "yarn test",
+ "lint-contracts": "solhint contracts/**/**/**/**/*.sol"
+ },
+ "config": {
+ "abis": "generated-artifacts/@(LibMath|LibOrder|LibFillResults|LibAbiEncoder|TestLibs|LibEIP712).json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x-monorepo.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x-monorepo/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/libs/README.md",
+ "devDependencies": {
+ "@0x/contracts-test-utils": "^1.0.0",
+ "@0x/abi-gen": "^1.0.17",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/sol-compiler": "^1.1.14",
+ "@0x/sol-cov": "^2.1.14",
+ "@0x/subproviders": "^2.1.6",
+ "@0x/tslint-config": "^1.0.10",
+ "@types/bn.js": "^4.11.0",
+ "@types/lodash": "4.14.104",
+ "@types/node": "*",
+ "@types/yargs": "^10.0.0",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-bignumber": "^2.0.1",
+ "dirty-chai": "^2.0.1",
+ "make-promises-safe": "^1.1.0",
+ "ethereumjs-abi": "0.6.5",
+ "mocha": "^4.1.0",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "solc": "^0.4.24",
+ "solhint": "^1.2.1",
+ "tslint": "5.11.0",
+ "typescript": "3.0.1",
+ "yargs": "^10.0.3"
+ },
+ "dependencies": {
+ "@0x/base-contract": "^3.0.8",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/contracts-multisig": "^1.0.0",
+ "@0x/contracts-utils": "^1.0.0",
+ "@0x/types": "^1.3.0",
+ "@0x/typescript-typings": "^3.0.4",
+ "@0x/utils": "^2.0.6",
+ "@0x/web3-wrapper": "^3.1.6",
+ "@types/js-combinatorics": "^0.5.29",
+ "bn.js": "^4.11.8",
+ "ethereum-types": "^1.1.2",
+ "ethereumjs-util": "^5.1.1",
+ "lodash": "^4.17.5"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/contracts/libs/src/artifacts/index.ts b/contracts/libs/src/artifacts/index.ts
new file mode 100644
index 000000000..3955bbe2b
--- /dev/null
+++ b/contracts/libs/src/artifacts/index.ts
@@ -0,0 +1,17 @@
+import { ContractArtifact } from 'ethereum-types';
+
+import * as LibAbiEncoder from '../../generated-artifacts/LibAbiEncoder.json';
+import * as LibEIP721 from '../../generated-artifacts/LibEIP712.json';
+import * as LibFillResults from '../../generated-artifacts/LibFillResults.json';
+import * as LibMath from '../../generated-artifacts/LibMath.json';
+import * as LibOrder from '../../generated-artifacts/LibOrder.json';
+import * as TestLibs from '../../generated-artifacts/TestLibs.json';
+
+export const artifacts = {
+ TestLibs: TestLibs as ContractArtifact,
+ LibAbiEncoder: LibAbiEncoder as ContractArtifact,
+ LibFillResults: LibFillResults as ContractArtifact,
+ LibMath: LibMath as ContractArtifact,
+ LibOrder: LibOrder as ContractArtifact,
+ LibEIP721: LibEIP721 as ContractArtifact,
+};
diff --git a/contracts/libs/src/index.ts b/contracts/libs/src/index.ts
new file mode 100644
index 000000000..d55f08ea2
--- /dev/null
+++ b/contracts/libs/src/index.ts
@@ -0,0 +1,2 @@
+export * from './artifacts';
+export * from './wrappers';
diff --git a/contracts/libs/src/wrappers/index.ts b/contracts/libs/src/wrappers/index.ts
new file mode 100644
index 000000000..baaae6e34
--- /dev/null
+++ b/contracts/libs/src/wrappers/index.ts
@@ -0,0 +1,6 @@
+export * from '../../generated-wrappers/test_libs';
+export * from '../../generated-wrappers/lib_abi_encoder';
+export * from '../../generated-wrappers/lib_fill_results';
+export * from '../../generated-wrappers/lib_math';
+export * from '../../generated-wrappers/lib_order';
+export * from '../../generated-wrappers/lib_e_i_p712';
diff --git a/packages/contracts/test/exchange/libs.ts b/contracts/libs/test/exchange/libs.ts
index 503ef0e0f..44ff6a844 100644
--- a/packages/contracts/test/exchange/libs.ts
+++ b/contracts/libs/test/exchange/libs.ts
@@ -1,17 +1,20 @@
+import {
+ addressUtils,
+ chaiSetup,
+ constants,
+ OrderFactory,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
-import { TestConstantsContract } from '../../generated-wrappers/test_constants';
import { TestLibsContract } from '../../generated-wrappers/test_libs';
import { artifacts } from '../../src/artifacts';
-import { addressUtils } from '../utils/address_utils';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { OrderFactory } from '../utils/order_factory';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
@@ -22,7 +25,6 @@ describe('Exchange libs', () => {
let signedOrder: SignedOrder;
let orderFactory: OrderFactory;
let libs: TestLibsContract;
- let testConstants: TestConstantsContract;
before(async () => {
await blockchainLifecycle.startAsync();
@@ -34,11 +36,6 @@ describe('Exchange libs', () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const makerAddress = accounts[0];
libs = await TestLibsContract.deployFrom0xArtifactAsync(artifacts.TestLibs, provider, txDefaults);
- testConstants = await TestConstantsContract.deployFrom0xArtifactAsync(
- artifacts.TestConstants,
- provider,
- txDefaults,
- );
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
@@ -58,15 +55,6 @@ describe('Exchange libs', () => {
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
-
- describe('LibConstants', () => {
- describe('ZRX_ASSET_DATA', () => {
- it('should have the correct ZRX_ASSET_DATA', async () => {
- const isValid = await testConstants.assertValidZrxAssetData.callAsync();
- expect(isValid).to.be.equal(true);
- });
- });
- });
// Note(albrow): These tests are designed to be supplemental to the
// combinatorial tests in test/exchange/internal. They test specific edge
// cases that are not covered by the combinatorial tests.
diff --git a/contracts/libs/test/global_hooks.ts b/contracts/libs/test/global_hooks.ts
new file mode 100644
index 000000000..f8ace376a
--- /dev/null
+++ b/contracts/libs/test/global_hooks.ts
@@ -0,0 +1,17 @@
+import { env, EnvVars } from '@0x/dev-utils';
+
+import { coverage, profiler, provider } from '@0x/contracts-test-utils';
+before('start web3 provider', () => {
+ provider.start();
+});
+after('generate coverage report', async () => {
+ if (env.parseBoolean(EnvVars.SolidityCoverage)) {
+ const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
+ await coverageSubprovider.writeCoverageAsync();
+ }
+ if (env.parseBoolean(EnvVars.SolidityProfiler)) {
+ const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
+ await profilerSubprovider.writeProfilerOutputAsync();
+ }
+ provider.stop();
+});
diff --git a/contracts/libs/tsconfig.json b/contracts/libs/tsconfig.json
new file mode 100644
index 000000000..27ca35085
--- /dev/null
+++ b/contracts/libs/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "rootDir": ".",
+ "resolveJsonModule": true
+ },
+ "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
+ "files": [
+ "./generated-artifacts/TestLibs.json",
+ "./generated-artifacts/LibOrder.json",
+ "./generated-artifacts/LibFillResults.json",
+ "./generated-artifacts/LibAbiEncoder.json",
+ "./generated-artifacts/LibEIP712.json",
+ "./generated-artifacts/LibMath.json"
+ ],
+ "exclude": ["./deploy/solc/solc_bin"]
+}
diff --git a/contracts/libs/tslint.json b/contracts/libs/tslint.json
new file mode 100644
index 000000000..1bb3ac2a2
--- /dev/null
+++ b/contracts/libs/tslint.json
@@ -0,0 +1,6 @@
+{
+ "extends": ["@0x/tslint-config"],
+ "rules": {
+ "custom-no-magic-numbers": false
+ }
+}
diff --git a/contracts/multisig/.solhint.json b/contracts/multisig/.solhint.json
new file mode 100644
index 000000000..076afe9f3
--- /dev/null
+++ b/contracts/multisig/.solhint.json
@@ -0,0 +1,20 @@
+{
+ "extends": "default",
+ "rules": {
+ "avoid-low-level-calls": false,
+ "avoid-tx-origin": "warn",
+ "bracket-align": false,
+ "code-complexity": false,
+ "const-name-snakecase": "error",
+ "expression-indent": "error",
+ "function-max-lines": false,
+ "func-order": "error",
+ "indent": ["error", 4],
+ "max-line-length": ["warn", 160],
+ "no-inline-assembly": false,
+ "quotes": ["error", "double"],
+ "separate-by-one-line-in-contract": "error",
+ "space-after-comma": "error",
+ "statement-indent": "error"
+ }
+}
diff --git a/contracts/multisig/CHANGELOG.json b/contracts/multisig/CHANGELOG.json
new file mode 100644
index 000000000..fe51488c7
--- /dev/null
+++ b/contracts/multisig/CHANGELOG.json
@@ -0,0 +1 @@
+[]
diff --git a/contracts/multisig/README.md b/contracts/multisig/README.md
new file mode 100644
index 000000000..93db63b5b
--- /dev/null
+++ b/contracts/multisig/README.md
@@ -0,0 +1,70 @@
+## MultisSig Contracts
+
+MultiSig smart contracts
+
+## Usage
+
+Contracts can be found in the [contracts](./contracts) directory. The contents of this directory are broken down into the following subdirectories:
+
+* [multisig](./contracts/multisig)
+ * This directory contains the [Gnosis MultiSigWallet](https://github.com/gnosis/MultiSigWallet) and a custom extension that adds a timelock to transactions within the MultiSigWallet.
+* [test](./contracts/test)
+ * This directory contains mocks and other contracts that are used solely for testing contracts within the other directories.
+
+## Contributing
+
+We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
+
+For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install Dependencies
+
+If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
+
+```bash
+yarn config set workspaces-experimental true
+```
+
+Then install dependencies
+
+```bash
+yarn install
+```
+
+### Build
+
+To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
+
+```bash
+PKG=@0x/contracts-multisig yarn build
+```
+
+Or continuously rebuild on change:
+
+```bash
+PKG=@0x/contracts-multisig yarn watch
+```
+
+### Clean
+
+```bash
+yarn clean
+```
+
+### Lint
+
+```bash
+yarn lint
+```
+
+### Run Tests
+
+```bash
+yarn test
+```
+
+#### Testing options
+
+Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
diff --git a/contracts/multisig/compiler.json b/contracts/multisig/compiler.json
new file mode 100644
index 000000000..5a1f689e2
--- /dev/null
+++ b/contracts/multisig/compiler.json
@@ -0,0 +1,22 @@
+{
+ "artifactsDir": "./generated-artifacts",
+ "contractsDir": "./contracts",
+ "compilerSettings": {
+ "optimizer": {
+ "enabled": true,
+ "runs": 1000000
+ },
+ "outputSelection": {
+ "*": {
+ "*": [
+ "abi",
+ "evm.bytecode.object",
+ "evm.bytecode.sourceMap",
+ "evm.deployedBytecode.object",
+ "evm.deployedBytecode.sourceMap"
+ ]
+ }
+ }
+ },
+ "contracts": ["MultiSigWallet", "MultiSigWalletWithTimeLock", "TestRejectEther"]
+}
diff --git a/packages/contracts/contracts/multisig/MultiSigWallet.sol b/contracts/multisig/contracts/multisig/MultiSigWallet.sol
index 516e7391c..516e7391c 100644
--- a/packages/contracts/contracts/multisig/MultiSigWallet.sol
+++ b/contracts/multisig/contracts/multisig/MultiSigWallet.sol
diff --git a/packages/contracts/contracts/multisig/MultiSigWalletWithTimeLock.sol b/contracts/multisig/contracts/multisig/MultiSigWalletWithTimeLock.sol
index 9513d3b30..9513d3b30 100644
--- a/packages/contracts/contracts/multisig/MultiSigWalletWithTimeLock.sol
+++ b/contracts/multisig/contracts/multisig/MultiSigWalletWithTimeLock.sol
diff --git a/contracts/multisig/contracts/test/TestRejectEther/TestRejectEther.sol b/contracts/multisig/contracts/test/TestRejectEther/TestRejectEther.sol
new file mode 100644
index 000000000..e523f591d
--- /dev/null
+++ b/contracts/multisig/contracts/test/TestRejectEther/TestRejectEther.sol
@@ -0,0 +1,23 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+
+// solhint-disable no-empty-blocks
+contract TestRejectEther {}
diff --git a/contracts/multisig/package.json b/contracts/multisig/package.json
new file mode 100644
index 000000000..37d064fef
--- /dev/null
+++ b/contracts/multisig/package.json
@@ -0,0 +1,86 @@
+{
+ "private": true,
+ "name": "@0x/contracts-multisig",
+ "version": "1.0.0",
+ "engines": {
+ "node": ">=6.12"
+ },
+ "description": "Multisig contracts used by 0x protocol",
+ "main": "lib/src/index.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "build": "yarn pre_build && tsc -b",
+ "build:ci": "yarn build",
+ "pre_build": "run-s compile generate_contract_wrappers",
+ "test": "yarn run_mocha",
+ "rebuild_and_test": "run-s build test",
+ "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
+ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
+ "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
+ "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
+ "compile": "sol-compiler --contracts-dir contracts",
+ "clean": "shx rm -rf lib generated-artifacts generated-wrappers",
+ "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../packages/abi-gen-templates/contract.handlebars --partials '../../packages/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
+ "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
+ "coverage:report:text": "istanbul report text",
+ "coverage:report:html": "istanbul report html && open coverage/index.html",
+ "profiler:report:html": "istanbul report html && open coverage/index.html",
+ "coverage:report:lcov": "istanbul report lcov",
+ "test:circleci": "yarn test",
+ "lint-contracts": "solhint contracts/**/**/**/**/*.sol"
+ },
+ "config": {
+ "abis": "generated-artifacts/@(MultiSigWallet|MultiSigWalletWithTimeLock|TestRejectEther).json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x-monorepo.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x-monorepo/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/core/README.md",
+ "devDependencies": {
+ "@0x/contracts-test-utils": "^1.0.0",
+ "@0x/abi-gen": "^1.0.17",
+ "@0x/dev-utils": "^1.0.18",
+ "@0x/sol-compiler": "^1.1.13",
+ "@0x/sol-cov": "^2.1.13",
+ "@0x/subproviders": "^2.1.5",
+ "@0x/tslint-config": "^1.0.10",
+ "@types/bn.js": "^4.11.0",
+ "@types/ethereumjs-abi": "^0.6.0",
+ "@types/lodash": "4.14.104",
+ "@types/node": "*",
+ "@types/yargs": "^10.0.0",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-bignumber": "^2.0.1",
+ "dirty-chai": "^2.0.1",
+ "make-promises-safe": "^1.1.0",
+ "mocha": "^4.1.0",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "solc": "^0.4.24",
+ "solhint": "^1.2.1",
+ "tslint": "5.11.0",
+ "typescript": "3.0.1",
+ "yargs": "^10.0.3"
+ },
+ "dependencies": {
+ "@0x/base-contract": "^3.0.7",
+ "@0x/order-utils": "^3.0.3",
+ "@0x/types": "^1.3.0",
+ "@0x/typescript-typings": "^3.0.4",
+ "@0x/utils": "^2.0.6",
+ "@0x/web3-wrapper": "^3.1.5",
+ "ethereum-types": "^1.1.2",
+ "lodash": "^4.17.5"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/contracts/multisig/src/artifacts/index.ts b/contracts/multisig/src/artifacts/index.ts
new file mode 100644
index 000000000..7cf47be01
--- /dev/null
+++ b/contracts/multisig/src/artifacts/index.ts
@@ -0,0 +1,11 @@
+import { ContractArtifact } from 'ethereum-types';
+
+import * as MultiSigWallet from '../../generated-artifacts/MultiSigWallet.json';
+import * as MultiSigWalletWithTimeLock from '../../generated-artifacts/MultiSigWalletWithTimeLock.json';
+import * as TestRejectEther from '../../generated-artifacts/TestRejectEther.json';
+
+export const artifacts = {
+ TestRejectEther: TestRejectEther as ContractArtifact,
+ MultiSigWallet: MultiSigWallet as ContractArtifact,
+ MultiSigWalletWithTimeLock: MultiSigWalletWithTimeLock as ContractArtifact,
+};
diff --git a/contracts/multisig/src/wrappers/index.ts b/contracts/multisig/src/wrappers/index.ts
new file mode 100644
index 000000000..69abd62f2
--- /dev/null
+++ b/contracts/multisig/src/wrappers/index.ts
@@ -0,0 +1,2 @@
+export * from '../../generated-wrappers/multi_sig_wallet';
+export * from '../../generated-wrappers/multi_sig_wallet_with_time_lock';
diff --git a/contracts/multisig/test/global_hooks.ts b/contracts/multisig/test/global_hooks.ts
new file mode 100644
index 000000000..68eb4f8d5
--- /dev/null
+++ b/contracts/multisig/test/global_hooks.ts
@@ -0,0 +1,19 @@
+import { env, EnvVars } from '@0x/dev-utils';
+
+import { coverage, profiler, provider } from '@0x/contracts-test-utils';
+
+before('start web3 provider engine', () => {
+ provider.start();
+});
+
+after('generate coverage report', async () => {
+ if (env.parseBoolean(EnvVars.SolidityCoverage)) {
+ const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
+ await coverageSubprovider.writeCoverageAsync();
+ }
+ if (env.parseBoolean(EnvVars.SolidityProfiler)) {
+ const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
+ await profilerSubprovider.writeProfilerOutputAsync();
+ }
+ provider.stop();
+});
diff --git a/packages/contracts/test/multisig/multi_sig_with_time_lock.ts b/contracts/multisig/test/multi_sig_with_time_lock.ts
index 1c0cb0515..31c215505 100644
--- a/packages/contracts/test/multisig/multi_sig_with_time_lock.ts
+++ b/contracts/multisig/test/multi_sig_with_time_lock.ts
@@ -1,3 +1,13 @@
+import {
+ chaiSetup,
+ constants,
+ expectTransactionFailedAsync,
+ expectTransactionFailedWithoutReasonAsync,
+ increaseTimeAndMineBlockAsync,
+ provider,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { RevertReason } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -5,7 +15,6 @@ import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
-import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token';
import {
MultiSigWalletWithTimeLockConfirmationEventArgs,
MultiSigWalletWithTimeLockConfirmationTimeSetEventArgs,
@@ -13,14 +22,11 @@ import {
MultiSigWalletWithTimeLockExecutionEventArgs,
MultiSigWalletWithTimeLockExecutionFailureEventArgs,
MultiSigWalletWithTimeLockSubmissionEventArgs,
-} from '../../generated-wrappers/multi_sig_wallet_with_time_lock';
-import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
-import { increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { MultiSigWrapper } from '../utils/multi_sig_wrapper';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
+} from '../generated-wrappers/multi_sig_wallet_with_time_lock';
+import { TestRejectEtherContract } from '../generated-wrappers/test_reject_ether';
+import { artifacts } from '../src/artifacts';
+
+import { MultiSigWrapper } from './utils/multi_sig_wrapper';
chaiSetup.configure();
const expect = chai.expect;
@@ -189,14 +195,10 @@ describe('MultiSigWalletWithTimeLock', () => {
await expectTransactionFailedWithoutReasonAsync(multiSigWrapper.executeTransactionAsync(txId, owners[1]));
});
it("should log an ExecutionFailure event and not update the transaction's execution state if unsuccessful", async () => {
- const contractWithoutFallback = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
- artifacts.DummyERC20Token,
+ const contractWithoutFallback = await TestRejectEtherContract.deployFrom0xArtifactAsync(
+ artifacts.TestRejectEther,
provider,
txDefaults,
- constants.DUMMY_TOKEN_NAME,
- constants.DUMMY_TOKEN_SYMBOL,
- constants.DUMMY_TOKEN_DECIMALS,
- constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
const data = constants.NULL_BYTES;
const value = new BigNumber(10);
diff --git a/packages/contracts/test/utils/multi_sig_wrapper.ts b/contracts/multisig/test/utils/multi_sig_wrapper.ts
index 74fd3b4d6..086143613 100644
--- a/packages/contracts/test/utils/multi_sig_wrapper.ts
+++ b/contracts/multisig/test/utils/multi_sig_wrapper.ts
@@ -1,12 +1,11 @@
+import { LogDecoder } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
-import { AssetProxyOwnerContract } from '../../generated-wrappers/asset_proxy_owner';
import { MultiSigWalletContract } from '../../generated-wrappers/multi_sig_wallet';
-
-import { LogDecoder } from './log_decoder';
+import { artifacts } from '../../src/artifacts';
export class MultiSigWrapper {
private readonly _multiSig: MultiSigWalletContract;
@@ -15,7 +14,7 @@ export class MultiSigWrapper {
constructor(multiSigContract: MultiSigWalletContract, provider: Provider) {
this._multiSig = multiSigContract;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper);
+ this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts);
}
public async submitTransactionAsync(
destination: string,
@@ -52,16 +51,4 @@ export class MultiSigWrapper {
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
}
- public async executeRemoveAuthorizedAddressAtIndexAsync(
- txId: BigNumber,
- from: string,
- ): Promise<TransactionReceiptWithDecodedLogs> {
- // tslint:disable-next-line:no-unnecessary-type-assertion
- const txHash = await (this
- ._multiSig as AssetProxyOwnerContract).executeRemoveAuthorizedAddressAtIndex.sendTransactionAsync(txId, {
- from,
- });
- const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
- return tx;
- }
}
diff --git a/contracts/multisig/tsconfig.json b/contracts/multisig/tsconfig.json
new file mode 100644
index 000000000..6f381620e
--- /dev/null
+++ b/contracts/multisig/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "rootDir": ".",
+ "resolveJsonModule": true
+ },
+ "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
+ "files": [
+ "./generated-artifacts/MultiSigWallet.json",
+ "./generated-artifacts/MultiSigWalletWithTimeLock.json",
+ "./generated-artifacts/TestRejectEther.json"
+ ],
+ "exclude": ["./deploy/solc/solc_bin"]
+}
diff --git a/contracts/multisig/tslint.json b/contracts/multisig/tslint.json
new file mode 100644
index 000000000..1bb3ac2a2
--- /dev/null
+++ b/contracts/multisig/tslint.json
@@ -0,0 +1,6 @@
+{
+ "extends": ["@0x/tslint-config"],
+ "rules": {
+ "custom-no-magic-numbers": false
+ }
+}
diff --git a/contracts/test-utils/README.md b/contracts/test-utils/README.md
new file mode 100644
index 000000000..73fd93f45
--- /dev/null
+++ b/contracts/test-utils/README.md
@@ -0,0 +1,73 @@
+## Contracts test utils
+
+This package contains test utilities used by other smart contracts packages.
+
+## Usage
+
+```typescript
+import {
+ chaiSetup,
+ constants,
+ expectContractCallFailedAsync,
+ expectContractCreationFailedAsync,
+ expectTransactionFailedAsync,
+ expectTransactionFailedWithoutReasonAsync,
+ increaseTimeAndMineBlockAsync,
+ provider,
+ sendTransactionResult,
+ txDefaults,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
+```
+
+## Contributing
+
+We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install Dependencies
+
+If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
+
+```bash
+yarn config set workspaces-experimental true
+```
+
+Then install dependencies
+
+```bash
+yarn install
+```
+
+### Build
+
+To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
+
+```bash
+PKG=@0x/contracts-test-utils yarn build
+```
+
+Or continuously rebuild on change:
+
+```bash
+PKG=@0x/contracts-test-utils yarn watch
+```
+
+### Clean
+
+```bash
+yarn clean
+```
+
+### Lint
+
+```bash
+yarn lint
+```
+
+### Run Tests
+
+```bash
+yarn test
+```
diff --git a/contracts/test-utils/package.json b/contracts/test-utils/package.json
new file mode 100644
index 000000000..513cfdc10
--- /dev/null
+++ b/contracts/test-utils/package.json
@@ -0,0 +1,75 @@
+{
+ "name": "@0x/contracts-test-utils",
+ "version": "1.0.0",
+ "engines": {
+ "node": ">=6.12"
+ },
+ "description": "Test utils for 0x contracts",
+ "main": "lib/src/index.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "build": "tsc -b",
+ "build:ci": "yarn build",
+ "test": "yarn run_mocha",
+ "test:coverage": "run-s build run_mocha coverage:report:text coverage:report:lcov",
+ "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
+ "clean": "shx rm -rf lib",
+ "lint": "tslint --format stylish --project tsconfig.lint.json",
+ "coverage:report:text": "istanbul report text",
+ "coverage:report:html": "istanbul report html && open coverage/index.html",
+ "profiler:report:html": "istanbul report html && open coverage/index.html",
+ "coverage:report:lcov": "istanbul report lcov",
+ "test:circleci": "yarn test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x-monorepo.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x-monorepo/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/test-utils/README.md",
+ "devDependencies": {
+ "mocha": "^4.1.0",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "tslint": "5.11.0",
+ "typescript": "3.0.1"
+ },
+ "dependencies": {
+ "@0x/abi-gen": "^1.0.17",
+ "@0x/dev-utils": "^1.0.18",
+ "@0x/sol-compiler": "^1.1.13",
+ "@0x/subproviders": "^2.1.5",
+ "@0x/tslint-config": "^1.0.10",
+ "@types/bn.js": "^4.11.0",
+ "@types/ethereumjs-abi": "^0.6.0",
+ "@types/lodash": "4.14.104",
+ "@types/node": "*",
+ "chai": "^4.0.1",
+ "chai-bignumber": "^2.0.1",
+ "dirty-chai": "^2.0.1",
+ "make-promises-safe": "^1.1.0",
+ "@0x/order-utils": "^3.0.3",
+ "@0x/types": "^1.3.0",
+ "@0x/typescript-typings": "^3.0.4",
+ "@0x/utils": "^2.0.6",
+ "@0x/sol-cov": "^2.1.13",
+ "@0x/web3-wrapper": "^3.1.5",
+ "@types/js-combinatorics": "^0.5.29",
+ "chai-as-promised": "^7.1.0",
+ "bn.js": "^4.11.8",
+ "ethereum-types": "^1.1.2",
+ "ethereumjs-abi": "0.6.5",
+ "ethereumjs-util": "^5.1.1",
+ "ethers": "~4.0.4",
+ "js-combinatorics": "^0.5.3",
+ "lodash": "^4.17.5"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/contracts/test/utils/abstract_asset_wrapper.ts b/contracts/test-utils/src/abstract_asset_wrapper.ts
index 4b56a8502..4b56a8502 100644
--- a/packages/contracts/test/utils/abstract_asset_wrapper.ts
+++ b/contracts/test-utils/src/abstract_asset_wrapper.ts
diff --git a/packages/contracts/test/utils/address_utils.ts b/contracts/test-utils/src/address_utils.ts
index 634da0c16..634da0c16 100644
--- a/packages/contracts/test/utils/address_utils.ts
+++ b/contracts/test-utils/src/address_utils.ts
diff --git a/packages/contracts/test/utils/assertions.ts b/contracts/test-utils/src/assertions.ts
index 5b1cedfcc..5b1cedfcc 100644
--- a/packages/contracts/test/utils/assertions.ts
+++ b/contracts/test-utils/src/assertions.ts
diff --git a/packages/contracts/test/utils/block_timestamp.ts b/contracts/test-utils/src/block_timestamp.ts
index 66c13eed1..66c13eed1 100644
--- a/packages/contracts/test/utils/block_timestamp.ts
+++ b/contracts/test-utils/src/block_timestamp.ts
diff --git a/packages/contracts/test/utils/chai_setup.ts b/contracts/test-utils/src/chai_setup.ts
index 1a8733093..1a8733093 100644
--- a/packages/contracts/test/utils/chai_setup.ts
+++ b/contracts/test-utils/src/chai_setup.ts
diff --git a/packages/contracts/test/utils/combinatorial_utils.ts b/contracts/test-utils/src/combinatorial_utils.ts
index bb1b55b4d..bb1b55b4d 100644
--- a/packages/contracts/test/utils/combinatorial_utils.ts
+++ b/contracts/test-utils/src/combinatorial_utils.ts
diff --git a/packages/contracts/test/utils/constants.ts b/contracts/test-utils/src/constants.ts
index cd21330e9..d2c3ab512 100644
--- a/packages/contracts/test/utils/constants.ts
+++ b/contracts/test-utils/src/constants.ts
@@ -35,7 +35,7 @@ export const constants = {
DUMMY_TOKEN_TOTAL_SUPPLY: new BigNumber(0),
NULL_BYTES: '0x',
NUM_DUMMY_ERC20_TO_DEPLOY: 3,
- NUM_DUMMY_ERC721_TO_DEPLOY: 1,
+ NUM_DUMMY_ERC721_TO_DEPLOY: 2,
NUM_ERC721_TOKENS_TO_MINT: 2,
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
diff --git a/packages/contracts/test/utils/coverage.ts b/contracts/test-utils/src/coverage.ts
index 5becfa1b6..5becfa1b6 100644
--- a/packages/contracts/test/utils/coverage.ts
+++ b/contracts/test-utils/src/coverage.ts
diff --git a/packages/contracts/test/utils/formatters.ts b/contracts/test-utils/src/formatters.ts
index 813eb45db..813eb45db 100644
--- a/packages/contracts/test/utils/formatters.ts
+++ b/contracts/test-utils/src/formatters.ts
diff --git a/packages/contracts/test/global_hooks.ts b/contracts/test-utils/src/global_hooks.ts
index 2e9ac9e21..307dd0777 100644
--- a/packages/contracts/test/global_hooks.ts
+++ b/contracts/test-utils/src/global_hooks.ts
@@ -1,7 +1,7 @@
import { env, EnvVars } from '@0x/dev-utils';
-import { coverage } from './utils/coverage';
-import { profiler } from './utils/profiler';
+import { coverage } from './coverage';
+import { profiler } from './profiler';
after('generate coverage report', async () => {
if (env.parseBoolean(EnvVars.SolidityCoverage)) {
diff --git a/contracts/test-utils/src/index.ts b/contracts/test-utils/src/index.ts
new file mode 100644
index 000000000..7880de0bf
--- /dev/null
+++ b/contracts/test-utils/src/index.ts
@@ -0,0 +1,55 @@
+export { AbstractAssetWrapper } from './abstract_asset_wrapper';
+export { chaiSetup } from './chai_setup';
+export { constants } from './constants';
+export {
+ expectContractCallFailedAsync,
+ expectContractCallFailedWithoutReasonAsync,
+ expectContractCreationFailedAsync,
+ expectContractCreationFailedWithoutReasonAsync,
+ expectInsufficientFundsAsync,
+ expectTransactionFailedAsync,
+ sendTransactionResult,
+ expectTransactionFailedWithoutReasonAsync,
+ getInvalidOpcodeErrorMessageForCallAsync,
+ getRevertReasonOrErrorMessageForSendTransactionAsync,
+} from './assertions';
+export { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from './block_timestamp';
+export { provider, txDefaults, web3Wrapper } from './web3_wrapper';
+export { LogDecoder } from './log_decoder';
+export { formatters } from './formatters';
+export { signingUtils } from './signing_utils';
+export { orderUtils } from './order_utils';
+export { typeEncodingUtils } from './type_encoding_utils';
+export { profiler } from './profiler';
+export { coverage } from './coverage';
+export { addressUtils } from './address_utils';
+export { OrderFactory } from './order_factory';
+export { bytes32Values, testCombinatoriallyWithReferenceFuncAsync, uint256Values } from './combinatorial_utils';
+export { TransactionFactory } from './transaction_factory';
+export { testWithReferenceFuncAsync } from './test_with_reference';
+export {
+ MarketBuyOrders,
+ MarketSellOrders,
+ ERC721TokenIdsByOwner,
+ SignedTransaction,
+ OrderStatus,
+ AllowanceAmountScenario,
+ AssetDataScenario,
+ BalanceAmountScenario,
+ ContractName,
+ ExpirationTimeSecondsScenario,
+ TransferAmountsLoggedByMatchOrders,
+ TransferAmountsByMatchOrders,
+ OrderScenario,
+ TraderStateScenario,
+ TransactionDataParams,
+ Token,
+ FillScenario,
+ FeeRecipientAddressScenario,
+ OrderAssetAmountScenario,
+ TakerAssetFillAmountScenario,
+ TakerScenario,
+ OrderInfo,
+ ERC20BalancesByOwner,
+ FillResults,
+} from './types';
diff --git a/packages/contracts/test/utils/log_decoder.ts b/contracts/test-utils/src/log_decoder.ts
index 05b0a9204..54666ea5f 100644
--- a/packages/contracts/test/utils/log_decoder.ts
+++ b/contracts/test-utils/src/log_decoder.ts
@@ -11,8 +11,6 @@ import {
} from 'ethereum-types';
import * as _ from 'lodash';
-import { artifacts } from '../../src/artifacts';
-
import { constants } from './constants';
export class LogDecoder {
@@ -27,7 +25,7 @@ export class LogDecoder {
}
}
}
- constructor(web3Wrapper: Web3Wrapper) {
+ constructor(web3Wrapper: Web3Wrapper, artifacts: { [contractName: string]: ContractArtifact }) {
this._web3Wrapper = web3Wrapper;
const abiArrays: AbiDefinition[][] = [];
_.forEach(artifacts, (artifact: ContractArtifact) => {
diff --git a/packages/contracts/test/utils/order_factory.ts b/contracts/test-utils/src/order_factory.ts
index 2449d1a8a..2449d1a8a 100644
--- a/packages/contracts/test/utils/order_factory.ts
+++ b/contracts/test-utils/src/order_factory.ts
diff --git a/packages/contracts/test/utils/order_utils.ts b/contracts/test-utils/src/order_utils.ts
index 4f7a34011..4f7a34011 100644
--- a/packages/contracts/test/utils/order_utils.ts
+++ b/contracts/test-utils/src/order_utils.ts
diff --git a/packages/contracts/test/utils/profiler.ts b/contracts/test-utils/src/profiler.ts
index 2c7c1d66c..2c7c1d66c 100644
--- a/packages/contracts/test/utils/profiler.ts
+++ b/contracts/test-utils/src/profiler.ts
diff --git a/packages/contracts/test/utils/revert_trace.ts b/contracts/test-utils/src/revert_trace.ts
index 3f74fd28b..3f74fd28b 100644
--- a/packages/contracts/test/utils/revert_trace.ts
+++ b/contracts/test-utils/src/revert_trace.ts
diff --git a/packages/contracts/test/utils/signing_utils.ts b/contracts/test-utils/src/signing_utils.ts
index 21f864bfa..21f864bfa 100644
--- a/packages/contracts/test/utils/signing_utils.ts
+++ b/contracts/test-utils/src/signing_utils.ts
diff --git a/packages/contracts/test/utils/test_with_reference.ts b/contracts/test-utils/src/test_with_reference.ts
index b80be4a6c..b80be4a6c 100644
--- a/packages/contracts/test/utils/test_with_reference.ts
+++ b/contracts/test-utils/src/test_with_reference.ts
diff --git a/packages/contracts/test/utils/transaction_factory.ts b/contracts/test-utils/src/transaction_factory.ts
index dbab3ade4..dbab3ade4 100644
--- a/packages/contracts/test/utils/transaction_factory.ts
+++ b/contracts/test-utils/src/transaction_factory.ts
diff --git a/packages/contracts/test/utils/type_encoding_utils.ts b/contracts/test-utils/src/type_encoding_utils.ts
index bfd9c9ef5..bfd9c9ef5 100644
--- a/packages/contracts/test/utils/type_encoding_utils.ts
+++ b/contracts/test-utils/src/type_encoding_utils.ts
diff --git a/packages/contracts/test/utils/types.ts b/contracts/test-utils/src/types.ts
index 9fc9e1570..d738fcd4e 100644
--- a/packages/contracts/test/utils/types.ts
+++ b/contracts/test-utils/src/types.ts
@@ -86,6 +86,7 @@ export enum ContractName {
ZRXToken = 'ZRXToken',
DummyERC20Token = 'DummyERC20Token',
EtherToken = 'WETH9',
+ DutchAuction = 'DutchAuction',
AssetProxyOwner = 'AssetProxyOwner',
AccountLevels = 'AccountLevels',
EtherDelta = 'EtherDelta',
diff --git a/packages/contracts/test/utils/web3_wrapper.ts b/contracts/test-utils/src/web3_wrapper.ts
index f7b1a732a..cb33476f3 100644
--- a/packages/contracts/test/utils/web3_wrapper.ts
+++ b/contracts/test-utils/src/web3_wrapper.ts
@@ -48,6 +48,7 @@ const ganacheConfigs = {
const providerConfigs = testProvider === ProviderType.Ganache ? ganacheConfigs : gethConfigs;
export const provider: Web3ProviderEngine = web3Factory.getRpcProvider(providerConfigs);
+provider.stop();
const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
const isProfilerEnabled = env.parseBoolean(EnvVars.SolidityProfiler);
const isRevertTraceEnabled = env.parseBoolean(EnvVars.SolidityRevertTrace);
diff --git a/packages/contracts/test/utils_test/test_with_reference.ts b/contracts/test-utils/test/test_with_reference.ts
index 8d633cd1f..1c1211003 100644
--- a/packages/contracts/test/utils_test/test_with_reference.ts
+++ b/contracts/test-utils/test/test_with_reference.ts
@@ -1,7 +1,7 @@
import * as chai from 'chai';
-import { chaiSetup } from '../utils/chai_setup';
-import { testWithReferenceFuncAsync } from '../utils/test_with_reference';
+import { chaiSetup } from '../src/chai_setup';
+import { testWithReferenceFuncAsync } from '../src/test_with_reference';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/contracts/test-utils/tsconfig.json b/contracts/test-utils/tsconfig.json
new file mode 100644
index 000000000..e35816553
--- /dev/null
+++ b/contracts/test-utils/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib"
+ },
+ "include": ["./src/**/*", "./test/**/*"]
+}
diff --git a/contracts/test-utils/tsconfig.lint.json b/contracts/test-utils/tsconfig.lint.json
new file mode 100644
index 000000000..b557e706a
--- /dev/null
+++ b/contracts/test-utils/tsconfig.lint.json
@@ -0,0 +1,7 @@
+{
+ // This file is a workaround that issue: https://github.com/palantir/tslint/issues/4148#issuecomment-419872702
+ "extends": "./tsconfig",
+ "compilerOptions": {
+ "composite": false
+ }
+}
diff --git a/contracts/test-utils/tslint.json b/contracts/test-utils/tslint.json
new file mode 100644
index 000000000..1bb3ac2a2
--- /dev/null
+++ b/contracts/test-utils/tslint.json
@@ -0,0 +1,6 @@
+{
+ "extends": ["@0x/tslint-config"],
+ "rules": {
+ "custom-no-magic-numbers": false
+ }
+}
diff --git a/contracts/utils/.solhint.json b/contracts/utils/.solhint.json
new file mode 100644
index 000000000..076afe9f3
--- /dev/null
+++ b/contracts/utils/.solhint.json
@@ -0,0 +1,20 @@
+{
+ "extends": "default",
+ "rules": {
+ "avoid-low-level-calls": false,
+ "avoid-tx-origin": "warn",
+ "bracket-align": false,
+ "code-complexity": false,
+ "const-name-snakecase": "error",
+ "expression-indent": "error",
+ "function-max-lines": false,
+ "func-order": "error",
+ "indent": ["error", 4],
+ "max-line-length": ["warn", 160],
+ "no-inline-assembly": false,
+ "quotes": ["error", "double"],
+ "separate-by-one-line-in-contract": "error",
+ "space-after-comma": "error",
+ "statement-indent": "error"
+ }
+}
diff --git a/contracts/utils/README.md b/contracts/utils/README.md
new file mode 100644
index 000000000..e7c7b49ff
--- /dev/null
+++ b/contracts/utils/README.md
@@ -0,0 +1,70 @@
+## Contracts utils
+
+Smart contracts utils used in the 0x protocol.
+
+## Usage
+
+Contracts can be found in the [contracts](./contracts) directory. The contents of this directory are broken down into the following subdirectories:
+
+* [utils](./contracts/utils)
+ * This directory contains libraries and utils.
+* [test](./contracts/test)
+ * This directory contains mocks and other contracts that are used solely for testing contracts within the other directories.
+
+## Contributing
+
+We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
+
+For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
+
+Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
+
+### Install Dependencies
+
+If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
+
+```bash
+yarn config set workspaces-experimental true
+```
+
+Then install dependencies
+
+```bash
+yarn install
+```
+
+### Build
+
+To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
+
+```bash
+PKG=@0x/contracts-utils yarn build
+```
+
+Or continuously rebuild on change:
+
+```bash
+PKG=@0x/contracts-utils yarn watch
+```
+
+### Clean
+
+```bash
+yarn clean
+```
+
+### Lint
+
+```bash
+yarn lint
+```
+
+### Run Tests
+
+```bash
+yarn test
+```
+
+#### Testing options
+
+Contracts testing options like coverage, profiling, revert traces or backing node choosing - are described [here](../TESTING.md).
diff --git a/contracts/utils/compiler.json b/contracts/utils/compiler.json
new file mode 100644
index 000000000..1524c1eaa
--- /dev/null
+++ b/contracts/utils/compiler.json
@@ -0,0 +1,22 @@
+{
+ "artifactsDir": "./generated-artifacts",
+ "contractsDir": "./contracts",
+ "compilerSettings": {
+ "optimizer": {
+ "enabled": true,
+ "runs": 1000000
+ },
+ "outputSelection": {
+ "*": {
+ "*": [
+ "abi",
+ "evm.bytecode.object",
+ "evm.bytecode.sourceMap",
+ "evm.deployedBytecode.object",
+ "evm.deployedBytecode.sourceMap"
+ ]
+ }
+ }
+ },
+ "contracts": ["TestConstants", "TestLibBytes", "LibBytes", "Ownable", "IOwnable", "ReentrancyGuard", "SafeMath"]
+}
diff --git a/packages/contracts/contracts/test/TestConstants/TestConstants.sol b/contracts/utils/contracts/test/TestConstants/TestConstants.sol
index 1275d007b..3c852173b 100644
--- a/packages/contracts/contracts/test/TestConstants/TestConstants.sol
+++ b/contracts/utils/contracts/test/TestConstants/TestConstants.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
// solhint-disable max-line-length
diff --git a/packages/contracts/contracts/test/TestLibBytes/TestLibBytes.sol b/contracts/utils/contracts/test/TestLibBytes/TestLibBytes.sol
index 00d861e61..444a3e717 100644
--- a/packages/contracts/contracts/test/TestLibBytes/TestLibBytes.sol
+++ b/contracts/utils/contracts/test/TestLibBytes/TestLibBytes.sol
@@ -18,7 +18,7 @@
pragma solidity 0.4.24;
-import "../../utils/LibBytes/LibBytes.sol";
+import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
contract TestLibBytes {
diff --git a/packages/contracts/contracts/utils/LibBytes/LibBytes.sol b/contracts/utils/contracts/utils/LibBytes/LibBytes.sol
index 369f588ad..369f588ad 100644
--- a/packages/contracts/contracts/utils/LibBytes/LibBytes.sol
+++ b/contracts/utils/contracts/utils/LibBytes/LibBytes.sol
diff --git a/packages/contracts/contracts/utils/Ownable/IOwnable.sol b/contracts/utils/contracts/utils/Ownable/IOwnable.sol
index 5deb13497..5deb13497 100644
--- a/packages/contracts/contracts/utils/Ownable/IOwnable.sol
+++ b/contracts/utils/contracts/utils/Ownable/IOwnable.sol
diff --git a/packages/contracts/contracts/utils/Ownable/Ownable.sol b/contracts/utils/contracts/utils/Ownable/Ownable.sol
index 0c830be68..0c830be68 100644
--- a/packages/contracts/contracts/utils/Ownable/Ownable.sol
+++ b/contracts/utils/contracts/utils/Ownable/Ownable.sol
diff --git a/packages/contracts/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol b/contracts/utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol
index 9f98a7a16..9f98a7a16 100644
--- a/packages/contracts/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol
+++ b/contracts/utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol
diff --git a/packages/contracts/contracts/utils/SafeMath/SafeMath.sol b/contracts/utils/contracts/utils/SafeMath/SafeMath.sol
index 2855edb9d..2855edb9d 100644
--- a/packages/contracts/contracts/utils/SafeMath/SafeMath.sol
+++ b/contracts/utils/contracts/utils/SafeMath/SafeMath.sol
diff --git a/contracts/utils/package.json b/contracts/utils/package.json
new file mode 100644
index 000000000..c0bc8bfcf
--- /dev/null
+++ b/contracts/utils/package.json
@@ -0,0 +1,90 @@
+{
+ "private": true,
+ "name": "@0x/contracts-utils",
+ "version": "1.0.0",
+ "engines": {
+ "node": ">=6.12"
+ },
+ "description": "Smart contract utils of 0x protocol",
+ "main": "lib/src/index.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "build": "yarn pre_build && tsc -b",
+ "build:ci": "yarn build",
+ "pre_build": "run-s compile generate_contract_wrappers",
+ "test": "yarn run_mocha",
+ "rebuild_and_test": "run-s build test",
+ "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
+ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
+ "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
+ "run_mocha":
+ "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
+ "compile": "sol-compiler --contracts-dir contracts",
+ "clean": "shx rm -rf lib generated-artifacts generated-wrappers",
+ "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers",
+ "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
+ "coverage:report:text": "istanbul report text",
+ "coverage:report:html": "istanbul report html && open coverage/index.html",
+ "profiler:report:html": "istanbul report html && open coverage/index.html",
+ "coverage:report:lcov": "istanbul report lcov",
+ "test:circleci": "yarn test",
+ "lint-contracts": "solhint contracts/**/**/**/**/*.sol"
+ },
+ "config": {
+ "abis": "generated-artifacts/@(IOwnable|Ownable|LibBytes|ReentrancyGuard|SafeMath|TestConstants|TestLibBytes).json"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/0xProject/0x-monorepo.git"
+ },
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/0xProject/0x-monorepo/issues"
+ },
+ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/utils/README.md",
+ "devDependencies": {
+ "@0x/contracts-test-utils": "^1.0.0",
+ "@0x/abi-gen": "^1.0.17",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/sol-compiler": "^1.1.14",
+ "@0x/sol-cov": "^2.1.14",
+ "@0x/subproviders": "^2.1.6",
+ "@0x/tslint-config": "^1.0.10",
+ "@types/bn.js": "^4.11.0",
+ "@types/lodash": "4.14.104",
+ "@types/node": "*",
+ "@types/yargs": "^10.0.0",
+ "bn.js": "^4.11.8",
+ "chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-bignumber": "^2.0.1",
+ "dirty-chai": "^2.0.1",
+ "make-promises-safe": "^1.1.0",
+ "ethereumjs-abi": "0.6.5",
+ "mocha": "^4.1.0",
+ "npm-run-all": "^4.1.2",
+ "shx": "^0.2.2",
+ "solc": "^0.4.24",
+ "solhint": "^1.2.1",
+ "tslint": "5.11.0",
+ "typescript": "3.0.1",
+ "yargs": "^10.0.3"
+ },
+ "dependencies": {
+ "@0x/base-contract": "^3.0.8",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/contracts-multisig": "^1.0.0",
+ "@0x/types": "^1.3.0",
+ "@0x/typescript-typings": "^3.0.4",
+ "@0x/utils": "^2.0.6",
+ "@0x/web3-wrapper": "^3.1.6",
+ "ethereum-types": "^1.1.2",
+ "ethereumjs-util": "^5.1.1",
+ "lodash": "^4.17.5"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/contracts/utils/src/artifacts/index.ts b/contracts/utils/src/artifacts/index.ts
new file mode 100644
index 000000000..a5c2b215c
--- /dev/null
+++ b/contracts/utils/src/artifacts/index.ts
@@ -0,0 +1,19 @@
+import { ContractArtifact } from 'ethereum-types';
+
+import * as IOwnable from '../../generated-artifacts/IOwnable.json';
+import * as LibBytes from '../../generated-artifacts/LibBytes.json';
+import * as Ownable from '../../generated-artifacts/Ownable.json';
+import * as ReentrancyGuard from '../../generated-artifacts/ReentrancyGuard.json';
+import * as SafeMath from '../../generated-artifacts/SafeMath.json';
+import * as TestConstants from '../../generated-artifacts/TestConstants.json';
+import * as TestLibBytes from '../../generated-artifacts/TestLibBytes.json';
+
+export const artifacts = {
+ TestConstants: TestConstants as ContractArtifact,
+ TestLibBytes: TestLibBytes as ContractArtifact,
+ IOwnable: IOwnable as ContractArtifact,
+ LibBytes: LibBytes as ContractArtifact,
+ Ownable: Ownable as ContractArtifact,
+ SafeMath: SafeMath as ContractArtifact,
+ ReentrancyGuard: ReentrancyGuard as ContractArtifact,
+};
diff --git a/contracts/utils/src/index.ts b/contracts/utils/src/index.ts
new file mode 100644
index 000000000..d55f08ea2
--- /dev/null
+++ b/contracts/utils/src/index.ts
@@ -0,0 +1,2 @@
+export * from './artifacts';
+export * from './wrappers';
diff --git a/contracts/utils/src/wrappers/index.ts b/contracts/utils/src/wrappers/index.ts
new file mode 100644
index 000000000..823b7fa4b
--- /dev/null
+++ b/contracts/utils/src/wrappers/index.ts
@@ -0,0 +1,2 @@
+export * from '../../generated-wrappers/test_constants';
+export * from '../../generated-wrappers/test_lib_bytes';
diff --git a/contracts/utils/test/global_hooks.ts b/contracts/utils/test/global_hooks.ts
new file mode 100644
index 000000000..f8ace376a
--- /dev/null
+++ b/contracts/utils/test/global_hooks.ts
@@ -0,0 +1,17 @@
+import { env, EnvVars } from '@0x/dev-utils';
+
+import { coverage, profiler, provider } from '@0x/contracts-test-utils';
+before('start web3 provider', () => {
+ provider.start();
+});
+after('generate coverage report', async () => {
+ if (env.parseBoolean(EnvVars.SolidityCoverage)) {
+ const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
+ await coverageSubprovider.writeCoverageAsync();
+ }
+ if (env.parseBoolean(EnvVars.SolidityProfiler)) {
+ const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
+ await profilerSubprovider.writeProfilerOutputAsync();
+ }
+ provider.stop();
+});
diff --git a/packages/contracts/test/libraries/lib_bytes.ts b/contracts/utils/test/lib_bytes.ts
index b1a389f00..985a98943 100644
--- a/packages/contracts/test/libraries/lib_bytes.ts
+++ b/contracts/utils/test/lib_bytes.ts
@@ -1,3 +1,12 @@
+import {
+ chaiSetup,
+ constants,
+ expectContractCallFailedAsync,
+ provider,
+ txDefaults,
+ typeEncodingUtils,
+ web3Wrapper,
+} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { generatePseudoRandomSalt } from '@0x/order-utils';
import { RevertReason } from '@0x/types';
@@ -7,13 +16,8 @@ import * as chai from 'chai';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
-import { TestLibBytesContract } from '../../generated-wrappers/test_lib_bytes';
-import { artifacts } from '../../src/artifacts';
-import { expectContractCallFailedAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { typeEncodingUtils } from '../utils/type_encoding_utils';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
+import { TestLibBytesContract } from '../generated-wrappers/test_lib_bytes';
+import { artifacts } from '../src';
chaiSetup.configure();
const expect = chai.expect;
diff --git a/contracts/utils/test/libs.ts b/contracts/utils/test/libs.ts
new file mode 100644
index 000000000..81596b2e4
--- /dev/null
+++ b/contracts/utils/test/libs.ts
@@ -0,0 +1,34 @@
+import { chaiSetup, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
+import { BlockchainLifecycle } from '@0x/dev-utils';
+import * as chai from 'chai';
+
+import { TestConstantsContract } from '../generated-wrappers/test_constants';
+import { artifacts } from '../src';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+
+describe('Libs', () => {
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+
+ describe('LibConstants', () => {
+ describe('ZRX_ASSET_DATA', () => {
+ it('should have the correct ZRX_ASSET_DATA', async () => {
+ const testConstants = await TestConstantsContract.deployFrom0xArtifactAsync(
+ artifacts.TestConstants,
+ provider,
+ txDefaults,
+ );
+ const isValid = await testConstants.assertValidZrxAssetData.callAsync();
+ expect(isValid).to.be.equal(true);
+ });
+ });
+ });
+});
diff --git a/contracts/utils/tsconfig.json b/contracts/utils/tsconfig.json
new file mode 100644
index 000000000..68251e6b0
--- /dev/null
+++ b/contracts/utils/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "../../tsconfig",
+ "compilerOptions": {
+ "outDir": "lib",
+ "rootDir": ".",
+ "resolveJsonModule": true
+ },
+ "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
+ "files": [
+ "./generated-artifacts/TestConstants.json",
+ "./generated-artifacts/TestLibBytes.json",
+ "./generated-artifacts/IOwnable.json",
+ "./generated-artifacts/Ownable.json",
+ "./generated-artifacts/LibBytes.json",
+ "./generated-artifacts/SafeMath.json",
+ "./generated-artifacts/ReentrancyGuard.json"
+ ],
+ "exclude": ["./deploy/solc/solc_bin"]
+}
diff --git a/contracts/utils/tslint.json b/contracts/utils/tslint.json
new file mode 100644
index 000000000..1bb3ac2a2
--- /dev/null
+++ b/contracts/utils/tslint.json
@@ -0,0 +1,6 @@
+{
+ "extends": ["@0x/tslint-config"],
+ "rules": {
+ "custom-no-magic-numbers": false
+ }
+}
diff --git a/lerna.json b/lerna.json
index e2147be85..fa9ef5aff 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,6 +1,6 @@
{
"lerna": "3.0.0-beta.23",
- "packages": ["packages/*"],
+ "packages": ["packages/*", "contracts/*"],
"version": "independent",
"command": {
"publish": {
diff --git a/package.json b/package.json
index 7307bea5d..81208dcd2 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,8 @@
"node": ">=6.12"
},
"workspaces": [
- "packages/*"
+ "packages/*",
+ "contracts/*"
],
"scripts": {
"ganache": "ganache-cli -p 8545 --gasLimit 10000000 --networkId 50 -m \"${npm_package_config_mnemonic}\"",
diff --git a/packages/0x.js/.npmignore b/packages/0x.js/.npmignore
index d7ee80c97..312a23faa 100644
--- a/packages/0x.js/.npmignore
+++ b/packages/0x.js/.npmignore
@@ -4,7 +4,7 @@ webpack.config.js
yarn-error.log
test/
/src/
-/contract_templates/
+/abi-gen-templates/
/generated_docs/
/scripts/
/lib/src/monorepo_scripts/
diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json
index 9ff4183e7..4ee1e92be 100644
--- a/packages/0x.js/CHANGELOG.json
+++ b/packages/0x.js/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "2.0.6",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "2.0.5",
"changes": [
diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md
index 1f40bca7a..463ff923d 100644
--- a/packages/0x.js/CHANGELOG.md
+++ b/packages/0x.js/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v2.0.6 - _November 28, 2018_
+
+ * Dependencies updated
+
## v2.0.5 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json
index 3850e9038..aa038c302 100644
--- a/packages/0x.js/package.json
+++ b/packages/0x.js/package.json
@@ -1,6 +1,6 @@
{
"name": "0x.js",
- "version": "2.0.5",
+ "version": "2.0.6",
"engines": {
"node": ">=6.12"
},
@@ -42,10 +42,10 @@
},
"license": "Apache-2.0",
"devDependencies": {
- "@0x/abi-gen-wrappers": "^1.1.0",
- "@0x/contract-addresses": "^1.2.0",
- "@0x/dev-utils": "^1.0.18",
- "@0x/migrations": "^2.1.0",
+ "@0x/abi-gen-wrappers": "^2.0.0",
+ "@0x/contract-addresses": "^2.0.0",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/migrations": "^2.2.0",
"@0x/tslint-config": "^1.0.10",
"@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42",
@@ -73,15 +73,15 @@
},
"dependencies": {
"@0x/assert": "^1.0.18",
- "@0x/base-contract": "^3.0.7",
- "@0x/contract-wrappers": "^4.1.0",
- "@0x/order-utils": "^3.0.3",
- "@0x/order-watcher": "^2.2.5",
- "@0x/subproviders": "^2.1.5",
+ "@0x/base-contract": "^3.0.8",
+ "@0x/contract-wrappers": "^4.1.1",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/order-watcher": "^2.2.6",
+ "@0x/subproviders": "^2.1.6",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@types/web3-provider-engine": "^14.0.0",
"ethereum-types": "^1.1.2",
"ethers": "~4.0.4",
diff --git a/packages/abi-gen-templates/CHANGELOG.json b/packages/abi-gen-templates/CHANGELOG.json
index adf615b3b..baf852ad5 100644
--- a/packages/abi-gen-templates/CHANGELOG.json
+++ b/packages/abi-gen-templates/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "1.0.1",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"version": "1.0.0",
"changes": [
{
diff --git a/packages/abi-gen-templates/CHANGELOG.md b/packages/abi-gen-templates/CHANGELOG.md
new file mode 100644
index 000000000..1c3f21c6c
--- /dev/null
+++ b/packages/abi-gen-templates/CHANGELOG.md
@@ -0,0 +1,14 @@
+<!--
+changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
+Edit the package's CHANGELOG.json file only.
+-->
+
+CHANGELOG
+
+## v1.0.1 - _November 28, 2018_
+
+ * Dependencies updated
+
+## v1.0.0 - _Invalid date_
+
+ * Initial publish (#1305)
diff --git a/packages/abi-gen-templates/package.json b/packages/abi-gen-templates/package.json
index e06be6127..09872ab49 100644
--- a/packages/abi-gen-templates/package.json
+++ b/packages/abi-gen-templates/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/abi-gen-templates",
- "version": "1.0.0",
+ "version": "1.0.1",
"engines": {
"node": ">=6.12"
},
diff --git a/packages/abi-gen-wrappers/CHANGELOG.json b/packages/abi-gen-wrappers/CHANGELOG.json
index f74d98afa..6905a7537 100644
--- a/packages/abi-gen-wrappers/CHANGELOG.json
+++ b/packages/abi-gen-wrappers/CHANGELOG.json
@@ -6,7 +6,8 @@
"pr": 1309,
"note": "Update Exchange artifact to receive ZRX asset data as a constructor argument"
}
- ]
+ ],
+ "timestamp": 1543401373
},
{
"version": "1.1.0",
diff --git a/packages/abi-gen-wrappers/CHANGELOG.md b/packages/abi-gen-wrappers/CHANGELOG.md
index 7d359f07b..30a10d6bd 100644
--- a/packages/abi-gen-wrappers/CHANGELOG.md
+++ b/packages/abi-gen-wrappers/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v2.0.0 - _November 28, 2018_
+
+ * Update Exchange artifact to receive ZRX asset data as a constructor argument (#1309)
+
## v1.1.0 - _November 21, 2018_
* `deployFrom0xArtifactAsync` additionally accepts artifacts that conform to the `SimpleContractArtifact` interface (#1298)
diff --git a/packages/abi-gen-wrappers/package.json b/packages/abi-gen-wrappers/package.json
index 1b7015d55..e4f103cf7 100644
--- a/packages/abi-gen-wrappers/package.json
+++ b/packages/abi-gen-wrappers/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/abi-gen-wrappers",
- "version": "1.1.0",
+ "version": "2.0.0",
"engines": {
"node": ">=6.12"
},
@@ -31,18 +31,18 @@
"homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen-wrappers/README.md",
"devDependencies": {
"@0x/abi-gen": "^1.0.17",
- "@0x/abi-gen-templates": "^1.0.0",
+ "@0x/abi-gen-templates": "^1.0.1",
"@0x/tslint-config": "^1.0.10",
"@0x/types": "^1.3.0",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"ethereum-types": "^1.1.2",
"ethers": "~4.0.4",
"lodash": "^4.17.5",
"shx": "^0.2.2"
},
"dependencies": {
- "@0x/base-contract": "^3.0.7"
+ "@0x/base-contract": "^3.0.8"
},
"publishConfig": {
"access": "public"
diff --git a/packages/abi-gen/README.md b/packages/abi-gen/README.md
index 20b9d4f30..214d8f257 100644
--- a/packages/abi-gen/README.md
+++ b/packages/abi-gen/README.md
@@ -4,7 +4,7 @@ This package allows you to generate TypeScript contract wrappers from ABI files.
It's heavily inspired by [Geth abigen](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) but takes a different approach.
You can write your custom handlebars templates which will allow you to seamlessly integrate the generated code into your existing codebase with existing conventions.
-[Here](https://github.com/0xProject/0x-monorepo/tree/development/packages/0x.js/contract_templates) are the templates used to generate the contract wrappers used by 0x.js.e
+[Here](https://github.com/0xProject/0x-monorepo/tree/development/packages/0x.js/abi-gen-templates) are the templates used to generate the contract wrappers used by 0x.js.e
## Installation
@@ -44,7 +44,7 @@ You need to also specify the location of your main template used for every contr
## How to write custom templates?
-The best way to get started is to copy [0x.js templates](https://github.com/0xProject/0x-monorepo/tree/development/packages/contract_templates) and start adjusting them for your needs.
+The best way to get started is to copy [0x.js templates](https://github.com/0xProject/0x-monorepo/tree/development/packages/abi-gen-templates) and start adjusting them for your needs.
We use [handlebars](http://handlebarsjs.com/) template engine under the hood.
You need to have a master template called `contract.mustache`. it will be used to generate each contract wrapper. Although - you don't need and probably shouldn't write all your logic in a single template file. You can write [partial templates](http://handlebarsjs.com/partials.html) and as long as they are within a partials folder - they will be registered and available.
diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json
index b7a83ccfc..4ff83018e 100644
--- a/packages/asset-buyer/CHANGELOG.json
+++ b/packages/asset-buyer/CHANGELOG.json
@@ -1,5 +1,22 @@
[
{
+ "version": "3.0.3",
+ "changes": [
+ {
+ "note": "Update SRA order provider to include Dai"
+ }
+ ]
+ },
+ {
+ "timestamp": 1543401373,
+ "version": "3.0.2",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"version": "3.0.1",
"changes": [
{
diff --git a/packages/asset-buyer/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md
index 20702a531..be3ef67d1 100644
--- a/packages/asset-buyer/CHANGELOG.md
+++ b/packages/asset-buyer/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v3.0.2 - _November 28, 2018_
+
+ * Dependencies updated
+
## v3.0.1 - _November 21, 2018_
* Dependencies updated (#1276)
diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json
index 50b276704..780b2e3e2 100644
--- a/packages/asset-buyer/package.json
+++ b/packages/asset-buyer/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/asset-buyer",
- "version": "3.0.1",
+ "version": "3.0.2",
"engines": {
"node": ">=6.12"
},
@@ -37,15 +37,15 @@
"homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md",
"dependencies": {
"@0x/assert": "^1.0.18",
- "@0x/connect": "^3.0.7",
- "@0x/contract-wrappers": "^4.1.0",
+ "@0x/connect": "^3.0.8",
+ "@0x/contract-wrappers": "^4.1.1",
"@0x/json-schemas": "^2.1.2",
- "@0x/order-utils": "^3.0.3",
- "@0x/subproviders": "^2.1.5",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/subproviders": "^2.1.6",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"ethereum-types": "^1.1.2",
"lodash": "^4.17.5"
},
diff --git a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts
index be1fc55d6..813c9923b 100644
--- a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts
+++ b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts
@@ -100,6 +100,12 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider {
} catch (err) {
throw new Error(AssetBuyerError.StandardRelayerApiError);
}
- return _.map(response.records, item => item.assetDataB.assetData);
+ return _.map(response.records, item => {
+ if (item.assetDataA.assetData === takerAssetData) {
+ return item.assetDataB.assetData;
+ } else {
+ return item.assetDataA.assetData;
+ }
+ });
}
}
diff --git a/packages/base-contract/CHANGELOG.json b/packages/base-contract/CHANGELOG.json
index 66633136c..e4dff5530 100644
--- a/packages/base-contract/CHANGELOG.json
+++ b/packages/base-contract/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "3.0.8",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "3.0.7",
"changes": [
diff --git a/packages/base-contract/CHANGELOG.md b/packages/base-contract/CHANGELOG.md
index 35032fc9f..f61b6c6ce 100644
--- a/packages/base-contract/CHANGELOG.md
+++ b/packages/base-contract/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v3.0.8 - _November 28, 2018_
+
+ * Dependencies updated
+
## v3.0.7 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json
index 2ae42d66b..2a331b3cb 100644
--- a/packages/base-contract/package.json
+++ b/packages/base-contract/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/base-contract",
- "version": "3.0.7",
+ "version": "3.0.8",
"engines": {
"node": ">=6.12"
},
@@ -42,7 +42,7 @@
"dependencies": {
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"ethereum-types": "^1.1.2",
"ethers": "~4.0.4",
"lodash": "^4.17.5"
diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json
index db9d8c92a..3abb895a7 100644
--- a/packages/connect/CHANGELOG.json
+++ b/packages/connect/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "3.0.8",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "3.0.7",
"changes": [
diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md
index 5e4013322..1dfc2672d 100644
--- a/packages/connect/CHANGELOG.md
+++ b/packages/connect/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v3.0.8 - _November 28, 2018_
+
+ * Dependencies updated
+
## v3.0.7 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/connect/package.json b/packages/connect/package.json
index d05f24463..2f3d30d84 100644
--- a/packages/connect/package.json
+++ b/packages/connect/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/connect",
- "version": "3.0.7",
+ "version": "3.0.8",
"engines": {
"node": ">=6.12"
},
@@ -46,7 +46,7 @@
"dependencies": {
"@0x/assert": "^1.0.18",
"@0x/json-schemas": "^2.1.2",
- "@0x/order-utils": "^3.0.3",
+ "@0x/order-utils": "^3.0.4",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json
index e65351c7e..21ffaf510 100644
--- a/packages/contract-addresses/CHANGELOG.json
+++ b/packages/contract-addresses/CHANGELOG.json
@@ -1,5 +1,19 @@
[
{
+ "version": "2.0.0",
+ "changes": [
+ {
+ "note": "Redeployed Rinkeby with testnet Exchange artifact",
+ "pr": 1318
+ },
+ {
+ "note": "Added Ganache snapshot addresses for network 50",
+ "pr": 1318
+ }
+ ],
+ "timestamp": 1543401373
+ },
+ {
"version": "1.2.0",
"changes": [
{
diff --git a/packages/contract-addresses/CHANGELOG.md b/packages/contract-addresses/CHANGELOG.md
index 9801831f7..c006c3b22 100644
--- a/packages/contract-addresses/CHANGELOG.md
+++ b/packages/contract-addresses/CHANGELOG.md
@@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v2.0.0 - _November 28, 2018_
+
+ * Redeployed Rinkeby with testnet Exchange artifact (#1318)
+ * Added Ganache snapshot addresses for network 50 (#1318)
+
## v1.2.0 - _November 21, 2018_
* Rinkeby Deployment
diff --git a/packages/contract-addresses/package.json b/packages/contract-addresses/package.json
index 9f0db4d30..c75ae6efa 100644
--- a/packages/contract-addresses/package.json
+++ b/packages/contract-addresses/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/contract-addresses",
- "version": "1.2.0",
+ "version": "2.0.0",
"engines": {
"node": ">=6.12"
},
diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts
index 57358dd38..7989631e3 100644
--- a/packages/contract-addresses/src/index.ts
+++ b/packages/contract-addresses/src/index.ts
@@ -16,6 +16,7 @@ export enum NetworkId {
Ropsten = 3,
Rinkeby = 4,
Kovan = 42,
+ Ganache = 50,
}
const networkToAddresses: { [networkId: number]: ContractAddresses } = {
@@ -40,14 +41,14 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f',
},
4: {
- erc20Proxy: '0x3e809c563c15a295e832e37053798ddc8d6c8dab',
- erc721Proxy: '0x8e1ff02637cb5e39f2fa36c14706aa348b065b09',
- zrxToken: '0x2727e688b8fd40b198cd5fe6e408e00494a06f07',
+ exchange: '0xbce0b5f6eb618c565c3e5f5cd69652bbc279f44e',
+ erc20Proxy: '0x2f5ae4f6106e89b4147651688a92256885c5f410',
+ erc721Proxy: '0x7656d773e11ff7383a14dcf09a9c50990481cd10',
+ zrxToken: '0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa',
etherToken: '0xc778417e063141139fce010982780140aa0cd5ab',
- exchange: '0x22ebc052f43a88efa06379426120718170f2204e',
- assetProxyOwner: '0x1da52d1d3a3acfa0a1836b737393b4e9931268fc',
- forwarder: '0xd2dbf3250a764eaaa94fa0c84ed87c0edc8ed04e',
- orderValidator: '0x39c3fc9f4d8430af2713306ce80c584752d9e1c7',
+ assetProxyOwner: '0xe1703da878afcebff5b7624a826902af475b9c03',
+ forwarder: '0x2d40589abbdee84961f3a7656b9af7adb0ee5ab4',
+ orderValidator: '0x0c5173a51e26b29d6126c686756fb9fbef71f762',
},
42: {
erc20Proxy: '0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e',
@@ -59,6 +60,17 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6',
orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d',
},
+ // NetworkId 50 represents our Ganache snapshot generated from migrations.
+ 50: {
+ exchange: '0x48bacb9266a570d521063ef5dd96e61686dbe788',
+ erc20Proxy: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
+ erc721Proxy: '0x1d7022f5b17d2f8b695918fb48fa1089c9f85401',
+ zrxToken: '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c',
+ etherToken: '0x0b1ba0af832d7c05fd64161e0db78e85978e8082',
+ assetProxyOwner: '0x34d402f14d58e001d8efbe6585051bf9706aa064',
+ forwarder: '0xb69e673309512a9d726f87304c6984054f87a93b',
+ orderValidator: '0xe86bb98fcf9bff3512c74589b78fb168200cc546',
+ },
};
/**
diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json
index b75ad5766..03c88e71a 100644
--- a/packages/contract-artifacts/CHANGELOG.json
+++ b/packages/contract-artifacts/CHANGELOG.json
@@ -6,7 +6,8 @@
"pr": 1309,
"note": "Update Exchange artifact to receive ZRX asset data as a constructor argument"
}
- ]
+ ],
+ "timestamp": 1543401373
},
{
"version": "1.1.0",
diff --git a/packages/contract-artifacts/CHANGELOG.md b/packages/contract-artifacts/CHANGELOG.md
index b3c399985..9e48058f5 100644
--- a/packages/contract-artifacts/CHANGELOG.md
+++ b/packages/contract-artifacts/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v1.1.2 - _November 28, 2018_
+
+ * Update Exchange artifact to receive ZRX asset data as a constructor argument (#1309)
+
## v1.1.0 - _November 9, 2018_
* Update Forwarder artifact (#1192)
diff --git a/packages/contract-artifacts/package.json b/packages/contract-artifacts/package.json
index 9c25e3ac5..71d887545 100644
--- a/packages/contract-artifacts/package.json
+++ b/packages/contract-artifacts/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/contract-artifacts",
- "version": "1.1.0",
+ "version": "1.1.2",
"engines": {
"node": ">=6.12"
},
diff --git a/packages/contract-wrappers/.npmignore b/packages/contract-wrappers/.npmignore
index 6a3eb57bd..6a222fd45 100644
--- a/packages/contract-wrappers/.npmignore
+++ b/packages/contract-wrappers/.npmignore
@@ -5,7 +5,7 @@ yarn-error.log
test/
/src/
/_bundles/
-/contract_templates/
+/abi-gen-templates/
/generated_docs/
/scripts/
/lib/src/monorepo_scripts/
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json
index 711ab49a1..006a0904d 100644
--- a/packages/contract-wrappers/CHANGELOG.json
+++ b/packages/contract-wrappers/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "4.1.1",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"version": "4.1.0",
"changes": [
{
diff --git a/packages/contract-wrappers/CHANGELOG.md b/packages/contract-wrappers/CHANGELOG.md
index 201c65a4c..ebdcc9638 100644
--- a/packages/contract-wrappers/CHANGELOG.md
+++ b/packages/contract-wrappers/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v4.1.1 - _November 28, 2018_
+
+ * Dependencies updated
+
## v4.1.0 - _November 21, 2018_
* Add a `nonce` field for `TxOpts` so that it's now possible to re-broadcast stuck transactions with a higher gas amount (#1292)
diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json
index 999375ea5..e11d1a63e 100644
--- a/packages/contract-wrappers/package.json
+++ b/packages/contract-wrappers/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/contract-wrappers",
- "version": "4.1.0",
+ "version": "4.1.1",
"description": "Smart TS wrappers for 0x smart contracts",
"keywords": [
"0xproject",
@@ -37,9 +37,9 @@
"node": ">=6.0.0"
},
"devDependencies": {
- "@0x/dev-utils": "^1.0.18",
- "@0x/migrations": "^2.1.0",
- "@0x/subproviders": "^2.1.5",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/migrations": "^2.2.0",
+ "@0x/subproviders": "^2.1.6",
"@0x/tslint-config": "^1.0.10",
"@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42",
@@ -65,17 +65,17 @@
"web3-provider-engine": "14.0.6"
},
"dependencies": {
- "@0x/abi-gen-wrappers": "^1.1.0",
+ "@0x/abi-gen-wrappers": "^2.0.0",
"@0x/assert": "^1.0.18",
- "@0x/contract-addresses": "^1.2.0",
- "@0x/contract-artifacts": "^1.1.0",
- "@0x/fill-scenarios": "^1.0.13",
+ "@0x/contract-addresses": "^2.0.0",
+ "@0x/contract-artifacts": "^1.1.2",
+ "@0x/fill-scenarios": "^1.0.14",
"@0x/json-schemas": "^2.1.2",
- "@0x/order-utils": "^3.0.3",
+ "@0x/order-utils": "^3.0.4",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"ethereum-types": "^1.1.2",
"ethereumjs-blockstream": "6.0.0",
"ethereumjs-util": "^5.1.1",
diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts
deleted file mode 100644
index b8305993e..000000000
--- a/packages/contracts/test/asset_proxy/proxies.ts
+++ /dev/null
@@ -1,629 +0,0 @@
-import { BlockchainLifecycle } from '@0x/dev-utils';
-import { assetDataUtils } from '@0x/order-utils';
-import { RevertReason } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-import * as chai from 'chai';
-import * as _ from 'lodash';
-
-import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token';
-import { DummyERC721ReceiverContract } from '../../generated-wrappers/dummy_erc721_receiver';
-import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token';
-import { DummyMultipleReturnERC20TokenContract } from '../../generated-wrappers/dummy_multiple_return_erc20_token';
-import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_no_return_erc20_token';
-import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy';
-import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy';
-import { IAssetProxyContract } from '../../generated-wrappers/i_asset_proxy';
-import { artifacts } from '../../src/artifacts';
-import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
-import { chaiSetup } from '../utils/chai_setup';
-import { constants } from '../utils/constants';
-import { ERC20Wrapper } from '../utils/erc20_wrapper';
-import { ERC721Wrapper } from '../utils/erc721_wrapper';
-import { LogDecoder } from '../utils/log_decoder';
-import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
-
-chaiSetup.configure();
-const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-const assetProxyInterface = new IAssetProxyContract(
- artifacts.IAssetProxy.compilerOutput.abi,
- constants.NULL_ADDRESS,
- provider,
-);
-
-// tslint:disable:no-unnecessary-type-assertion
-describe('Asset Transfer Proxies', () => {
- let owner: string;
- let notAuthorized: string;
- let exchangeAddress: string;
- let makerAddress: string;
- let takerAddress: string;
-
- let zrxToken: DummyERC20TokenContract;
- let erc721Token: DummyERC721TokenContract;
- let erc721Receiver: DummyERC721ReceiverContract;
- let erc20Proxy: ERC20ProxyContract;
- let erc721Proxy: ERC721ProxyContract;
- let noReturnErc20Token: DummyNoReturnERC20TokenContract;
- let multipleReturnErc20Token: DummyMultipleReturnERC20TokenContract;
-
- let erc20Wrapper: ERC20Wrapper;
- let erc721Wrapper: ERC721Wrapper;
- let erc721MakerTokenId: BigNumber;
-
- before(async () => {
- await blockchainLifecycle.startAsync();
- });
- after(async () => {
- await blockchainLifecycle.revertAsync();
- });
- before(async () => {
- const accounts = await web3Wrapper.getAvailableAddressesAsync();
- const usedAddresses = ([owner, notAuthorized, exchangeAddress, makerAddress, takerAddress] = _.slice(
- accounts,
- 0,
- 5,
- ));
-
- erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
- erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
-
- const numDummyErc20ToDeploy = 1;
- [zrxToken] = await erc20Wrapper.deployDummyTokensAsync(numDummyErc20ToDeploy, constants.DUMMY_TOKEN_DECIMALS);
- erc20Proxy = await erc20Wrapper.deployProxyAsync();
- await erc20Wrapper.setBalancesAndAllowancesAsync();
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, {
- from: owner,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
-
- [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
- erc721Proxy = await erc721Wrapper.deployProxyAsync();
- await erc721Wrapper.setBalancesAndAllowancesAsync();
- const erc721Balances = await erc721Wrapper.getBalancesAsync();
- erc721MakerTokenId = erc721Balances[makerAddress][erc721Token.address][0];
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, {
- from: owner,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
- artifacts.DummyERC721Receiver,
- provider,
- txDefaults,
- );
- noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
- artifacts.DummyNoReturnERC20Token,
- provider,
- txDefaults,
- constants.DUMMY_TOKEN_NAME,
- constants.DUMMY_TOKEN_SYMBOL,
- constants.DUMMY_TOKEN_DECIMALS,
- constants.DUMMY_TOKEN_TOTAL_SUPPLY,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await noReturnErc20Token.setBalance.sendTransactionAsync(makerAddress, constants.INITIAL_ERC20_BALANCE),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await noReturnErc20Token.approve.sendTransactionAsync(
- erc20Proxy.address,
- constants.INITIAL_ERC20_ALLOWANCE,
- { from: makerAddress },
- ),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync(
- artifacts.DummyMultipleReturnERC20Token,
- provider,
- txDefaults,
- constants.DUMMY_TOKEN_NAME,
- constants.DUMMY_TOKEN_SYMBOL,
- constants.DUMMY_TOKEN_DECIMALS,
- constants.DUMMY_TOKEN_TOTAL_SUPPLY,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await multipleReturnErc20Token.setBalance.sendTransactionAsync(
- makerAddress,
- constants.INITIAL_ERC20_BALANCE,
- ),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await multipleReturnErc20Token.approve.sendTransactionAsync(
- erc20Proxy.address,
- constants.INITIAL_ERC20_ALLOWANCE,
- { from: makerAddress },
- ),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
- describe('Transfer Proxy - ERC20', () => {
- it('should revert if undefined function is called', async () => {
- const undefinedSelector = '0x01020304';
- await expectTransactionFailedWithoutReasonAsync(
- web3Wrapper.sendTransactionAsync({
- from: owner,
- to: erc20Proxy.address,
- value: constants.ZERO_AMOUNT,
- data: undefinedSelector,
- }),
- );
- });
- describe('transferFrom', () => {
- it('should successfully transfer tokens', async () => {
- // Construct ERC20 asset data
- const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- // Perform a transfer from makerAddress to takerAddress
- const erc20Balances = await erc20Wrapper.getBalancesAsync();
- const amount = new BigNumber(10);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await web3Wrapper.sendTransactionAsync({
- to: erc20Proxy.address,
- data,
- from: exchangeAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- // Verify transfer was successful
- const newBalances = await erc20Wrapper.getBalancesAsync();
- expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][zrxToken.address].minus(amount),
- );
- expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[takerAddress][zrxToken.address].add(amount),
- );
- });
-
- it('should successfully transfer tokens that do not return a value', async () => {
- // Construct ERC20 asset data
- const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address);
- // Perform a transfer from makerAddress to takerAddress
- const initialMakerBalance = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
- const initialTakerBalance = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
- const amount = new BigNumber(10);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await web3Wrapper.sendTransactionAsync({
- to: erc20Proxy.address,
- data,
- from: exchangeAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- // Verify transfer was successful
- const newMakerBalance = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
- const newTakerBalance = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
- expect(newMakerBalance).to.be.bignumber.equal(initialMakerBalance.minus(amount));
- expect(newTakerBalance).to.be.bignumber.equal(initialTakerBalance.plus(amount));
- });
-
- it('should successfully transfer tokens and ignore extra assetData', async () => {
- // Construct ERC20 asset data
- const extraData = '0102030405060708';
- const encodedAssetData = `${assetDataUtils.encodeERC20AssetData(zrxToken.address)}${extraData}`;
- // Perform a transfer from makerAddress to takerAddress
- const erc20Balances = await erc20Wrapper.getBalancesAsync();
- const amount = new BigNumber(10);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await web3Wrapper.sendTransactionAsync({
- to: erc20Proxy.address,
- data,
- from: exchangeAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- // Verify transfer was successful
- const newBalances = await erc20Wrapper.getBalancesAsync();
- expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][zrxToken.address].minus(amount),
- );
- expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[takerAddress][zrxToken.address].add(amount),
- );
- });
-
- it('should do nothing if transferring 0 amount of a token', async () => {
- // Construct ERC20 asset data
- const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- // Perform a transfer from makerAddress to takerAddress
- const erc20Balances = await erc20Wrapper.getBalancesAsync();
- const amount = new BigNumber(0);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await web3Wrapper.sendTransactionAsync({
- to: erc20Proxy.address,
- data,
- from: exchangeAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- // Verify transfer was successful
- const newBalances = await erc20Wrapper.getBalancesAsync();
- expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][zrxToken.address],
- );
- expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[takerAddress][zrxToken.address],
- );
- });
-
- it('should throw if allowances are too low', async () => {
- // Construct ERC20 asset data
- const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- // Create allowance less than transfer amount. Set allowance on proxy.
- const allowance = new BigNumber(0);
- const amount = new BigNumber(10);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const erc20Balances = await erc20Wrapper.getBalancesAsync();
- // Perform a transfer; expect this to fail.
- await expectTransactionFailedAsync(
- web3Wrapper.sendTransactionAsync({
- to: erc20Proxy.address,
- data,
- from: exchangeAddress,
- }),
- RevertReason.TransferFailed,
- );
- const newBalances = await erc20Wrapper.getBalancesAsync();
- expect(newBalances).to.deep.equal(erc20Balances);
- });
-
- it('should throw if allowances are too low and token does not return a value', async () => {
- // Construct ERC20 asset data
- const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address);
- // Create allowance less than transfer amount. Set allowance on proxy.
- const allowance = new BigNumber(0);
- const amount = new BigNumber(10);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await noReturnErc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const initialMakerBalance = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
- const initialTakerBalance = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
- // Perform a transfer; expect this to fail.
- await expectTransactionFailedAsync(
- web3Wrapper.sendTransactionAsync({
- to: erc20Proxy.address,
- data,
- from: exchangeAddress,
- }),
- RevertReason.TransferFailed,
- );
- const newMakerBalance = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
- const newTakerBalance = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
- expect(newMakerBalance).to.be.bignumber.equal(initialMakerBalance);
- expect(newTakerBalance).to.be.bignumber.equal(initialTakerBalance);
- });
-
- it('should throw if requesting address is not authorized', async () => {
- // Construct ERC20 asset data
- const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- // Perform a transfer from makerAddress to takerAddress
- const amount = new BigNumber(10);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- const erc20Balances = await erc20Wrapper.getBalancesAsync();
- await expectTransactionFailedAsync(
- web3Wrapper.sendTransactionAsync({
- to: erc20Proxy.address,
- data,
- from: notAuthorized,
- }),
- RevertReason.SenderNotAuthorized,
- );
- const newBalances = await erc20Wrapper.getBalancesAsync();
- expect(newBalances).to.deep.equal(erc20Balances);
- });
-
- it('should throw if token returns more than 32 bytes', async () => {
- // Construct ERC20 asset data
- const encodedAssetData = assetDataUtils.encodeERC20AssetData(multipleReturnErc20Token.address);
- const amount = new BigNumber(10);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- const initialMakerBalance = await multipleReturnErc20Token.balanceOf.callAsync(makerAddress);
- const initialTakerBalance = await multipleReturnErc20Token.balanceOf.callAsync(takerAddress);
- // Perform a transfer; expect this to fail.
- await expectTransactionFailedAsync(
- web3Wrapper.sendTransactionAsync({
- to: erc20Proxy.address,
- data,
- from: exchangeAddress,
- }),
- RevertReason.TransferFailed,
- );
- const newMakerBalance = await multipleReturnErc20Token.balanceOf.callAsync(makerAddress);
- const newTakerBalance = await multipleReturnErc20Token.balanceOf.callAsync(takerAddress);
- expect(newMakerBalance).to.be.bignumber.equal(initialMakerBalance);
- expect(newTakerBalance).to.be.bignumber.equal(initialTakerBalance);
- });
- });
-
- it('should have an id of 0xf47261b0', async () => {
- const proxyId = await erc20Proxy.getProxyId.callAsync();
- const expectedProxyId = '0xf47261b0';
- expect(proxyId).to.equal(expectedProxyId);
- });
- });
-
- describe('Transfer Proxy - ERC721', () => {
- it('should revert if undefined function is called', async () => {
- const undefinedSelector = '0x01020304';
- await expectTransactionFailedWithoutReasonAsync(
- web3Wrapper.sendTransactionAsync({
- from: owner,
- to: erc721Proxy.address,
- value: constants.ZERO_AMOUNT,
- data: undefinedSelector,
- }),
- );
- });
- describe('transferFrom', () => {
- it('should successfully transfer tokens', async () => {
- // Construct ERC721 asset data
- const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
- // Verify pre-condition
- const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(ownerMakerAsset).to.be.equal(makerAddress);
- // Perform a transfer from makerAddress to takerAddress
- const amount = new BigNumber(1);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await web3Wrapper.sendTransactionAsync({
- to: erc721Proxy.address,
- data,
- from: exchangeAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- // Verify transfer was successful
- const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress);
- });
-
- it('should successfully transfer tokens and ignore extra assetData', async () => {
- // Construct ERC721 asset data
- const extraData = '0102030405060708';
- const encodedAssetData = `${assetDataUtils.encodeERC721AssetData(
- erc721Token.address,
- erc721MakerTokenId,
- )}${extraData}`;
- // Verify pre-condition
- const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(ownerMakerAsset).to.be.equal(makerAddress);
- // Perform a transfer from makerAddress to takerAddress
- const amount = new BigNumber(1);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await web3Wrapper.sendTransactionAsync({
- to: erc721Proxy.address,
- data,
- from: exchangeAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- // Verify transfer was successful
- const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress);
- });
-
- it('should not call onERC721Received when transferring to a smart contract', async () => {
- // Construct ERC721 asset data
- const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
- // Verify pre-condition
- const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(ownerMakerAsset).to.be.equal(makerAddress);
- // Perform a transfer from makerAddress to takerAddress
- const amount = new BigNumber(1);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- erc721Receiver.address,
- amount,
- );
- const logDecoder = new LogDecoder(web3Wrapper);
- const tx = await logDecoder.getTxWithDecodedLogsAsync(
- await web3Wrapper.sendTransactionAsync({
- to: erc721Proxy.address,
- data,
- from: exchangeAddress,
- gas: constants.MAX_TRANSFER_FROM_GAS,
- }),
- );
- // Verify that no log was emitted by erc721 receiver
- expect(tx.logs.length).to.be.equal(1);
- // Verify transfer was successful
- const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(newOwnerMakerAsset).to.be.bignumber.equal(erc721Receiver.address);
- });
-
- it('should throw if transferring 0 amount of a token', async () => {
- // Construct ERC721 asset data
- const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
- // Verify pre-condition
- const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(ownerMakerAsset).to.be.equal(makerAddress);
- // Perform a transfer from makerAddress to takerAddress
- const amount = new BigNumber(0);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await expectTransactionFailedAsync(
- web3Wrapper.sendTransactionAsync({
- to: erc721Proxy.address,
- data,
- from: exchangeAddress,
- }),
- RevertReason.InvalidAmount,
- );
- const newOwner = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(newOwner).to.be.equal(ownerMakerAsset);
- });
-
- it('should throw if transferring > 1 amount of a token', async () => {
- // Construct ERC721 asset data
- const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
- // Verify pre-condition
- const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(ownerMakerAsset).to.be.equal(makerAddress);
- // Perform a transfer from makerAddress to takerAddress
- const amount = new BigNumber(500);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await expectTransactionFailedAsync(
- web3Wrapper.sendTransactionAsync({
- to: erc721Proxy.address,
- data,
- from: exchangeAddress,
- }),
- RevertReason.InvalidAmount,
- );
- const newOwner = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(newOwner).to.be.equal(ownerMakerAsset);
- });
-
- it('should throw if allowances are too low', async () => {
- // Construct ERC721 asset data
- const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
- // Verify pre-condition
- const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(ownerMakerAsset).to.be.equal(makerAddress);
- // Remove transfer approval for makerAddress.
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.approve.sendTransactionAsync(constants.NULL_ADDRESS, erc721MakerTokenId, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- // Perform a transfer; expect this to fail.
- const amount = new BigNumber(1);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await expectTransactionFailedAsync(
- web3Wrapper.sendTransactionAsync({
- to: erc721Proxy.address,
- data,
- from: exchangeAddress,
- }),
- RevertReason.TransferFailed,
- );
- const newOwner = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(newOwner).to.be.equal(ownerMakerAsset);
- });
-
- it('should throw if requesting address is not authorized', async () => {
- // Construct ERC721 asset data
- const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
- // Verify pre-condition
- const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(ownerMakerAsset).to.be.equal(makerAddress);
- // Perform a transfer from makerAddress to takerAddress
- const amount = new BigNumber(1);
- const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
- encodedAssetData,
- makerAddress,
- takerAddress,
- amount,
- );
- await expectTransactionFailedAsync(
- web3Wrapper.sendTransactionAsync({
- to: erc721Proxy.address,
- data,
- from: notAuthorized,
- }),
- RevertReason.SenderNotAuthorized,
- );
- const newOwner = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
- expect(newOwner).to.be.equal(ownerMakerAsset);
- });
- });
-
- it('should have an id of 0x02571792', async () => {
- const proxyId = await erc721Proxy.getProxyId.callAsync();
- const expectedProxyId = '0x02571792';
- expect(proxyId).to.equal(expectedProxyId);
- });
- });
-});
-// tslint:enable:no-unnecessary-type-assertion
-// tslint:disable:max-file-line-count
diff --git a/packages/dev-tools-pages/package.json b/packages/dev-tools-pages/package.json
index eb320c103..4b13beb01 100644
--- a/packages/dev-tools-pages/package.json
+++ b/packages/dev-tools-pages/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/dev-tools-pages",
- "version": "0.0.7",
+ "version": "0.0.8",
"engines": {
"node": ">=6.12"
},
@@ -16,7 +16,7 @@
},
"license": "Apache-2.0",
"dependencies": {
- "@0x/react-shared": "^1.0.22",
+ "@0x/react-shared": "^1.0.23",
"basscss": "^8.0.3",
"bowser": "^1.9.3",
"less": "^2.7.2",
diff --git a/packages/dev-utils/CHANGELOG.json b/packages/dev-utils/CHANGELOG.json
index 4f47f0f45..417a3c65e 100644
--- a/packages/dev-utils/CHANGELOG.json
+++ b/packages/dev-utils/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "1.0.19",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "1.0.18",
"changes": [
diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md
index 3ab8192c7..1842c6824 100644
--- a/packages/dev-utils/CHANGELOG.md
+++ b/packages/dev-utils/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v1.0.19 - _November 28, 2018_
+
+ * Dependencies updated
+
## v1.0.18 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md
index 73b0b8816..b85159dd8 100644
--- a/packages/dev-utils/README.md
+++ b/packages/dev-utils/README.md
@@ -27,6 +27,21 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol
}
```
+## Troubleshooting
+
+If you are still seeing TS type errors complaining about missing DOM types such as `Response`:
+
+```
+error TS2304: Cannot find name 'Response'.
+```
+
+Then you need to explicitly add the `dom` lib to your compiler options in `tsconfig.json`. The `dom` library is included by default, but customizing the `lib` option can cause it to be dropped.
+
+```
+"compilerOptions": {
+ "lib": [..., "dom"],
+```
+
## Contributing
We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json
index e7cd62a81..a3b5c9090 100644
--- a/packages/dev-utils/package.json
+++ b/packages/dev-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/dev-utils",
- "version": "1.0.18",
+ "version": "1.0.19",
"engines": {
"node": ">=6.12"
},
@@ -41,11 +41,11 @@
"typescript": "3.0.1"
},
"dependencies": {
- "@0x/subproviders": "^2.1.5",
+ "@0x/subproviders": "^2.1.6",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@types/web3-provider-engine": "^14.0.0",
"chai": "^4.0.1",
"ethereum-types": "^1.1.2",
diff --git a/packages/ethereum-types/src/index.ts b/packages/ethereum-types/src/index.ts
index eff38711a..9430fdc98 100644
--- a/packages/ethereum-types/src/index.ts
+++ b/packages/ethereum-types/src/index.ts
@@ -283,6 +283,11 @@ export interface RawLogEntry {
export enum SolidityTypes {
Address = 'address',
+ Bool = 'bool',
+ Bytes = 'bytes',
+ Int = 'int',
+ String = 'string',
+ Tuple = 'tuple',
Uint256 = 'uint256',
Uint8 = 'uint8',
Uint = 'uint',
diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json
index f83a6612d..58ba49509 100644
--- a/packages/fill-scenarios/CHANGELOG.json
+++ b/packages/fill-scenarios/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "1.0.14",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "1.0.13",
"changes": [
diff --git a/packages/fill-scenarios/CHANGELOG.md b/packages/fill-scenarios/CHANGELOG.md
index 3c39e1650..aa7df302e 100644
--- a/packages/fill-scenarios/CHANGELOG.md
+++ b/packages/fill-scenarios/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v1.0.14 - _November 28, 2018_
+
+ * Dependencies updated
+
## v1.0.13 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json
index e91ed8a4e..b29eb674c 100644
--- a/packages/fill-scenarios/package.json
+++ b/packages/fill-scenarios/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/fill-scenarios",
- "version": "1.0.13",
+ "version": "1.0.14",
"description": "0x order fill scenario generator",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -28,14 +28,14 @@
"typescript": "3.0.1"
},
"dependencies": {
- "@0x/abi-gen-wrappers": "^1.1.0",
- "@0x/base-contract": "^3.0.7",
- "@0x/contract-artifacts": "^1.1.0",
- "@0x/order-utils": "^3.0.3",
+ "@0x/abi-gen-wrappers": "^2.0.0",
+ "@0x/base-contract": "^3.0.8",
+ "@0x/contract-artifacts": "^1.1.2",
+ "@0x/order-utils": "^3.0.4",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"ethereum-types": "^1.1.2",
"ethers": "~4.0.4",
"lodash": "^4.17.5"
diff --git a/packages/instant/.DS_Store b/packages/instant/.DS_Store
new file mode 100644
index 000000000..9a0cceca6
--- /dev/null
+++ b/packages/instant/.DS_Store
Binary files differ
diff --git a/packages/instant/.dogfood.discharge.json b/packages/instant/.dogfood.discharge.json
index ca36b3861..651b3daa6 100644
--- a/packages/instant/.dogfood.discharge.json
+++ b/packages/instant/.dogfood.discharge.json
@@ -1,12 +1,12 @@
{
"domain": "0x-instant-dogfood",
- "build_command": "WEBPACK_OUTPUT_PATH=public yarn build:umd:prod",
+ "build_command": "WEBPACK_OUTPUT_PATH=public dotenv yarn build -- --env.discharge_target=dogfood",
"upload_directory": "public",
"index_key": "index.html",
"error_key": "index.html",
"trailing_slashes": true,
"cache": 3600,
- "aws_profile": "default",
+ "aws_profile": "0xproject",
"aws_region": "us-east-1",
"cdn": false,
"dns_configured": true
diff --git a/packages/instant/.env_example b/packages/instant/.env_example
new file mode 100644
index 000000000..234e64bbe
--- /dev/null
+++ b/packages/instant/.env_example
@@ -0,0 +1,7 @@
+INSTANT_ROLLBAR_PUBLISH_TOKEN=
+INSTANT_ROLLBAR_CLIENT_TOKEN=
+INSTANT_HEAP_ANALYTICS_ID_PRODUCTION=
+INSTANT_HEAP_ANALYTICS_ID_DEVELOPMENT=
+# if you want to report to heap or rollbar when building in development mode, you can use the following:
+# INSTANT_HEAP_FORCE_DEVELOPMENT=true
+# INSTANT_ROLLBAR_FORCE_DEVELOPMENT=true \ No newline at end of file
diff --git a/packages/instant/.gitignore b/packages/instant/.gitignore
index a99cea187..2e65f192d 100644
--- a/packages/instant/.gitignore
+++ b/packages/instant/.gitignore
@@ -1,3 +1,4 @@
public/instant.js
public/instant.js.map
-umd/* \ No newline at end of file
+umd/*
+.env \ No newline at end of file
diff --git a/packages/instant/.npmignore b/packages/instant/.npmignore
index a4f7810c0..563923652 100644
--- a/packages/instant/.npmignore
+++ b/packages/instant/.npmignore
@@ -2,4 +2,5 @@
*
*/
!lib/**/*
-!umd/**/* \ No newline at end of file
+!umd/**/*
+.env \ No newline at end of file
diff --git a/packages/instant/.production.discharge.json b/packages/instant/.production.discharge.json
new file mode 100644
index 000000000..1ce39fdd8
--- /dev/null
+++ b/packages/instant/.production.discharge.json
@@ -0,0 +1,13 @@
+{
+ "domain": "instant.0xproject.com",
+ "build_command": "dotenv yarn build -- --env.discharge_target=production",
+ "upload_directory": "umd",
+ "index_key": "instant.js",
+ "error_key": "404.html",
+ "trailing_slashes": true,
+ "cache": 3600,
+ "aws_profile": "0xproject",
+ "aws_region": "us-east-1",
+ "cdn": true,
+ "dns_configured": true
+}
diff --git a/packages/instant/.staging.discharge.json b/packages/instant/.staging.discharge.json
index c917a650b..844e3ca4e 100644
--- a/packages/instant/.staging.discharge.json
+++ b/packages/instant/.staging.discharge.json
@@ -1,12 +1,12 @@
{
"domain": "0x-instant-staging",
- "build_command": "WEBPACK_OUTPUT_PATH=public yarn build:umd:prod",
+ "build_command": "WEBPACK_OUTPUT_PATH=public dotenv yarn build -- --env.discharge_target=staging",
"upload_directory": "public",
"index_key": "index.html",
"error_key": "index.html",
"trailing_slashes": true,
"cache": 3600,
- "aws_profile": "default",
+ "aws_profile": "0xproject",
"aws_region": "us-east-1",
"cdn": false,
"dns_configured": true
diff --git a/packages/instant/README.md b/packages/instant/README.md
index b83a10508..2092b45d9 100644
--- a/packages/instant/README.md
+++ b/packages/instant/README.md
@@ -2,39 +2,11 @@
## Installation
-```bash
-yarn add @0x/instant
-```
-
-**Import**
-
-**CommonJS module**
-
-```typescript
-import { ZeroExInstant } from '@0x/instant';
-```
-
-or
-
-```javascript
-var ZeroExInstant = require('@0x/instant').ZeroExInstant;
-```
-
-If your project is in [TypeScript](https://www.typescriptlang.org/), add the following to your `tsconfig.json`:
-
-```json
-"compilerOptions": {
- "typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"],
-}
-```
-
-**UMD Module**
-
-The package is also available as a UMD module named `zeroExInstant`.
+The package is available as a UMD module named `zeroExInstant` at https://instant.0xproject.com/instant.js.
```html
<head>
- <script type="text/javascript" src="[zeroExInstantUMDPath]" charset="utf-8"></script>
+ <script type="text/javascript" src="https://instant.0xproject.com/instant.js" charset="utf-8"></script>
</head>
<body>
<div id="zeroExInstantContainer"></div>
@@ -48,23 +20,33 @@ The package is also available as a UMD module named `zeroExInstant`.
## Deploying
-You can deploy a work-in-progress version of 0x Instant at http://0x-instant-dogfood.s3-website-us-east-1.amazonaws.com for easy sharing.
+To run any of the following commands you need to configure your `.env` file. There is an example `.env_example` file to show you what values are required.
+
+You can deploy a work-in-progress version of 0x Instant at http://0x-instant-dogfood.s3-website-us-east-1.amazonaws.com/instant.js for easy sharing.
-To build and deploy the site run
+To build and deploy the bundle run
```
yarn deploy_dogfood
```
-We also have a staging bucket that is to be updated less frequently can be used to share instant externally: http://0x-instant-staging.s3-website-us-east-1.amazonaws.com/
+We also have a staging bucket that is to be updated less frequently can be used to share a beta version of instant externally: http://0x-instant-staging.s3-website-us-east-1.amazonaws.com/instant.js
-To build and deploy to this bucket, run
+To build and deploy to this bundle, run
```
yarn deploy_staging
```
-**NOTE: On deploying the site, it will say the site is available at a non-existent URL. Please ignore and use the (now updated) URL above.**
+Finally, we have our live production bundle that is only meant to be updated with stable, polished releases: https://instant.0xproject.com/instant.js
+
+To build and deploy to this bundle, run
+
+```
+yarn deploy_production
+```
+
+**NOTE: On deploying the site to staging and dogfood, it will say the site is available at a non-existent URL. Please ignore and use the (now updated) URL above.**
## Contributing
diff --git a/packages/instant/package.json b/packages/instant/package.json
index 4422dc83f..7d0bf6bec 100644
--- a/packages/instant/package.json
+++ b/packages/instant/package.json
@@ -1,20 +1,15 @@
{
"name": "@0x/instant",
- "version": "1.0.1",
+ "version": "1.0.2",
"engines": {
"node": ">=6.12"
},
"private": true,
"description": "0x Instant React Component",
- "main": "lib/index.js",
- "types": "lib/index.d.ts",
+ "main": "umd/instant.js",
"scripts": {
- "build": "yarn build:all",
- "build:all": "run-p build:umd:prod build:commonjs",
- "build:umd:prod": "webpack --mode production",
- "build:commonjs": "tsc -b",
+ "build": "webpack --mode production",
"build:ci": "yarn build",
- "watch_without_deps": "tsc -w",
"dev": "webpack-dev-server --mode development",
"lint": "tslint --format stylish --project .",
"test": "jest",
@@ -24,6 +19,7 @@
"clean": "shx rm -rf lib coverage scripts",
"deploy_dogfood": "discharge deploy -c .dogfood.discharge.json",
"deploy_staging": "discharge deploy -c .staging.discharge.json",
+ "deploy_production": "discharge deploy -c .production.discharge.json",
"manual:postpublish": "yarn build; node ./scripts/postpublish.js"
},
"config": {
@@ -46,14 +42,14 @@
"homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md",
"dependencies": {
"@0x/assert": "^1.0.18",
- "@0x/asset-buyer": "^3.0.1",
+ "@0x/asset-buyer": "^3.0.2",
"@0x/json-schemas": "^2.1.2",
- "@0x/order-utils": "^3.0.3",
- "@0x/subproviders": "^2.1.5",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/subproviders": "^2.1.6",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"bowser": "^1.9.4",
"copy-to-clipboard": "^3.0.8",
"ethereum-types": "^1.1.2",
@@ -64,6 +60,7 @@
"react-redux": "^5.0.7",
"redux": "^4.0.0",
"redux-devtools-extension": "^2.13.5",
+ "rollbar": "^2.5.0",
"styled-components": "^4.0.2",
"ts-optchain": "^0.1.1"
},
@@ -81,6 +78,7 @@
"@types/redux": "^3.6.0",
"@types/styled-components": "^4.0.1",
"awesome-typescript-loader": "^5.2.1",
+ "dotenv-cli": "^1.4.0",
"enzyme": "^3.6.0",
"enzyme-adapter-react-16": "^1.5.0",
"ip": "^1.1.5",
@@ -88,7 +86,9 @@
"make-promises-safe": "^1.1.0",
"npm-run-all": "^4.1.2",
"nyc": "^11.0.1",
+ "rollbar-sourcemap-webpack-plugin": "^2.4.0",
"shx": "^0.2.2",
+ "source-map-loader": "^0.2.4",
"svg-react-loader": "^0.4.6",
"ts-jest": "^23.10.3",
"tslint": "5.11.0",
@@ -99,6 +99,6 @@
"webpack-dev-server": "^3.1.9"
},
"publishConfig": {
- "access": "public"
+ "access": "private"
}
}
diff --git a/packages/instant/public/index.html b/packages/instant/public/index.html
index df39994ef..d10618c58 100644
--- a/packages/instant/public/index.html
+++ b/packages/instant/public/index.html
@@ -175,6 +175,7 @@
defaultSelectedAssetData: queryParams.getQueryParamValue('defaultSelectedAssetData'),
affiliateInfo: affiliateInfoOverride,
shouldDisablePushToHistory: !!queryParams.getQueryParamValue('shouldDisablePushToHistory'),
+ walletDisplayName: queryParams.getQueryParamValue('walletDisplayName') || undefined,
};
return renderOptionsOverrides;
};
diff --git a/packages/instant/src/assets/icons/zrx.svg b/packages/instant/src/assets/icons/zrx.svg
index 07518f551..da623710b 100644
--- a/packages/instant/src/assets/icons/zrx.svg
+++ b/packages/instant/src/assets/icons/zrx.svg
@@ -1,3 +1,6 @@
-<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M22.6726 18.5002L22.6787 18.5063L22.625 18.5868C22.641 18.558 22.6569 18.5291 22.6726 18.5002V18.5002H22.6726ZM22.7893 18.5002L21.3866 17.0053L17.9692 12.9131L19.6779 11.0919L17.1249 7.89955L18.8337 6.07835L20.0599 4.74999C20.663 5.26419 21.2058 5.83552 21.6882 6.46394C22.1707 7.09242 22.5828 7.77444 22.9245 8.5101C23.2663 9.2457 23.5309 10.0206 23.7186 10.8347C23.9062 11.6489 24 12.4916 24 13.3628C24 14.3055 23.8928 15.216 23.6784 16.0945C23.4703 16.9468 23.174 17.7487 22.7893 18.5L22.7893 18.5002ZM6.74427 15.3604L8.87512 18.1019L7.20654 19.8795L5.94009 21.2502L5.91999 21.2288C5.3169 20.7291 4.77415 20.1651 4.29169 19.5368C3.80923 18.9086 3.39714 18.2268 3.05539 17.4915C2.71365 16.7562 2.45567 15.9816 2.28144 15.1677C2.09382 14.3539 2 13.5114 2 12.6405C2 11.6981 2.10721 10.7913 2.32164 9.92041C2.53608 9.04943 2.83091 8.24276 3.20615 7.50025L4.61334 8.99943L8.45293 13.54L6.7442 15.3605L6.74427 15.3604ZM7.89849 8.87512L6.12088 7.20654L4.75015 5.94009L4.77157 5.91999C5.27132 5.3169 5.83531 4.77415 6.46352 4.29169C7.09178 3.80923 7.77357 3.39714 8.50886 3.05539C9.2442 2.71365 10.0188 2.45567 10.8326 2.28144C11.6465 2.09382 12.489 2 13.3599 2C14.3023 2 15.209 2.10721 16.08 2.32164C16.9509 2.53608 17.7576 2.83091 18.5001 3.20615L17.0866 4.55302L12.9101 8.0307L11.0896 6.32197L7.89835 8.87496L7.89849 8.87512ZM18.1019 17.1252L19.8795 18.7938L21.2503 20.0602L21.2288 20.0803C20.7291 20.6834 20.1651 21.2262 19.5369 21.7086C18.9086 22.1911 18.2268 22.6032 17.4916 22.9449C16.7562 23.2867 15.9817 23.5447 15.1678 23.7189C14.3539 23.9065 13.5114 24.0003 12.6405 24.0003C11.6981 24.0003 10.7914 23.8931 9.92046 23.6787C9.04952 23.4643 8.2428 23.1694 7.50029 22.7942L8.91381 21.4473L13.54 17.5474L15.3606 19.6782L18.1021 17.1252L18.1019 17.1252Z" fill="white"/>
+<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.62099 13.9044L6.3287 12.1375L4.20565 9.27256L1.50251 5.44771C0.547534 7.07749 0 8.97462 0 11C0 14.3552 1.50251 17.3593 3.8722 19.3768L7.30341 16.9518C6.13632 16.2248 5.19614 15.1662 4.62099 13.9044Z" fill="white"/>
+<path d="M8.09561 4.62099L9.86251 6.3287L12.7274 4.20565L16.5523 1.50251C14.9225 0.547534 13.0254 0 11 0C7.64475 0 4.64072 1.50251 2.62323 3.8722L5.04816 7.30341C5.77525 6.13632 6.83381 5.19614 8.09561 4.62099Z" fill="white"/>
+<path d="M15.6713 9.86251L17.7943 12.7274L20.4975 16.5523C21.4525 14.9225 22 13.0254 22 11C22 7.64475 20.4975 4.64072 18.1278 2.62323L14.6966 5.04816C15.8637 5.77525 16.8039 6.83381 17.379 8.09561L15.6713 9.86251Z" fill="white"/>
+<path d="M19.3768 18.1278L16.9518 14.6966C16.2248 15.8637 15.1662 16.8039 13.9044 17.379L12.1375 15.6713L9.27256 17.7943L5.44771 20.4975C7.07749 21.4525 8.97462 22 11 22C14.3552 22 17.3593 20.4975 19.3768 18.1278Z" fill="white"/>
</svg>
diff --git a/packages/instant/src/components/buy_button.tsx b/packages/instant/src/components/buy_button.tsx
index 8b6121e43..1489b94d4 100644
--- a/packages/instant/src/components/buy_button.tsx
+++ b/packages/instant/src/components/buy_button.tsx
@@ -1,4 +1,5 @@
import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
+import { AssetProxyId } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
@@ -7,7 +8,8 @@ import { oc } from 'ts-optchain';
import { WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX } from '../constants';
import { ColorOption } from '../style/theme';
-import { AffiliateInfo, ZeroExInstantError } from '../types';
+import { AffiliateInfo, Asset, ZeroExInstantError } from '../types';
+import { analytics } from '../util/analytics';
import { gasPriceEstimator } from '../util/gas_price_estimator';
import { util } from '../util/util';
@@ -20,6 +22,7 @@ export interface BuyButtonProps {
assetBuyer: AssetBuyer;
web3Wrapper: Web3Wrapper;
affiliateInfo?: AffiliateInfo;
+ selectedAsset?: Asset;
onValidationPending: (buyQuote: BuyQuote) => void;
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
onSignatureDenied: (buyQuote: BuyQuote) => void;
@@ -35,8 +38,12 @@ export class BuyButton extends React.Component<BuyButtonProps> {
onBuyFailure: util.boundNoop,
};
public render(): React.ReactNode {
- const { buyQuote, accountAddress } = this.props;
+ const { buyQuote, accountAddress, selectedAsset } = this.props;
const shouldDisableButton = _.isUndefined(buyQuote) || _.isUndefined(accountAddress);
+ const buttonText =
+ !_.isUndefined(selectedAsset) && selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20
+ ? `Buy ${selectedAsset.metaData.symbol.toUpperCase()}`
+ : 'Buy Now';
return (
<Button
width="100%"
@@ -44,7 +51,7 @@ export class BuyButton extends React.Component<BuyButtonProps> {
isDisabled={shouldDisableButton}
fontColor={ColorOption.white}
>
- Buy
+ {buttonText}
</Button>
);
}
@@ -59,6 +66,7 @@ export class BuyButton extends React.Component<BuyButtonProps> {
// if we don't have a balance for the user, let the transaction through, it will be handled by the wallet
const hasSufficientEth = _.isUndefined(accountEthBalanceInWei) || accountEthBalanceInWei.gte(ethNeededForBuy);
if (!hasSufficientEth) {
+ analytics.trackBuyNotEnoughEth(buyQuote);
this.props.onValidationFail(buyQuote, ZeroExInstantError.InsufficientETH);
return;
}
@@ -66,6 +74,7 @@ export class BuyButton extends React.Component<BuyButtonProps> {
const gasInfo = await gasPriceEstimator.getGasInfoAsync();
const feeRecipient = oc(affiliateInfo).feeRecipient();
try {
+ analytics.trackBuyStarted(buyQuote);
txHash = await assetBuyer.executeBuyQuoteAsync(buyQuote, {
feeRecipient,
takerAddress: accountAddress,
@@ -74,9 +83,11 @@ export class BuyButton extends React.Component<BuyButtonProps> {
} catch (e) {
if (e instanceof Error) {
if (e.message === AssetBuyerError.SignatureRequestDenied) {
+ analytics.trackBuySignatureDenied(buyQuote);
this.props.onSignatureDenied(buyQuote);
return;
} else if (e.message === AssetBuyerError.TransactionValueTooLow) {
+ analytics.trackBuySimulationFailed(buyQuote);
this.props.onValidationFail(buyQuote, AssetBuyerError.TransactionValueTooLow);
return;
}
@@ -87,14 +98,17 @@ export class BuyButton extends React.Component<BuyButtonProps> {
const expectedEndTimeUnix = startTimeUnix + gasInfo.estimatedTimeMs;
this.props.onBuyProcessing(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
try {
+ analytics.trackBuyTxSubmitted(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
await web3Wrapper.awaitTransactionSuccessAsync(txHash);
} catch (e) {
if (e instanceof Error && e.message.startsWith(WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX)) {
+ analytics.trackBuyTxFailed(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
this.props.onBuyFailure(buyQuote, txHash);
return;
}
throw e;
}
+ analytics.trackBuyTxSucceeded(buyQuote, txHash, startTimeUnix, expectedEndTimeUnix);
this.props.onBuySuccess(buyQuote, txHash);
};
}
diff --git a/packages/instant/src/components/buy_order_progress.tsx b/packages/instant/src/components/buy_order_progress.tsx
index 6568de91b..a19f5a4d0 100644
--- a/packages/instant/src/components/buy_order_progress.tsx
+++ b/packages/instant/src/components/buy_order_progress.tsx
@@ -21,7 +21,7 @@ export const BuyOrderProgress: React.StatelessComponent<BuyOrderProgressProps> =
const hasEnded = buyOrderState.processState !== OrderProcessState.Processing;
const expectedTimeMs = progress.expectedEndTimeUnix - progress.startTimeUnix;
return (
- <Container padding="20px 20px 0px 20px" width="100%">
+ <Container width="100%" padding="20px 20px 0px 20px">
<Container marginBottom="5px">
<TimeCounter estimatedTimeMs={expectedTimeMs} hasEnded={hasEnded} key={progress.startTimeUnix} />
</Container>
diff --git a/packages/instant/src/components/buy_order_state_buttons.tsx b/packages/instant/src/components/buy_order_state_buttons.tsx
index e563bec73..833818900 100644
--- a/packages/instant/src/components/buy_order_state_buttons.tsx
+++ b/packages/instant/src/components/buy_order_state_buttons.tsx
@@ -4,7 +4,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
import * as React from 'react';
import { ColorOption } from '../style/theme';
-import { AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types';
+import { AffiliateInfo, Asset, OrderProcessState, ZeroExInstantError } from '../types';
import { BuyButton } from './buy_button';
import { PlacingOrderButton } from './placing_order_button';
@@ -21,6 +21,7 @@ export interface BuyOrderStateButtonProps {
assetBuyer: AssetBuyer;
web3Wrapper: Web3Wrapper;
affiliateInfo?: AffiliateInfo;
+ selectedAsset?: Asset;
onViewTransaction: () => void;
onValidationPending: (buyQuote: BuyQuote) => void;
onValidationFail: (buyQuote: BuyQuote, errorMessage: AssetBuyerError | ZeroExInstantError) => void;
@@ -60,6 +61,7 @@ export const BuyOrderStateButtons: React.StatelessComponent<BuyOrderStateButtonP
assetBuyer={props.assetBuyer}
web3Wrapper={props.web3Wrapper}
affiliateInfo={props.affiliateInfo}
+ selectedAsset={props.selectedAsset}
onValidationPending={props.onValidationPending}
onValidationFail={props.onValidationFail}
onSignatureDenied={props.onSignatureDenied}
diff --git a/packages/instant/src/components/erc20_asset_amount_input.tsx b/packages/instant/src/components/erc20_asset_amount_input.tsx
index ff900842a..4da82eb73 100644
--- a/packages/instant/src/components/erc20_asset_amount_input.tsx
+++ b/packages/instant/src/components/erc20_asset_amount_input.tsx
@@ -113,7 +113,7 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput
);
};
private readonly _renderChevronIcon = (): React.ReactNode => {
- if (!this._areMultipleAssetsAvailable()) {
+ if (!this._areAnyAssetsAvailable()) {
return null;
}
return (
@@ -134,14 +134,14 @@ export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInput
// We don't want to allow opening the token selection panel if there are no assets.
// Since styles are inferred from the presence of a click handler, we want to return undefined
// instead of providing a noop.
- if (!this._areMultipleAssetsAvailable() || _.isUndefined(this.props.onSelectAssetClick)) {
+ if (!this._areAnyAssetsAvailable() || _.isUndefined(this.props.onSelectAssetClick)) {
return undefined;
}
return this._handleSelectAssetClick;
};
- private readonly _areMultipleAssetsAvailable = (): boolean => {
+ private readonly _areAnyAssetsAvailable = (): boolean => {
const { numberOfAssetsAvailable } = this.props;
- return !_.isUndefined(numberOfAssetsAvailable) && numberOfAssetsAvailable > 1;
+ return !_.isUndefined(numberOfAssetsAvailable) && numberOfAssetsAvailable > 0;
};
private readonly _handleSelectAssetClick = (): void => {
if (this.props.onSelectAssetClick) {
diff --git a/packages/instant/src/components/erc20_token_selector.tsx b/packages/instant/src/components/erc20_token_selector.tsx
index 1b1921acb..f7d5a4fe4 100644
--- a/packages/instant/src/components/erc20_token_selector.tsx
+++ b/packages/instant/src/components/erc20_token_selector.tsx
@@ -3,6 +3,7 @@ import * as React from 'react';
import { ColorOption } from '../style/theme';
import { ERC20Asset } from '../types';
+import { analytics } from '../util/analytics';
import { assetUtils } from '../util/asset';
import { SearchInput } from './search_input';
@@ -18,12 +19,12 @@ export interface ERC20TokenSelectorProps {
}
export interface ERC20TokenSelectorState {
- searchQuery?: string;
+ searchQuery: string;
}
export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps> {
public state: ERC20TokenSelectorState = {
- searchQuery: undefined,
+ searchQuery: '',
};
public render(): React.ReactNode {
const { tokens, onTokenSelect } = this.props;
@@ -57,13 +58,14 @@ export class ERC20TokenSelector extends React.Component<ERC20TokenSelectorProps>
this.setState({
searchQuery,
});
+ analytics.trackTokenSelectorSearched(searchQuery);
};
private readonly _isTokenQueryMatch = (token: ERC20Asset): boolean => {
const { searchQuery } = this.state;
- if (_.isUndefined(searchQuery)) {
+ const searchQueryLowerCase = searchQuery.toLowerCase().trim();
+ if (searchQueryLowerCase === '') {
return true;
}
- const searchQueryLowerCase = searchQuery.toLowerCase();
const tokenName = token.metaData.name.toLowerCase();
const tokenSymbol = token.metaData.symbol.toLowerCase();
return _.startsWith(tokenSymbol, searchQueryLowerCase) || _.startsWith(tokenName, searchQueryLowerCase);
diff --git a/packages/instant/src/components/install_wallet_panel_content.tsx b/packages/instant/src/components/install_wallet_panel_content.tsx
index 88c26f59c..481d82da0 100644
--- a/packages/instant/src/components/install_wallet_panel_content.tsx
+++ b/packages/instant/src/components/install_wallet_panel_content.tsx
@@ -8,7 +8,9 @@ import {
} from '../constants';
import { ColorOption } from '../style/theme';
import { Browser } from '../types';
+import { analytics } from '../util/analytics';
import { envUtil } from '../util/env';
+import { util } from '../util/util';
import { MetaMaskLogo } from './meta_mask_logo';
import { StandardPanelContent, StandardPanelContentProps } from './standard_panel_content';
@@ -45,6 +47,10 @@ export class InstallWalletPanelContent extends React.Component<InstallWalletPane
default:
break;
}
+ const onActionClick = () => {
+ analytics.trackInstallWalletModalClickedGet();
+ util.createOpenUrlInNewWindow(actionUrl)();
+ };
return {
image: <MetaMaskLogo width={85} height={80} />,
title: 'Install MetaMask',
@@ -52,10 +58,11 @@ export class InstallWalletPanelContent extends React.Component<InstallWalletPane
moreInfoSettings: {
href: META_MASK_SITE_URL,
text: 'What is MetaMask?',
+ onClick: analytics.trackInstallWalletModalClickedExplanation,
},
action: (
<Button
- href={actionUrl}
+ onClick={onActionClick}
width="100%"
fontColor={ColorOption.white}
backgroundColor={ColorOption.darkOrange}
diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx
index ace577824..117f9dd5f 100644
--- a/packages/instant/src/components/instant_heading.tsx
+++ b/packages/instant/src/components/instant_heading.tsx
@@ -32,7 +32,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
public render(): React.ReactNode {
const iconOrAmounts = this._renderIcon() || this._renderAmountsSection();
return (
- <Container backgroundColor={ColorOption.primaryColor} padding="20px" width="100%">
+ <Container backgroundColor={ColorOption.primaryColor} width="100%" padding="20px">
<Container marginBottom="5px">
<Text
letterSpacing="1px"
@@ -107,7 +107,14 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
private readonly _renderEthAmount = (): React.ReactNode => {
return (
- <Text fontSize="16px" textAlign="right" width="100%" fontColor={ColorOption.white} fontWeight={500}>
+ <Text
+ fontSize="16px"
+ textAlign="right"
+ width="100%"
+ fontColor={ColorOption.white}
+ fontWeight={500}
+ noWrap={true}
+ >
{format.ethBaseUnitAmount(
this.props.totalEthBaseUnitAmount,
4,
@@ -119,7 +126,7 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> {
private readonly _renderDollarAmount = (): React.ReactNode => {
return (
- <Text fontSize="16px" textAlign="right" width="100%" fontColor={ColorOption.white}>
+ <Text fontSize="16px" textAlign="right" width="100%" fontColor={ColorOption.white} noWrap={true}>
{format.ethBaseUnitAmountInUsd(
this.props.totalEthBaseUnitAmount,
this.props.ethUsdPrice,
diff --git a/packages/instant/src/components/order_details.tsx b/packages/instant/src/components/order_details.tsx
index 5fc956e1c..a8e0e2513 100644
--- a/packages/instant/src/components/order_details.tsx
+++ b/packages/instant/src/components/order_details.tsx
@@ -34,7 +34,7 @@ export class OrderDetails extends React.Component<OrderDetailsProps> {
? assetEthBaseUnitAmount.div(selectedAssetUnitAmount).ceil()
: undefined;
return (
- <Container padding="20px" width="100%" flexGrow={1}>
+ <Container width="100%" flexGrow={1} padding="20px 20px 0px 20px">
<Container marginBottom="10px">
<Text
letterSpacing="1px"
diff --git a/packages/instant/src/components/payment_method.tsx b/packages/instant/src/components/payment_method.tsx
index ebcd62f35..7c93f1d1c 100644
--- a/packages/instant/src/components/payment_method.tsx
+++ b/packages/instant/src/components/payment_method.tsx
@@ -18,7 +18,7 @@ import { WalletPrompt } from './wallet_prompt';
export interface PaymentMethodProps {
account: Account;
network: Network;
- walletName: string;
+ walletDisplayName: string;
onInstallWalletClick: () => void;
onUnlockWalletClick: () => void;
}
@@ -26,7 +26,7 @@ export interface PaymentMethodProps {
export class PaymentMethod extends React.Component<PaymentMethodProps> {
public render(): React.ReactNode {
return (
- <Container padding="20px" width="100%">
+ <Container width="100%" height="120px" padding="20px 20px 0px 20px">
<Container marginBottom="12px">
<Flex justify="space-between">
<Text
@@ -62,11 +62,11 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> {
if (account.state === AccountState.Ready || account.state === AccountState.Locked) {
const circleColor: ColorOption = account.state === AccountState.Ready ? ColorOption.green : ColorOption.red;
return (
- <Flex>
+ <Flex align="center">
<Circle diameter={8} color={circleColor} />
- <Container marginLeft="3px">
- <Text fontColor={ColorOption.darkGrey} fontSize="12px">
- {this.props.walletName}
+ <Container marginLeft="5px">
+ <Text fontColor={ColorOption.darkGrey} fontSize="12px" lineHeight="30px">
+ {this.props.walletDisplayName}
</Text>
</Container>
</Flex>
@@ -83,16 +83,19 @@ export class PaymentMethod extends React.Component<PaymentMethodProps> {
const colors = { primaryColor, secondaryColor };
switch (account.state) {
case AccountState.Loading:
- // Just take up the same amount of space as the other states.
- return <Container height="52px" />;
+ return null;
case AccountState.Locked:
return (
<WalletPrompt
onClick={this.props.onUnlockWalletClick}
- image={<Icon width={13} icon="lock" color={ColorOption.black} />}
+ image={
+ <Container position="relative" top="2px">
+ <Icon width={13} icon="lock" color={ColorOption.black} />
+ </Container>
+ }
{...colors}
>
- Please Unlock {this.props.walletName}
+ Click to Connect {this.props.walletDisplayName}
</WalletPrompt>
);
case AccountState.None:
diff --git a/packages/instant/src/components/payment_method_dropdown.tsx b/packages/instant/src/components/payment_method_dropdown.tsx
index b330dbcd6..872ac0831 100644
--- a/packages/instant/src/components/payment_method_dropdown.tsx
+++ b/packages/instant/src/components/payment_method_dropdown.tsx
@@ -1,8 +1,9 @@
import { BigNumber } from '@0x/utils';
-import copy from 'copy-to-clipboard';
+import * as copy from 'copy-to-clipboard';
import * as React from 'react';
import { Network } from '../types';
+import { analytics } from '../util/analytics';
import { envUtil } from '../util/env';
import { etherscanUtil } from '../util/etherscan';
import { format } from '../util/format';
@@ -20,7 +21,14 @@ export class PaymentMethodDropdown extends React.Component<PaymentMethodDropdown
const { accountAddress, accountEthBalanceInWei } = this.props;
const value = format.ethAddress(accountAddress);
const label = format.ethBaseUnitAmount(accountEthBalanceInWei, 4, '') as string;
- return <Dropdown value={value} label={label} items={this._getDropdownItemConfigs()} />;
+ return (
+ <Dropdown
+ value={value}
+ label={label}
+ items={this._getDropdownItemConfigs()}
+ onOpen={analytics.trackPaymentMethodDropdownOpened}
+ />
+ );
}
private readonly _getDropdownItemConfigs = (): DropdownItemConfig[] => {
if (envUtil.isMobileOperatingSystem()) {
@@ -37,11 +45,15 @@ export class PaymentMethodDropdown extends React.Component<PaymentMethodDropdown
return [viewOnEtherscan, copyAddressToClipboard];
};
private readonly _handleEtherscanClick = (): void => {
+ analytics.trackPaymentMethodOpenedEtherscan();
+
const { accountAddress, network } = this.props;
const etherscanUrl = etherscanUtil.getEtherScanEthAddressIfExists(accountAddress, network);
window.open(etherscanUrl, '_blank');
};
private readonly _handleCopyToClipboardClick = (): void => {
+ analytics.trackPaymentMethodCopiedAddress();
+
const { accountAddress } = this.props;
copy(accountAddress);
};
diff --git a/packages/instant/src/components/scaling_amount_input.tsx b/packages/instant/src/components/scaling_amount_input.tsx
index 0861bbe05..86aca5a65 100644
--- a/packages/instant/src/components/scaling_amount_input.tsx
+++ b/packages/instant/src/components/scaling_amount_input.tsx
@@ -4,6 +4,7 @@ import * as React from 'react';
import { Maybe } from '../types';
+import { GIT_SHA, MAGIC_TRIGGER_ERROR_INPUT, MAGIC_TRIGGER_ERROR_MESSAGE, NPM_PACKAGE_VERSION } from '../constants';
import { ColorOption } from '../style/theme';
import { maybeBigNumberUtil } from '../util/maybe_big_number';
import { util } from '../util/util';
@@ -71,6 +72,10 @@ export class ScalingAmountInput extends React.Component<ScalingAmountInputProps,
);
}
private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
+ if (event.target.value === MAGIC_TRIGGER_ERROR_INPUT) {
+ throw new Error(`${MAGIC_TRIGGER_ERROR_MESSAGE} git: ${GIT_SHA}, npm: ${NPM_PACKAGE_VERSION}`);
+ }
+
const sanitizedValue = event.target.value.replace(/[^0-9.]/g, ''); // only allow numbers and "."
this.setState({
stringValue: sanitizedValue,
diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx
index 129162a74..791692257 100644
--- a/packages/instant/src/components/scaling_input.tsx
+++ b/packages/instant/src/components/scaling_input.tsx
@@ -98,6 +98,12 @@ export class ScalingInput extends React.Component<ScalingInputProps, ScalingInpu
inputWidthPx: this._getInputWidthInPx(),
};
}
+ public componentDidMount(): void {
+ // Trigger an initial notification of the calculated fontSize.
+ const currentPhase = ScalingInput.getPhaseFromProps(this.props);
+ const currentFontSize = ScalingInput.calculateFontSizeFromProps(this.props, currentPhase);
+ this.props.onFontSizeChange(currentFontSize);
+ }
public componentDidUpdate(
prevProps: ScalingInputProps,
prevState: ScalingInputState,
diff --git a/packages/instant/src/components/search_input.tsx b/packages/instant/src/components/search_input.tsx
index 3a693b9f8..71bc18915 100644
--- a/packages/instant/src/components/search_input.tsx
+++ b/packages/instant/src/components/search_input.tsx
@@ -13,10 +13,10 @@ export interface SearchInputProps extends InputProps {
}
export const SearchInput: React.StatelessComponent<SearchInputProps> = props => (
- <Container backgroundColor={props.backgroundColor} borderRadius="3px" padding=".5em .3em">
- <Flex justify="flex-start" align="flex-end">
- <Icon width={14} height={14} icon="search" color={ColorOption.lightGrey} padding="0px 12px" />
- <Input {...props} fontSize="14px" fontColor={props.fontColor} />
+ <Container backgroundColor={props.backgroundColor} borderRadius="3px" padding=".5em .5em">
+ <Flex justify="flex-start" align="center">
+ <Icon width={14} height={14} icon="search" color={ColorOption.lightGrey} padding="2px 12px" />
+ <Input {...props} type="search" fontSize="16px" fontColor={props.fontColor} />
</Flex>
</Container>
);
diff --git a/packages/instant/src/components/standard_panel_content.tsx b/packages/instant/src/components/standard_panel_content.tsx
index 582b3318e..79b7bff24 100644
--- a/packages/instant/src/components/standard_panel_content.tsx
+++ b/packages/instant/src/components/standard_panel_content.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { ColorOption } from '../style/theme';
+import { util } from '../util/util';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
@@ -9,6 +10,7 @@ import { Text } from './ui/text';
export interface MoreInfoSettings {
text: string;
href: string;
+ onClick?: () => void;
}
export interface StandardPanelContentProps {
@@ -21,6 +23,15 @@ export interface StandardPanelContentProps {
const SPACING_BETWEEN_PX = '20px';
+const onMoreInfoClick = (href: string, onClick?: () => void) => {
+ return () => {
+ if (onClick) {
+ onClick();
+ }
+ util.createOpenUrlInNewWindow(href)();
+ };
+};
+
export const StandardPanelContent: React.StatelessComponent<StandardPanelContentProps> = ({
image,
title,
@@ -50,7 +61,7 @@ export const StandardPanelContent: React.StatelessComponent<StandardPanelContent
fontSize="13px"
textDecorationLine="underline"
fontColor={ColorOption.lightGrey}
- href={moreInfoSettings.href}
+ onClick={onMoreInfoClick(moreInfoSettings.href, moreInfoSettings.onClick)}
>
{moreInfoSettings.text}
</Text>
diff --git a/packages/instant/src/components/standard_sliding_panel.tsx b/packages/instant/src/components/standard_sliding_panel.tsx
index f587ff79a..9f517d273 100644
--- a/packages/instant/src/components/standard_sliding_panel.tsx
+++ b/packages/instant/src/components/standard_sliding_panel.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
-import { SlideAnimationState, StandardSlidingPanelContent, StandardSlidingPanelSettings } from '../types';
+import { StandardSlidingPanelContent, StandardSlidingPanelSettings } from '../types';
import { InstallWalletPanelContent } from './install_wallet_panel_content';
import { SlidingPanel } from './sliding_panel';
diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx
index 8465b9cd0..fb3927088 100644
--- a/packages/instant/src/components/timed_progress_bar.tsx
+++ b/packages/instant/src/components/timed_progress_bar.tsx
@@ -1,8 +1,9 @@
import * as _ from 'lodash';
+import { transparentize } from 'polished';
import * as React from 'react';
import { PROGRESS_FINISH_ANIMATION_TIME_MS, PROGRESS_STALL_AT_WIDTH } from '../constants';
-import { ColorOption, css, keyframes, styled } from '../style/theme';
+import { ColorOption, css, keyframes, styled, ThemeConsumer } from '../style/theme';
import { Container } from './ui/container';
@@ -93,8 +94,16 @@ export interface ProgressBarProps extends ProgressProps {}
export const ProgressBar: React.ComponentType<ProgressBarProps & React.ClassAttributes<{}>> = React.forwardRef(
(props, ref) => (
- <Container width="100%" backgroundColor={ColorOption.lightGrey} borderRadius="6px">
- <Progress {...props} ref={ref as any} />
- </Container>
+ <ThemeConsumer>
+ {theme => (
+ <Container
+ width="100%"
+ borderRadius="6px"
+ rawBackgroundColor={transparentize(0.5, theme[ColorOption.primaryColor])}
+ >
+ <Progress {...props} ref={ref as any} />
+ </Container>
+ )}
+ </ThemeConsumer>
),
);
diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx
index 4dafe1386..636eb8fc9 100644
--- a/packages/instant/src/components/ui/container.tsx
+++ b/packages/instant/src/components/ui/container.tsx
@@ -27,7 +27,9 @@ export interface ContainerProps {
borderBottom?: string;
className?: string;
backgroundColor?: ColorOption;
+ rawBackgroundColor?: string;
hasBoxShadow?: boolean;
+ isHidden?: boolean;
zIndex?: number;
whiteSpace?: string;
opacity?: number;
@@ -38,6 +40,16 @@ export interface ContainerProps {
flexGrow?: string | number;
}
+const getBackgroundColor = (theme: any, backgroundColor?: ColorOption, rawBackgroundColor?: string): string => {
+ if (backgroundColor) {
+ return theme[backgroundColor] as string;
+ }
+ if (rawBackgroundColor) {
+ return rawBackgroundColor;
+ }
+ return 'none';
+};
+
export const Container =
styled.div <
ContainerProps >
@@ -65,12 +77,14 @@ export const Container =
${props => cssRuleIfExists(props, 'opacity')}
${props => cssRuleIfExists(props, 'cursor')}
${props => cssRuleIfExists(props, 'overflow')}
+ ${props => (props.overflow === 'scroll' ? `-webkit-overflow-scrolling: touch` : '')};
${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')};
${props => props.display && stylesForMedia<string>('display', props.display)}
${props => props.width && stylesForMedia<string>('width', props.width)}
${props => props.height && stylesForMedia<string>('height', props.height)}
${props => props.borderRadius && stylesForMedia<string>('border-radius', props.borderRadius)}
- background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')};
+ ${props => (props.isHidden ? 'visibility: hidden;' : '')}
+ background-color: ${props => getBackgroundColor(props.theme, props.backgroundColor, props.rawBackgroundColor)};
border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')};
&:hover {
${props =>
diff --git a/packages/instant/src/components/ui/dropdown.tsx b/packages/instant/src/components/ui/dropdown.tsx
index 3a23f456d..02e87d639 100644
--- a/packages/instant/src/components/ui/dropdown.tsx
+++ b/packages/instant/src/components/ui/dropdown.tsx
@@ -19,6 +19,7 @@ export interface DropdownProps {
value: string;
label?: string;
items: DropdownItemConfig[];
+ onOpen?: () => void;
}
export interface DropdownState {
@@ -97,9 +98,14 @@ export class Dropdown extends React.Component<DropdownProps, DropdownState> {
if (_.isEmpty(this.props.items)) {
return;
}
+ const isOpen = !this.state.isOpen;
this.setState({
- isOpen: !this.state.isOpen,
+ isOpen,
});
+
+ if (isOpen && this.props.onOpen) {
+ this.props.onOpen();
+ }
};
private readonly _closeDropdown = (): void => {
this.setState({
diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx
index 1ea5d8fe1..863c970ef 100644
--- a/packages/instant/src/components/ui/input.tsx
+++ b/packages/instant/src/components/ui/input.tsx
@@ -10,6 +10,7 @@ export interface InputProps {
fontSize?: string;
fontColor?: ColorOption;
placeholder?: string;
+ type?: string;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}
diff --git a/packages/instant/src/components/ui/overlay.tsx b/packages/instant/src/components/ui/overlay.tsx
index f67d6fb2f..0b5eaf299 100644
--- a/packages/instant/src/components/ui/overlay.tsx
+++ b/packages/instant/src/components/ui/overlay.tsx
@@ -33,7 +33,7 @@ export const Overlay =
Overlay.defaultProps = {
zIndex: zIndex.overlayDefault,
- backgroundColor: generateOverlayBlack(0.6),
+ backgroundColor: generateOverlayBlack(0.7),
};
Overlay.displayName = 'Overlay';
diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx
index 8e573d7b9..282477758 100644
--- a/packages/instant/src/components/ui/text.tsx
+++ b/packages/instant/src/components/ui/text.tsx
@@ -1,4 +1,3 @@
-import { darken } from 'polished';
import * as React from 'react';
import { ColorOption, styled } from '../../style/theme';
@@ -31,7 +30,7 @@ export const Text: React.StatelessComponent<TextProps> = ({ href, onClick, ...re
return <StyledText {...rest} onClick={computedOnClick} />;
};
-const darkenOnHoverAmount = 0.3;
+const opacityOnHoverAmount = 0.5;
export const StyledText =
styled.div <
TextProps >
@@ -56,8 +55,7 @@ export const StyledText =
${props => (props.textAlign ? `text-align: ${props.textAlign}` : '')};
${props => (props.width ? `width: ${props.width}` : '')};
&:hover {
- ${props =>
- props.onClick ? `color: ${darken(darkenOnHoverAmount, props.theme[props.fontColor || 'white'])}` : ''};
+ ${props => (props.onClick ? `opacity: ${opacityOnHoverAmount};` : '')};
}
}
`;
diff --git a/packages/instant/src/components/wallet_prompt.tsx b/packages/instant/src/components/wallet_prompt.tsx
index a0b3ae457..c07cfe7b5 100644
--- a/packages/instant/src/components/wallet_prompt.tsx
+++ b/packages/instant/src/components/wallet_prompt.tsx
@@ -21,7 +21,7 @@ export const WalletPrompt: React.StatelessComponent<WalletPromptProps> = ({
primaryColor,
}) => (
<Container
- padding="14.5px"
+ padding="10px"
border={`1px solid ${primaryColor}`}
backgroundColor={secondaryColor}
width="100%"
@@ -33,7 +33,7 @@ export const WalletPrompt: React.StatelessComponent<WalletPromptProps> = ({
<Flex>
{image}
<Container marginLeft="10px">
- <Text fontSize="16px" fontColor={primaryColor}>
+ <Text fontSize="16px" fontColor={primaryColor} fontWeight="500">
{children}
</Text>
</Container>
diff --git a/packages/instant/src/components/zero_ex_instant_container.tsx b/packages/instant/src/components/zero_ex_instant_container.tsx
index 47c938472..0337c7714 100644
--- a/packages/instant/src/components/zero_ex_instant_container.tsx
+++ b/packages/instant/src/components/zero_ex_instant_container.tsx
@@ -11,21 +11,20 @@ import { SelectedAssetBuyOrderStateButtons } from '../containers/selected_asset_
import { SelectedAssetInstantHeading } from '../containers/selected_asset_instant_heading';
import { ColorOption } from '../style/theme';
import { zIndex } from '../style/z_index';
-import { OrderProcessState, SlideAnimationState } from '../types';
+import { SlideAnimationState } from '../types';
+import { analytics, TokenSelectorClosedVia } from '../util/analytics';
import { CSSReset } from './css_reset';
import { SlidingPanel } from './sliding_panel';
import { Container } from './ui/container';
import { Flex } from './ui/flex';
-export interface ZeroExInstantContainerProps {
- orderProcessState: OrderProcessState;
-}
+export interface ZeroExInstantContainerProps {}
export interface ZeroExInstantContainerState {
tokenSelectionPanelAnimationState: SlideAnimationState;
}
-export class ZeroExInstantContainer extends React.Component<{}, ZeroExInstantContainerState> {
+export class ZeroExInstantContainer extends React.Component<ZeroExInstantContainerProps, ZeroExInstantContainerState> {
public state = {
tokenSelectionPanelAnimationState: 'none' as SlideAnimationState,
};
@@ -60,9 +59,9 @@ export class ZeroExInstantContainer extends React.Component<{}, ZeroExInstantCon
</Flex>
<SlidingPanel
animationState={this.state.tokenSelectionPanelAnimationState}
- onClose={this._handlePanelClose}
+ onClose={this._handlePanelCloseClickedX}
>
- <AvailableERC20TokenSelector onTokenSelect={this._handlePanelClose} />
+ <AvailableERC20TokenSelector onTokenSelect={this._handlePanelCloseAfterChose} />
</SlidingPanel>
<CurrentStandardSlidingPanel />
</Container>
@@ -71,7 +70,7 @@ export class ZeroExInstantContainer extends React.Component<{}, ZeroExInstantCon
marginTop="10px"
marginLeft="auto"
marginRight="auto"
- width="140px"
+ width="108px"
>
<a href={ZERO_EX_SITE_URL} target="_blank">
<PoweredByLogo />
@@ -82,11 +81,19 @@ export class ZeroExInstantContainer extends React.Component<{}, ZeroExInstantCon
);
}
private readonly _handleSymbolClick = (): void => {
+ analytics.trackTokenSelectorOpened();
this.setState({
tokenSelectionPanelAnimationState: 'slidIn',
});
};
- private readonly _handlePanelClose = (): void => {
+ private readonly _handlePanelCloseClickedX = (): void => {
+ this._handlePanelClose(TokenSelectorClosedVia.ClickedX);
+ };
+ private readonly _handlePanelCloseAfterChose = (): void => {
+ this._handlePanelClose(TokenSelectorClosedVia.TokenChose);
+ };
+ private readonly _handlePanelClose = (closedVia: TokenSelectorClosedVia): void => {
+ analytics.trackTokenSelectorClosed(closedVia);
this.setState({
tokenSelectionPanelAnimationState: 'slidOut',
});
diff --git a/packages/instant/src/components/zero_ex_instant_overlay.tsx b/packages/instant/src/components/zero_ex_instant_overlay.tsx
index 2856ea3e3..96e560691 100644
--- a/packages/instant/src/components/zero_ex_instant_overlay.tsx
+++ b/packages/instant/src/components/zero_ex_instant_overlay.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { ZeroExInstantContainer } from '../components/zero_ex_instant_container';
+import { MAIN_CONTAINER_DIV_CLASS, OVERLAY_CLOSE_BUTTON_DIV_CLASS, OVERLAY_DIV_CLASS } from '../constants';
import { ColorOption } from '../style/theme';
import { Container } from './ui/container';
@@ -18,9 +19,15 @@ export const ZeroExInstantOverlay: React.StatelessComponent<ZeroExInstantOverlay
const { onClose, zIndex, ...rest } = props;
return (
<ZeroExInstantProvider {...rest}>
- <Overlay zIndex={zIndex}>
+ <Overlay zIndex={zIndex} className={OVERLAY_DIV_CLASS}>
<Flex height="100vh">
- <Container position="absolute" top="0px" right="0px" display={{ default: 'initial', sm: 'none' }}>
+ <Container
+ className={OVERLAY_CLOSE_BUTTON_DIV_CLASS}
+ position="absolute"
+ top="0px"
+ right="0px"
+ display={{ default: 'initial', sm: 'none' }}
+ >
<Icon
height={18}
width={18}
@@ -30,7 +37,11 @@ export const ZeroExInstantOverlay: React.StatelessComponent<ZeroExInstantOverlay
padding="2em 2em"
/>
</Container>
- <Container width={{ default: 'auto', sm: '100%' }} height={{ default: 'auto', sm: '100%' }}>
+ <Container
+ width={{ default: 'auto', sm: '100%' }}
+ height={{ default: 'auto', sm: '100%' }}
+ className={MAIN_CONTAINER_DIV_CLASS}
+ >
<ZeroExInstantContainer />
</Container>
</Flex>
diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx
index fe34c4466..dae9124c6 100644
--- a/packages/instant/src/components/zero_ex_instant_provider.tsx
+++ b/packages/instant/src/components/zero_ex_instant_provider.tsx
@@ -11,10 +11,11 @@ import { asyncData } from '../redux/async_data';
import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer';
import { store, Store } from '../redux/store';
import { fonts } from '../style/fonts';
-import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types';
+import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource, QuoteFetchOrigin } from '../types';
import { analytics, disableAnalytics } from '../util/analytics';
import { assetUtils } from '../util/asset';
import { errorFlasher } from '../util/error_flasher';
+import { setupRollbar } from '../util/error_reporter';
import { gasPriceEstimator } from '../util/gas_price_estimator';
import { Heartbeater } from '../util/heartbeater';
import { generateAccountHeartbeater, generateBuyQuoteHeartbeater } from '../util/heartbeater_factory';
@@ -29,6 +30,7 @@ export interface ZeroExInstantProviderRequiredProps {
export interface ZeroExInstantProviderOptionalProps {
provider: Provider;
+ walletDisplayName: string;
availableAssetDatas: string[];
defaultAssetBuyAmount: number;
defaultSelectedAssetData: string;
@@ -66,6 +68,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
...defaultState,
providerState,
network: networkId,
+ walletDisplayName: props.walletDisplayName,
selectedAsset: _.isUndefined(props.defaultSelectedAssetData)
? undefined
: assetUtils.createAssetFromAssetDataOrThrow(
@@ -86,6 +89,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
}
constructor(props: ZeroExInstantProviderProps) {
super(props);
+ setupRollbar();
fonts.include();
const initialAppState = ZeroExInstantProvider._mergeDefaultStateWithProps(this.props);
this._store = store.create(initialAppState);
@@ -115,7 +119,9 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
this._buyQuoteHeartbeat.start(BUY_QUOTE_UPDATE_INTERVAL_TIME_MS);
// Trigger first buyquote fetch
// tslint:disable-next-line:no-floating-promises
- asyncData.fetchCurrentBuyQuoteAndDispatchToStore(state, dispatch, { updateSilently: false });
+ asyncData.fetchCurrentBuyQuoteAndDispatchToStore(state, dispatch, QuoteFetchOrigin.Manual, {
+ updateSilently: false,
+ });
// warm up the gas price estimator cache just in case we can't
// grab the gas price estimate when submitting the transaction
// tslint:disable-next-line:no-floating-promises
@@ -131,6 +137,7 @@ export class ZeroExInstantProvider extends React.Component<ZeroExInstantProvider
this.props.orderSource,
state.providerState,
window,
+ state.selectedAsset,
this.props.affiliateInfo,
),
);
diff --git a/packages/instant/src/constants.ts b/packages/instant/src/constants.ts
index be6077ca9..506348092 100644
--- a/packages/instant/src/constants.ts
+++ b/packages/instant/src/constants.ts
@@ -7,19 +7,42 @@ export const ETH_DECIMALS = 18;
export const DEFAULT_ZERO_EX_CONTAINER_SELECTOR = '#zeroExInstantContainer';
export const INJECTED_DIV_CLASS = 'zeroExInstantResetRoot';
export const INJECTED_DIV_ID = 'zeroExInstant';
+export const OVERLAY_DIV_CLASS = 'zeroExInstantOverlay';
+export const OVERLAY_CLOSE_BUTTON_DIV_CLASS = 'zeroExInstantOverlayCloseButton';
+export const MAIN_CONTAINER_DIV_CLASS = 'zeroExInstantMainContainer';
export const WEB_3_WRAPPER_TRANSACTION_FAILED_ERROR_MSG_PREFIX = 'Transaction failed';
export const GWEI_IN_WEI = new BigNumber(1000000000);
export const ONE_SECOND_MS = 1000;
export const ONE_MINUTE_MS = ONE_SECOND_MS * 60;
+export const GIT_SHA = process.env.GIT_SHA;
+export const NPM_PACKAGE_VERSION = process.env.NPM_PACKAGE_VERSION;
export const ACCOUNT_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 5;
export const BUY_QUOTE_UPDATE_INTERVAL_TIME_MS = ONE_SECOND_MS * 15;
export const DEFAULT_GAS_PRICE = GWEI_IN_WEI.mul(6);
export const DEFAULT_ESTIMATED_TRANSACTION_TIME_MS = ONE_MINUTE_MS * 2;
+export const MAGIC_TRIGGER_ERROR_INPUT = '0€';
+export const MAGIC_TRIGGER_ERROR_MESSAGE = 'Triggered error';
export const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info';
export const HEAP_ANALYTICS_ID = process.env.HEAP_ANALYTICS_ID;
+export const HEAP_ENABLED = process.env.HEAP_ENABLED;
export const COINBASE_API_BASE_URL = 'https://api.coinbase.com/v2';
export const PROGRESS_STALL_AT_WIDTH = '95%';
export const PROGRESS_FINISH_ANIMATION_TIME_MS = 200;
+export const HOST_DOMAINS = [
+ '0x-instant-staging.s3-website-us-east-1.amazonaws.com',
+ '0x-instant-dogfood.s3-website-us-east-1.amazonaws.com',
+ 'localhost',
+ '127.0.0.1',
+ '0.0.0.0',
+ 'instant.0xproject.com',
+];
+export const ROLLBAR_CLIENT_TOKEN = process.env.ROLLBAR_CLIENT_TOKEN;
+export const ROLLBAR_ENABLED = process.env.ROLLBAR_ENABLED;
+export const INSTANT_DISCHARGE_TARGET = process.env.INSTANT_DISCHARGE_TARGET as
+ | 'production'
+ | 'dogfood'
+ | 'staging'
+ | undefined;
export const COINBASE_WALLET_IOS_APP_STORE_URL = 'https://itunes.apple.com/us/app/coinbase-wallet/id1278383455?mt=8';
export const COINBASE_WALLET_ANDROID_APP_STORE_URL = 'https://play.google.com/store/apps/details?id=org.toshi&hl=en';
export const COINBASE_WALLET_SITE_URL = 'https://wallet.coinbase.com/';
diff --git a/packages/instant/src/containers/connected_account_payment_method.ts b/packages/instant/src/containers/connected_account_payment_method.ts
index cdeb49a25..bb68fdd57 100644
--- a/packages/instant/src/containers/connected_account_payment_method.ts
+++ b/packages/instant/src/containers/connected_account_payment_method.ts
@@ -11,7 +11,7 @@ import {
import { Action, actions } from '../redux/actions';
import { asyncData } from '../redux/async_data';
import { State } from '../redux/reducer';
-import { Network, Omit, OperatingSystem, ProviderState, StandardSlidingPanelContent } from '../types';
+import { Network, Omit, OperatingSystem, ProviderState, StandardSlidingPanelContent, WalletSuggestion } from '../types';
import { analytics } from '../util/analytics';
import { envUtil } from '../util/env';
@@ -20,6 +20,7 @@ export interface ConnectedAccountPaymentMethodProps {}
interface ConnectedState {
network: Network;
providerState: ProviderState;
+ walletDisplayName?: string;
}
interface ConnectedDispatch {
@@ -34,6 +35,7 @@ type FinalProps = ConnectedProps & ConnectedAccountPaymentMethodProps;
const mapStateToProps = (state: State, _ownProps: ConnectedAccountPaymentMethodProps): ConnectedState => ({
network: state.network,
providerState: state.providerState,
+ walletDisplayName: state.walletDisplayName,
});
const mapDispatchToProps = (
@@ -56,27 +58,32 @@ const mergeProps = (
...ownProps,
network: connectedState.network,
account: connectedState.providerState.account,
- walletName: connectedState.providerState.name,
+ walletDisplayName: connectedState.walletDisplayName || connectedState.providerState.name,
onUnlockWalletClick: () => connectedDispatch.unlockWalletAndDispatchToStore(connectedState.providerState),
onInstallWalletClick: () => {
const isMobile = envUtil.isMobileOperatingSystem();
- if (!isMobile) {
+ const walletSuggestion: WalletSuggestion = isMobile
+ ? WalletSuggestion.CoinbaseWallet
+ : WalletSuggestion.MetaMask;
+
+ analytics.trackInstallWalletClicked(walletSuggestion);
+ if (walletSuggestion === WalletSuggestion.MetaMask) {
connectedDispatch.openInstallWalletPanel();
- return;
- }
- const operatingSystem = envUtil.getOperatingSystem();
- let url = COINBASE_WALLET_SITE_URL;
- switch (operatingSystem) {
- case OperatingSystem.Android:
- url = COINBASE_WALLET_ANDROID_APP_STORE_URL;
- break;
- case OperatingSystem.iOS:
- url = COINBASE_WALLET_IOS_APP_STORE_URL;
- break;
- default:
- break;
+ } else {
+ const operatingSystem = envUtil.getOperatingSystem();
+ let url = COINBASE_WALLET_SITE_URL;
+ switch (operatingSystem) {
+ case OperatingSystem.Android:
+ url = COINBASE_WALLET_ANDROID_APP_STORE_URL;
+ break;
+ case OperatingSystem.iOS:
+ url = COINBASE_WALLET_IOS_APP_STORE_URL;
+ break;
+ default:
+ break;
+ }
+ window.open(url, '_blank');
}
- window.open(url, '_blank');
},
});
diff --git a/packages/instant/src/containers/latest_error.tsx b/packages/instant/src/containers/latest_error.tsx
index b7cfdb504..0d4349124 100644
--- a/packages/instant/src/containers/latest_error.tsx
+++ b/packages/instant/src/containers/latest_error.tsx
@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { SlidingError } from '../components/sliding_error';
+import { Container } from '../components/ui/container';
import { Overlay } from '../components/ui/overlay';
import { Action } from '../redux/actions';
import { State } from '../redux/reducer';
@@ -23,7 +24,12 @@ export interface LatestErrorComponentProps {
export const LatestErrorComponent: React.StatelessComponent<LatestErrorComponentProps> = props => {
if (!props.latestErrorMessage) {
- return <div />;
+ // Render a hidden SlidingError such that instant does not move when a real error is rendered.
+ return (
+ <Container isHidden={true}>
+ <SlidingError animationState="slidIn" icon="😢" message="" />
+ </Container>
+ );
}
return (
<React.Fragment>
diff --git a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
index 610335243..80943a96f 100644
--- a/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
+++ b/packages/instant/src/containers/selected_asset_buy_order_state_buttons.ts
@@ -9,7 +9,8 @@ import { Dispatch } from 'redux';
import { BuyOrderStateButtons } from '../components/buy_order_state_buttons';
import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
-import { AccountState, AffiliateInfo, OrderProcessState, ZeroExInstantError } from '../types';
+import { AccountState, AffiliateInfo, Asset, OrderProcessState, ZeroExInstantError } from '../types';
+import { analytics } from '../util/analytics';
import { errorFlasher } from '../util/error_flasher';
import { etherscanUtil } from '../util/etherscan';
@@ -21,6 +22,7 @@ interface ConnectedState {
assetBuyer: AssetBuyer;
web3Wrapper: Web3Wrapper;
affiliateInfo?: AffiliateInfo;
+ selectedAsset?: Asset;
onViewTransaction: () => void;
}
@@ -40,6 +42,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButt
const account = state.providerState.account;
const accountAddress = account.state === AccountState.Ready ? account.address : undefined;
const accountEthBalanceInWei = account.state === AccountState.Ready ? account.ethBalanceInWei : undefined;
+ const selectedAsset = state.selectedAsset;
return {
accountAddress,
accountEthBalanceInWei,
@@ -48,6 +51,7 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButt
web3Wrapper,
buyQuote: state.latestBuyQuote,
affiliateInfo: state.affiliateInfo,
+ selectedAsset,
onViewTransaction: () => {
if (
state.buyOrderState.processState === OrderProcessState.Processing ||
@@ -59,6 +63,8 @@ const mapStateToProps = (state: State, _ownProps: SelectedAssetBuyOrderStateButt
assetBuyer.networkId,
);
if (etherscanUrl) {
+ analytics.trackTransactionViewed(state.buyOrderState.processState);
+
window.open(etherscanUrl, '_blank');
return;
}
diff --git a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
index a39bc46a2..cb9df527e 100644
--- a/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
+++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts
@@ -10,7 +10,7 @@ import { ERC20AssetAmountInput, ERC20AssetAmountInputProps } from '../components
import { Action, actions } from '../redux/actions';
import { State } from '../redux/reducer';
import { ColorOption } from '../style/theme';
-import { AffiliateInfo, ERC20Asset, Omit, OrderProcessState } from '../types';
+import { AffiliateInfo, ERC20Asset, Omit, OrderProcessState, QuoteFetchOrigin } from '../types';
import { buyQuoteUpdater } from '../util/buy_quote_updater';
export interface SelectedERC20AssetAmountInputProps {
@@ -88,7 +88,7 @@ const mapDispatchToProps = (
// even if it's debounced, give them the illusion it's loading
dispatch(actions.setQuoteRequestStatePending());
// tslint:disable-next-line:no-floating-promises
- debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, {
+ debouncedUpdateBuyQuoteAsync(assetBuyer, dispatch, asset, value, QuoteFetchOrigin.Manual, {
setPending: true,
dispatchErrors: true,
affiliateInfo,
diff --git a/packages/instant/src/data/asset_meta_data_map.ts b/packages/instant/src/data/asset_meta_data_map.ts
index b24c9c83d..88611a8c0 100644
--- a/packages/instant/src/data/asset_meta_data_map.ts
+++ b/packages/instant/src/data/asset_meta_data_map.ts
@@ -83,14 +83,14 @@ export const assetMetaDataMap: ObjectMap<AssetMetaData> = {
'0xf47261b0000000000000000000000000e0b7927c4af23765cb51314a0e0521a9645f0e2a': {
assetProxyId: AssetProxyId.ERC20,
decimals: 9,
- primaryColor: '#DEB564',
+ primaryColor: '#E1AA3E',
symbol: 'dgd',
name: 'DigixDao',
},
'0xf47261b00000000000000000000000004f3afec4e5a3f2a6a1a411def7d7dfe50ee057bf': {
assetProxyId: AssetProxyId.ERC20,
decimals: 9,
- primaryColor: '#DEB564',
+ primaryColor: '#E1AA3E',
symbol: 'dgx',
name: 'Digix Gold Token',
},
@@ -195,7 +195,7 @@ export const assetMetaDataMap: ObjectMap<AssetMetaData> = {
'0xf47261b000000000000000000000000089d24a6b4ccb1b6faa2625fe562bdd9a23260359': {
assetProxyId: AssetProxyId.ERC20,
decimals: 18,
- primaryColor: '#F2B350',
+ primaryColor: '#DEA349',
symbol: 'dai',
name: 'Dai Stablecoin',
},
diff --git a/packages/instant/src/index.umd.ts b/packages/instant/src/index.umd.ts
index 3a8694d6a..d172f4145 100644
--- a/packages/instant/src/index.umd.ts
+++ b/packages/instant/src/index.umd.ts
@@ -2,8 +2,15 @@ import * as _ from 'lodash';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
-import { DEFAULT_ZERO_EX_CONTAINER_SELECTOR, INJECTED_DIV_CLASS, INJECTED_DIV_ID } from './constants';
+import {
+ DEFAULT_ZERO_EX_CONTAINER_SELECTOR,
+ GIT_SHA as GIT_SHA_FROM_CONSTANT,
+ INJECTED_DIV_CLASS,
+ INJECTED_DIV_ID,
+ NPM_PACKAGE_VERSION,
+} from './constants';
import { ZeroExInstantOverlay, ZeroExInstantOverlayProps } from './index';
+import { analytics } from './util/analytics';
import { assert } from './util/assert';
import { util } from './util/util';
@@ -38,6 +45,9 @@ const validateInstantRenderConfig = (config: ZeroExInstantConfig, selector: stri
if (!_.isUndefined(config.provider)) {
assert.isWeb3Provider('provider', config.provider);
}
+ if (!_.isUndefined(config.walletDisplayName)) {
+ assert.isString('walletDisplayName', config.walletDisplayName);
+ }
if (!_.isUndefined(config.shouldDisablePushToHistory)) {
assert.isBoolean('shouldDisablePushToHistory', config.shouldDisablePushToHistory);
}
@@ -57,6 +67,7 @@ const renderInstant = (config: ZeroExInstantConfig, selector: string) => {
injectedDiv.setAttribute('class', INJECTED_DIV_CLASS);
appendTo.appendChild(injectedDiv);
const closeInstant = () => {
+ analytics.trackInstantClosed();
if (!_.isUndefined(config.onClose)) {
config.onClose();
}
@@ -89,12 +100,12 @@ export const render = (config: ZeroExInstantConfig, selector: string = DEFAULT_Z
// If the integrator defined a popstate handler, save it to __zeroExInstantIntegratorsPopStateHandler
// unless we have already done so on a previous render.
const anyWindow = window as any;
- if (window.onpopstate && !anyWindow.__zeroExInstantIntegratorsPopStateHandler) {
- anyWindow.__zeroExInstantIntegratorsPopStateHandler = window.onpopstate.bind(window);
- }
- const integratorsOnPopStateHandler = anyWindow.__zeroExInstantIntegratorsPopStateHandler || util.boundNoop;
+ const popStateExistsAndNotSetPreviously = window.onpopstate && !anyWindow.__zeroExInstantIntegratorsPopStateHandler;
+ anyWindow.__zeroExInstantIntegratorsPopStateHandler = popStateExistsAndNotSetPreviously
+ ? anyWindow.onpopstate.bind(window)
+ : util.boundNoop;
const onPopStateHandler = (e: PopStateEvent) => {
- integratorsOnPopStateHandler(e);
+ anyWindow.__zeroExInstantIntegratorsPopStateHandler(e);
const newState = e.state;
if (newState && newState.zeroExInstantShowing) {
// We have returned to a history state that expects instant to be rendered.
@@ -110,3 +121,7 @@ export const render = (config: ZeroExInstantConfig, selector: string = DEFAULT_Z
};
window.onpopstate = onPopStateHandler;
};
+
+// Write version info to the exported object for debugging
+export const GIT_SHA = GIT_SHA_FROM_CONSTANT;
+export const NPM_VERSION = NPM_PACKAGE_VERSION;
diff --git a/packages/instant/src/redux/analytics_middleware.ts b/packages/instant/src/redux/analytics_middleware.ts
index 299c2560e..3f7a51707 100644
--- a/packages/instant/src/redux/analytics_middleware.ts
+++ b/packages/instant/src/redux/analytics_middleware.ts
@@ -1,10 +1,11 @@
+import { AssetProxyId } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { Middleware } from 'redux';
import { ETH_DECIMALS } from '../constants';
-import { Account, AccountState } from '../types';
-import { analytics } from '../util/analytics';
+import { AccountState, StandardSlidingPanelContent } from '../types';
+import { analytics, AnalyticsEventOptions } from '../util/analytics';
import { Action, ActionTypes } from './actions';
@@ -29,9 +30,11 @@ export const analyticsMiddleware: Middleware = store => next => middlewareAction
if (didJustTurnReady) {
analytics.trackAccountReady(ethAddress);
analytics.addUserProperties({ lastKnownEthAddress: ethAddress });
+ analytics.addEventProperties({ ethAddress });
} else if (didJustUpdateAddress) {
analytics.trackAccountAddressChanged(ethAddress);
analytics.addUserProperties({ lastKnownEthAddress: ethAddress });
+ analytics.addEventProperties({ ethAddress });
}
}
break;
@@ -51,8 +54,51 @@ export const analyticsMiddleware: Middleware = store => next => middlewareAction
curAccount.ethBalanceInWei,
ETH_DECIMALS,
).toString();
- analytics.addUserProperties({ ethBalanceInUnitAmount });
+ analytics.addUserProperties({ lastEthBalanceInUnitAmount: ethBalanceInUnitAmount });
+ analytics.addEventProperties({ ethBalanceInUnitAmount });
}
+ break;
+ case ActionTypes.UPDATE_SELECTED_ASSET:
+ const selectedAsset = curState.selectedAsset;
+ if (selectedAsset) {
+ const assetName = selectedAsset.metaData.name;
+ const assetData = selectedAsset.assetData;
+ analytics.trackTokenSelectorChose({
+ assetName,
+ assetData,
+ });
+
+ const selectedAssetEventProperties: AnalyticsEventOptions = {
+ selectedAssetName: assetName,
+ selectedAssetData: assetData,
+ };
+ if (selectedAsset.metaData.assetProxyId === AssetProxyId.ERC20) {
+ selectedAssetEventProperties.selectedAssetDecimals = selectedAsset.metaData.decimals;
+ selectedAssetEventProperties.selectedAssetSymbol = selectedAsset.metaData.symbol;
+ }
+ analytics.addEventProperties(selectedAssetEventProperties);
+ }
+ break;
+ case ActionTypes.SET_AVAILABLE_ASSETS:
+ const availableAssets = curState.availableAssets;
+ if (availableAssets) {
+ analytics.addEventProperties({
+ numberAvailableAssets: availableAssets.length,
+ });
+ }
+ break;
+ case ActionTypes.OPEN_STANDARD_SLIDING_PANEL:
+ const openSlidingContent = curState.standardSlidingPanelSettings.content;
+ if (openSlidingContent === StandardSlidingPanelContent.InstallWallet) {
+ analytics.trackInstallWalletModalOpened();
+ }
+ break;
+ case ActionTypes.CLOSE_STANDARD_SLIDING_PANEL:
+ const closeSlidingContent = curState.standardSlidingPanelSettings.content;
+ if (closeSlidingContent === StandardSlidingPanelContent.InstallWallet) {
+ analytics.trackInstallWalletModalClosed();
+ }
+ break;
}
return nextAction;
diff --git a/packages/instant/src/redux/async_data.ts b/packages/instant/src/redux/async_data.ts
index 6feb760e7..c67b222d1 100644
--- a/packages/instant/src/redux/async_data.ts
+++ b/packages/instant/src/redux/async_data.ts
@@ -4,12 +4,13 @@ import * as _ from 'lodash';
import { Dispatch } from 'redux';
import { BIG_NUMBER_ZERO } from '../constants';
-import { AccountState, ERC20Asset, OrderProcessState, ProviderState } from '../types';
+import { AccountState, ERC20Asset, OrderProcessState, ProviderState, QuoteFetchOrigin } from '../types';
import { analytics } from '../util/analytics';
import { assetUtils } from '../util/asset';
import { buyQuoteUpdater } from '../util/buy_quote_updater';
import { coinbaseApi } from '../util/coinbase_api';
import { errorFlasher } from '../util/error_flasher';
+import { errorReporter } from '../util/error_reporter';
import { actions } from './actions';
import { State } from './reducer';
@@ -23,6 +24,7 @@ export const asyncData = {
const errorMessage = 'Error fetching ETH/USD price';
errorFlasher.flashNewErrorMessage(dispatch, errorMessage);
dispatch(actions.updateEthUsdPrice(BIG_NUMBER_ZERO));
+ errorReporter.report(e);
}
},
fetchAvailableAssetDatasAndDispatchToStore: async (state: State, dispatch: Dispatch) => {
@@ -30,13 +32,15 @@ export const asyncData = {
const assetBuyer = providerState.assetBuyer;
try {
const assetDatas = await assetBuyer.getAvailableAssetDatasAsync();
- const assets = assetUtils.createAssetsFromAssetDatas(assetDatas, assetMetaDataMap, network);
+ const deduplicatedAssetDatas = _.uniq(assetDatas);
+ const assets = assetUtils.createAssetsFromAssetDatas(deduplicatedAssetDatas, assetMetaDataMap, network);
dispatch(actions.setAvailableAssets(assets));
} catch (e) {
const errorMessage = 'Could not find any assets';
errorFlasher.flashNewErrorMessage(dispatch, errorMessage);
// On error, just specify that none are available
dispatch(actions.setAvailableAssets([]));
+ errorReporter.report(e);
}
},
fetchAccountInfoAndDispatchToStore: async (
@@ -77,6 +81,7 @@ export const asyncData = {
const ethBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(address);
dispatch(actions.updateAccountEthBalance({ address, ethBalanceInWei }));
} catch (e) {
+ errorReporter.report(e);
// leave balance as is
return;
}
@@ -84,6 +89,7 @@ export const asyncData = {
fetchCurrentBuyQuoteAndDispatchToStore: async (
state: State,
dispatch: Dispatch,
+ fetchOrigin: QuoteFetchOrigin,
options: { updateSilently: boolean },
) => {
const { buyOrderState, providerState, selectedAsset, selectedAssetUnitAmount, affiliateInfo } = state;
@@ -99,7 +105,12 @@ export const asyncData = {
dispatch,
selectedAsset as ERC20Asset,
selectedAssetUnitAmount,
- { setPending: !options.updateSilently, dispatchErrors: !options.updateSilently, affiliateInfo },
+ fetchOrigin,
+ {
+ setPending: !options.updateSilently,
+ dispatchErrors: !options.updateSilently,
+ affiliateInfo,
+ },
);
}
},
diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts
index dfc2b89f3..a9a407b7d 100644
--- a/packages/instant/src/redux/reducer.ts
+++ b/packages/instant/src/redux/reducer.ts
@@ -49,6 +49,7 @@ interface OptionalState {
latestBuyQuote: BuyQuote;
latestErrorMessage: string;
affiliateInfo: AffiliateInfo;
+ walletDisplayName: string;
}
export type State = DefaultState & PropsDerivedState & Partial<OptionalState>;
diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts
index a0751286b..fd3f03c3f 100644
--- a/packages/instant/src/style/theme.ts
+++ b/packages/instant/src/style/theme.ts
@@ -1,6 +1,14 @@
import * as styledComponents from 'styled-components';
-const { default: styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider } = styledComponents;
+const {
+ default: styled,
+ css,
+ keyframes,
+ withTheme,
+ createGlobalStyle,
+ ThemeConsumer,
+ ThemeProvider,
+} = styledComponents;
export type Theme = { [key in ColorOption]: string };
@@ -30,8 +38,8 @@ export const theme: Theme = {
lightestGrey: '#EEEEEE',
darkGrey: '#333333',
white: 'white',
- lightOrange: '#F9F2ED',
- darkOrange: '#F2994C',
+ lightOrange: '#FFF8F2',
+ darkOrange: '#F7A24F',
green: '#3CB34F',
red: '#D00000',
darkBlue: '#135df6',
@@ -45,4 +53,4 @@ export const generateOverlayBlack = (opacity = 0.6) => {
return `rgba(0, 0, 0, ${opacity})`;
};
-export { styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider };
+export { styled, css, keyframes, withTheme, createGlobalStyle, ThemeConsumer, ThemeProvider };
diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts
index 999d50fed..2d73ba29e 100644
--- a/packages/instant/src/types.ts
+++ b/packages/instant/src/types.ts
@@ -21,6 +21,11 @@ export enum OrderProcessState {
Failure = 'FAILURE',
}
+export enum QuoteFetchOrigin {
+ Manual = 'Manual',
+ Heartbeat = 'Heartbeat',
+}
+
export interface SimulatedProgress {
startTimeUnix: number;
expectedEndTimeUnix: number;
@@ -149,6 +154,11 @@ export enum Browser {
Other = 'OTHER',
}
+export enum WalletSuggestion {
+ CoinbaseWallet = 'Coinbase Wallet',
+ MetaMask = 'MetaMask',
+}
+
export enum OperatingSystem {
Android = 'ANDROID',
iOS = 'IOS',
diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts
index cec99dd1b..6da37bedb 100644
--- a/packages/instant/src/util/analytics.ts
+++ b/packages/instant/src/util/analytics.ts
@@ -1,26 +1,66 @@
-import { AffiliateInfo, Network, OrderSource, ProviderState } from '../types';
+import { BuyQuote } from '@0x/asset-buyer';
+import { BigNumber } from '@0x/utils';
+import * as _ from 'lodash';
+
+import { GIT_SHA, HEAP_ENABLED, INSTANT_DISCHARGE_TARGET, NPM_PACKAGE_VERSION } from '../constants';
+import {
+ AffiliateInfo,
+ Asset,
+ Network,
+ OrderProcessState,
+ OrderSource,
+ ProviderState,
+ QuoteFetchOrigin,
+ WalletSuggestion,
+} from '../types';
import { EventProperties, heapUtil } from './heap';
-let isDisabled = false;
+let isDisabledViaConfig = false;
export const disableAnalytics = (shouldDisableAnalytics: boolean) => {
- isDisabled = shouldDisableAnalytics;
+ isDisabledViaConfig = shouldDisableAnalytics;
};
export const evaluateIfEnabled = (fnCall: () => void) => {
- if (isDisabled) {
+ if (isDisabledViaConfig) {
return;
}
- fnCall();
+ if (HEAP_ENABLED) {
+ fnCall();
+ }
};
enum EventNames {
INSTANT_OPENED = 'Instant - Opened',
+ INSTANT_CLOSED = 'Instant - Closed',
ACCOUNT_LOCKED = 'Account - Locked',
ACCOUNT_READY = 'Account - Ready',
ACCOUNT_UNLOCK_REQUESTED = 'Account - Unlock Requested',
ACCOUNT_UNLOCK_DENIED = 'Account - Unlock Denied',
ACCOUNT_ADDRESS_CHANGED = 'Account - Address Changed',
+ PAYMENT_METHOD_DROPDOWN_OPENED = 'Payment Method - Dropdown Opened',
+ PAYMENT_METHOD_OPENED_ETHERSCAN = 'Payment Method - Opened Etherscan',
+ PAYMENT_METHOD_COPIED_ADDRESS = 'Payment Method - Copied Address',
+ BUY_NOT_ENOUGH_ETH = 'Buy - Not Enough Eth',
+ BUY_STARTED = 'Buy - Started',
+ BUY_SIGNATURE_DENIED = 'Buy - Signature Denied',
+ BUY_SIMULATION_FAILED = 'Buy - Simulation Failed',
+ BUY_TX_SUBMITTED = 'Buy - Tx Submitted',
+ BUY_TX_SUCCEEDED = 'Buy - Tx Succeeded',
+ BUY_TX_FAILED = 'Buy - Tx Failed',
+ INSTALL_WALLET_CLICKED = 'Install Wallet - Clicked',
+ INSTALL_WALLET_MODAL_OPENED = 'Install Wallet - Modal - Opened',
+ INSTALL_WALLET_MODAL_CLICKED_EXPLANATION = 'Install Wallet - Modal - Clicked Explanation',
+ INSTALL_WALLET_MODAL_CLICKED_GET = 'Install Wallet - Modal - Clicked Get',
+ INSTALL_WALLET_MODAL_CLOSED = 'Install Wallet - Modal - Closed',
+ TOKEN_SELECTOR_OPENED = 'Token Selector - Opened',
+ TOKEN_SELECTOR_CLOSED = 'Token Selector - Closed',
+ TOKEN_SELECTOR_CHOSE = 'Token Selector - Chose',
+ TOKEN_SELECTOR_SEARCHED = 'Token Selector - Searched',
+ TRANSACTION_VIEWED = 'Transaction - Viewed',
+ QUOTE_FETCHED = 'Quote - Fetched',
+ QUOTE_ERROR = 'Quote - Error',
}
+
const track = (eventName: EventNames, eventProperties: EventProperties = {}): void => {
evaluateIfEnabled(() => {
heapUtil.evaluateHeapCall(heap => heap.track(eventName, eventProperties));
@@ -38,22 +78,50 @@ function trackingEventFnWithPayload(eventName: EventNames): (eventProperties: Ev
};
}
+const buyQuoteEventProperties = (buyQuote: BuyQuote) => {
+ const assetBuyAmount = buyQuote.assetBuyAmount.toString();
+ const assetEthAmount = buyQuote.worstCaseQuoteInfo.assetEthAmount.toString();
+ const feeEthAmount = buyQuote.worstCaseQuoteInfo.feeEthAmount.toString();
+ const totalEthAmount = buyQuote.worstCaseQuoteInfo.totalEthAmount.toString();
+ const feePercentage = !_.isUndefined(buyQuote.feePercentage) ? buyQuote.feePercentage.toString() : 0;
+ const hasFeeOrders = !_.isEmpty(buyQuote.feeOrders) ? 'true' : 'false';
+ return {
+ assetBuyAmount,
+ assetEthAmount,
+ feeEthAmount,
+ totalEthAmount,
+ feePercentage,
+ hasFeeOrders,
+ };
+};
+
export interface AnalyticsUserOptions {
lastKnownEthAddress?: string;
- ethBalanceInUnitAmount?: string;
+ lastEthBalanceInUnitAmount?: string;
}
export interface AnalyticsEventOptions {
embeddedHost?: string;
embeddedUrl?: string;
+ ethBalanceInUnitAmount?: string;
+ ethAddress?: string;
networkId?: number;
providerName?: string;
gitSha?: string;
npmVersion?: string;
+ instantEnvironment?: string;
orderSource?: string;
affiliateAddress?: string;
affiliateFeePercent?: number;
+ numberAvailableAssets?: number;
+ selectedAssetName?: string;
+ selectedAssetSymbol?: string;
+ selectedAssetData?: string;
+ selectedAssetDecimals?: number;
+}
+export enum TokenSelectorClosedVia {
+ ClickedX = 'Clicked X',
+ TokenChose = 'Token Chose',
}
-
export const analytics = {
addUserProperties: (properties: AnalyticsUserOptions): void => {
evaluateIfEnabled(() => {
@@ -70,28 +138,94 @@ export const analytics = {
orderSource: OrderSource,
providerState: ProviderState,
window: Window,
+ selectedAsset?: Asset,
affiliateInfo?: AffiliateInfo,
): AnalyticsEventOptions => {
const affiliateAddress = affiliateInfo ? affiliateInfo.feeRecipient : 'none';
const affiliateFeePercent = affiliateInfo ? parseFloat(affiliateInfo.feePercentage.toFixed(4)) : 0;
const orderSourceName = typeof orderSource === 'string' ? orderSource : 'provided';
- return {
+ const eventOptions: AnalyticsEventOptions = {
embeddedHost: window.location.host,
embeddedUrl: window.location.href,
networkId: network,
providerName: providerState.name,
- gitSha: process.env.GIT_SHA,
- npmVersion: process.env.NPM_PACKAGE_VERSION,
+ gitSha: GIT_SHA,
+ npmVersion: NPM_PACKAGE_VERSION,
orderSource: orderSourceName,
affiliateAddress,
affiliateFeePercent,
+ selectedAssetName: selectedAsset ? selectedAsset.metaData.name : 'none',
+ selectedAssetData: selectedAsset ? selectedAsset.assetData : 'none',
+ instantEnvironment: INSTANT_DISCHARGE_TARGET || `Local ${process.env.NODE_ENV}`,
};
+ return eventOptions;
},
trackInstantOpened: trackingEventFnWithoutPayload(EventNames.INSTANT_OPENED),
+ trackInstantClosed: trackingEventFnWithoutPayload(EventNames.INSTANT_CLOSED),
trackAccountLocked: trackingEventFnWithoutPayload(EventNames.ACCOUNT_LOCKED),
trackAccountReady: (address: string) => trackingEventFnWithPayload(EventNames.ACCOUNT_READY)({ address }),
trackAccountUnlockRequested: trackingEventFnWithoutPayload(EventNames.ACCOUNT_UNLOCK_REQUESTED),
trackAccountUnlockDenied: trackingEventFnWithoutPayload(EventNames.ACCOUNT_UNLOCK_DENIED),
trackAccountAddressChanged: (address: string) =>
trackingEventFnWithPayload(EventNames.ACCOUNT_ADDRESS_CHANGED)({ address }),
+ trackPaymentMethodDropdownOpened: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_DROPDOWN_OPENED),
+ trackPaymentMethodOpenedEtherscan: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_OPENED_ETHERSCAN),
+ trackPaymentMethodCopiedAddress: trackingEventFnWithoutPayload(EventNames.PAYMENT_METHOD_COPIED_ADDRESS),
+ trackBuyNotEnoughEth: (buyQuote: BuyQuote) =>
+ trackingEventFnWithPayload(EventNames.BUY_NOT_ENOUGH_ETH)(buyQuoteEventProperties(buyQuote)),
+ trackBuyStarted: (buyQuote: BuyQuote) =>
+ trackingEventFnWithPayload(EventNames.BUY_STARTED)(buyQuoteEventProperties(buyQuote)),
+ trackBuySignatureDenied: (buyQuote: BuyQuote) =>
+ trackingEventFnWithPayload(EventNames.BUY_SIGNATURE_DENIED)(buyQuoteEventProperties(buyQuote)),
+ trackBuySimulationFailed: (buyQuote: BuyQuote) =>
+ trackingEventFnWithPayload(EventNames.BUY_SIMULATION_FAILED)(buyQuoteEventProperties(buyQuote)),
+ trackBuyTxSubmitted: (buyQuote: BuyQuote, txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) =>
+ trackingEventFnWithPayload(EventNames.BUY_TX_SUBMITTED)({
+ ...buyQuoteEventProperties(buyQuote),
+ txHash,
+ expectedTxTimeMs: expectedEndTimeUnix - startTimeUnix,
+ }),
+ trackBuyTxSucceeded: (buyQuote: BuyQuote, txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) =>
+ trackingEventFnWithPayload(EventNames.BUY_TX_SUCCEEDED)({
+ ...buyQuoteEventProperties(buyQuote),
+ txHash,
+ expectedTxTimeMs: expectedEndTimeUnix - startTimeUnix,
+ actualTxTimeMs: new Date().getTime() - startTimeUnix,
+ }),
+ trackBuyTxFailed: (buyQuote: BuyQuote, txHash: string, startTimeUnix: number, expectedEndTimeUnix: number) =>
+ trackingEventFnWithPayload(EventNames.BUY_TX_FAILED)({
+ ...buyQuoteEventProperties(buyQuote),
+ txHash,
+ expectedTxTimeMs: expectedEndTimeUnix - startTimeUnix,
+ actualTxTimeMs: new Date().getTime() - startTimeUnix,
+ }),
+ trackInstallWalletClicked: (walletSuggestion: WalletSuggestion) =>
+ trackingEventFnWithPayload(EventNames.INSTALL_WALLET_CLICKED)({ walletSuggestion }),
+ trackInstallWalletModalClickedExplanation: trackingEventFnWithoutPayload(
+ EventNames.INSTALL_WALLET_MODAL_CLICKED_EXPLANATION,
+ ),
+ trackInstallWalletModalClickedGet: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_CLICKED_GET),
+ trackInstallWalletModalOpened: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_OPENED),
+ trackInstallWalletModalClosed: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_CLOSED),
+ trackTokenSelectorOpened: trackingEventFnWithoutPayload(EventNames.TOKEN_SELECTOR_OPENED),
+ trackTokenSelectorClosed: (closedVia: TokenSelectorClosedVia) =>
+ trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CLOSED)({ closedVia }),
+ trackTokenSelectorChose: (payload: { assetName: string; assetData: string }) =>
+ trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CHOSE)(payload),
+ trackTokenSelectorSearched: (searchText: string) =>
+ trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_SEARCHED)({ searchText }),
+ trackTransactionViewed: (orderProcesState: OrderProcessState) =>
+ trackingEventFnWithPayload(EventNames.TRANSACTION_VIEWED)({ orderState: orderProcesState }),
+ trackQuoteFetched: (buyQuote: BuyQuote, fetchOrigin: QuoteFetchOrigin) =>
+ trackingEventFnWithPayload(EventNames.QUOTE_FETCHED)({
+ ...buyQuoteEventProperties(buyQuote),
+ fetchOrigin,
+ }),
+ trackQuoteError: (errorMessage: string, assetBuyAmount: BigNumber, fetchOrigin: QuoteFetchOrigin) => {
+ trackingEventFnWithPayload(EventNames.QUOTE_ERROR)({
+ errorMessage,
+ assetBuyAmount: assetBuyAmount.toString(),
+ fetchOrigin,
+ });
+ },
};
diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts
index 40560d3eb..08f3642e3 100644
--- a/packages/instant/src/util/asset.ts
+++ b/packages/instant/src/util/asset.ts
@@ -1,3 +1,4 @@
+import { AssetBuyerError } from '@0x/asset-buyer';
import { AssetProxyId, ObjectMap } from '@0x/types';
import * as _ from 'lodash';
@@ -106,4 +107,20 @@ export const assetUtils = {
);
return _.compact(erc20sOrUndefined);
},
+ assetBuyerErrorMessage: (asset: ERC20Asset, error: Error): string | undefined => {
+ if (error.message === AssetBuyerError.InsufficientAssetLiquidity) {
+ const assetName = assetUtils.bestNameForAsset(asset, 'of this asset');
+ return `Not enough ${assetName} available`;
+ } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) {
+ return 'Not enough ZRX available';
+ } else if (
+ error.message === AssetBuyerError.StandardRelayerApiError ||
+ error.message.startsWith(AssetBuyerError.AssetUnavailable)
+ ) {
+ const assetName = assetUtils.bestNameForAsset(asset, 'This asset');
+ return `${assetName} is currently unavailable`;
+ }
+
+ return undefined;
+ },
};
diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts
index 2fd16d781..4229f2735 100644
--- a/packages/instant/src/util/buy_quote_updater.ts
+++ b/packages/instant/src/util/buy_quote_updater.ts
@@ -1,4 +1,4 @@
-import { AssetBuyer, AssetBuyerError, BuyQuote } from '@0x/asset-buyer';
+import { AssetBuyer, BuyQuote } from '@0x/asset-buyer';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
@@ -6,9 +6,11 @@ import { Dispatch } from 'redux';
import { oc } from 'ts-optchain';
import { Action, actions } from '../redux/actions';
-import { AffiliateInfo, ERC20Asset } from '../types';
+import { AffiliateInfo, ERC20Asset, QuoteFetchOrigin } from '../types';
+import { analytics } from '../util/analytics';
import { assetUtils } from '../util/asset';
import { errorFlasher } from '../util/error_flasher';
+import { errorReporter } from '../util/error_reporter';
export const buyQuoteUpdater = {
updateBuyQuoteAsync: async (
@@ -16,7 +18,12 @@ export const buyQuoteUpdater = {
dispatch: Dispatch<Action>,
asset: ERC20Asset,
assetUnitAmount: BigNumber,
- options: { setPending: boolean; dispatchErrors: boolean; affiliateInfo?: AffiliateInfo },
+ fetchOrigin: QuoteFetchOrigin,
+ options: {
+ setPending: boolean;
+ dispatchErrors: boolean;
+ affiliateInfo?: AffiliateInfo;
+ },
): Promise<void> => {
// get a new buy quote.
const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetUnitAmount, asset.metaData.decimals);
@@ -29,34 +36,24 @@ export const buyQuoteUpdater = {
try {
newBuyQuote = await assetBuyer.getBuyQuoteAsync(asset.assetData, baseUnitValue, { feePercentage });
} catch (error) {
+ const errorMessage = assetUtils.assetBuyerErrorMessage(asset, error);
+
+ if (_.isUndefined(errorMessage)) {
+ // This is an unknown error, report it to rollbar
+ errorReporter.report(error);
+ }
+
if (options.dispatchErrors) {
dispatch(actions.setQuoteRequestStateFailure());
- let errorMessage;
- if (error.message === AssetBuyerError.InsufficientAssetLiquidity) {
- const assetName = assetUtils.bestNameForAsset(asset, 'of this asset');
- errorMessage = `Not enough ${assetName} available`;
- } else if (error.message === AssetBuyerError.InsufficientZrxLiquidity) {
- errorMessage = 'Not enough ZRX available';
- } else if (
- error.message === AssetBuyerError.StandardRelayerApiError ||
- error.message.startsWith(AssetBuyerError.AssetUnavailable)
- ) {
- const assetName = assetUtils.bestNameForAsset(asset, 'This asset');
- errorMessage = `${assetName} is currently unavailable`;
- }
- if (!_.isUndefined(errorMessage)) {
- errorFlasher.flashNewErrorMessage(dispatch, errorMessage);
- } else {
- throw error;
- }
+ analytics.trackQuoteError(error.message ? error.message : 'other', baseUnitValue, fetchOrigin);
+ errorFlasher.flashNewErrorMessage(dispatch, errorMessage || 'Error fetching price, please try again');
}
- // TODO: report to error reporter on else
-
return;
}
// We have a successful new buy quote
errorFlasher.clearError(dispatch);
// invalidate the last buy quote.
dispatch(actions.updateLatestBuyQuote(newBuyQuote));
+ analytics.trackQuoteFetched(newBuyQuote, fetchOrigin);
},
};
diff --git a/packages/instant/src/util/error_reporter.ts b/packages/instant/src/util/error_reporter.ts
new file mode 100644
index 000000000..b1824eaf9
--- /dev/null
+++ b/packages/instant/src/util/error_reporter.ts
@@ -0,0 +1,62 @@
+import { logUtils } from '@0x/utils';
+import * as _ from 'lodash';
+
+import { GIT_SHA, HOST_DOMAINS, INSTANT_DISCHARGE_TARGET, ROLLBAR_CLIENT_TOKEN, ROLLBAR_ENABLED } from '../constants';
+
+// Import version of Rollbar designed for embedded components
+// See https://docs.rollbar.com/docs/using-rollbarjs-inside-an-embedded-component
+// tslint:disable-next-line:no-var-requires
+const Rollbar = require('rollbar/dist/rollbar.noconflict.umd');
+
+let rollbar: any;
+// Configures rollbar and sets up error catching
+export const setupRollbar = (): any => {
+ if (_.isUndefined(rollbar) && ROLLBAR_CLIENT_TOKEN && ROLLBAR_ENABLED) {
+ rollbar = new Rollbar({
+ accessToken: ROLLBAR_CLIENT_TOKEN,
+ captureUncaught: true,
+ captureUnhandledRejections: true,
+ enabled: true,
+ itemsPerMinute: 10,
+ maxItems: 500,
+ payload: {
+ environment: INSTANT_DISCHARGE_TARGET || `Local ${process.env.NODE_ENV}`,
+ client: {
+ javascript: {
+ source_map_enabled: true,
+ code_version: GIT_SHA,
+ guess_uncaught_frames: true,
+ },
+ },
+ },
+ hostWhiteList: HOST_DOMAINS,
+ uncaughtErrorLevel: 'error',
+ ignoredMessages: [
+ // Errors from the third-party scripts
+ 'Script error',
+ // Network errors or ad-blockers
+ 'TypeError: Failed to fetch',
+ 'Exchange has not been deployed to detected network (network/artifact mismatch)',
+ // Source: https://groups.google.com/a/chromium.org/forum/#!topic/chromium-discuss/7VU0_VvC7mE
+ "undefined is not an object (evaluating '__gCrWeb.autofill.extractForms')",
+ // Source: http://stackoverflow.com/questions/43399818/securityerror-from-facebook-and-cross-domain-messaging
+ 'SecurityError (DOM Exception 18)',
+ ],
+ });
+ }
+};
+
+export const errorReporter = {
+ report(err: Error): void {
+ if (!rollbar) {
+ logUtils.log('Not reporting to rollbar because not configured', err);
+ return;
+ }
+
+ rollbar.error(err, (rollbarErr: Error) => {
+ if (rollbarErr) {
+ logUtils.log(`Error reporting to rollbar, ignoring: ${rollbarErr}`);
+ }
+ });
+ },
+};
diff --git a/packages/instant/src/util/gas_price_estimator.ts b/packages/instant/src/util/gas_price_estimator.ts
index 6b15809a3..332c8d00a 100644
--- a/packages/instant/src/util/gas_price_estimator.ts
+++ b/packages/instant/src/util/gas_price_estimator.ts
@@ -7,6 +7,8 @@ import {
GWEI_IN_WEI,
} from '../constants';
+import { errorReporter } from './error_reporter';
+
interface EthGasStationResult {
average: number;
fastestWait: number;
@@ -42,8 +44,9 @@ export class GasPriceEstimator {
let fetchedAmount: GasInfo | undefined;
try {
fetchedAmount = await fetchFastAmountInWeiAsync();
- } catch {
+ } catch (e) {
fetchedAmount = undefined;
+ errorReporter.report(e);
}
if (fetchedAmount) {
diff --git a/packages/instant/src/util/heap.ts b/packages/instant/src/util/heap.ts
index 7c53c9918..279ff3059 100644
--- a/packages/instant/src/util/heap.ts
+++ b/packages/instant/src/util/heap.ts
@@ -5,6 +5,7 @@ import * as _ from 'lodash';
import { HEAP_ANALYTICS_ID } from '../constants';
import { AnalyticsEventOptions, AnalyticsUserOptions } from './analytics';
+import { errorReporter } from './error_reporter';
export type EventProperties = ObjectMap<string | number>;
@@ -107,8 +108,8 @@ export const heapUtil = {
heapFunctionCall(curHeap);
} catch (e) {
// We never want analytics to crash our React component
- // TODO(sk): error reporter here
logUtils.log('Analytics error', e);
+ errorReporter.report(e);
}
}
},
diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts
index 2b852fb0d..cf29bf3ea 100644
--- a/packages/instant/src/util/heartbeater_factory.ts
+++ b/packages/instant/src/util/heartbeater_factory.ts
@@ -1,5 +1,6 @@
import { asyncData } from '../redux/async_data';
import { Store } from '../redux/store';
+import { QuoteFetchOrigin } from '../types';
import { Heartbeater } from './heartbeater';
@@ -17,8 +18,13 @@ export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): He
export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => {
const { store, shouldPerformImmediatelyOnStart } = options;
return new Heartbeater(async () => {
- await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store.getState(), store.dispatch, {
- updateSilently: true,
- });
+ await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(
+ store.getState(),
+ store.dispatch,
+ QuoteFetchOrigin.Heartbeat,
+ {
+ updateSilently: true,
+ },
+ );
}, shouldPerformImmediatelyOnStart);
};
diff --git a/packages/instant/test/util/asset.test.ts b/packages/instant/test/util/asset.test.ts
index 4229b24ed..fc4e4e2e4 100644
--- a/packages/instant/test/util/asset.test.ts
+++ b/packages/instant/test/util/asset.test.ts
@@ -1,6 +1,7 @@
+import { AssetBuyerError } from '@0x/asset-buyer';
import { AssetProxyId, ObjectMap } from '@0x/types';
-import { Asset, AssetMetaData, ERC20AssetMetaData, Network, ZeroExInstantError } from '../../src/types';
+import { Asset, AssetMetaData, ERC20Asset, ERC20AssetMetaData, Network, ZeroExInstantError } from '../../src/types';
import { assetUtils } from '../../src/util/asset';
const ZRX_ASSET_DATA = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498';
@@ -11,7 +12,7 @@ const ZRX_META_DATA: ERC20AssetMetaData = {
decimals: 18,
name: '0x',
};
-const ZRX_ASSET: Asset = {
+const ZRX_ASSET: ERC20Asset = {
assetData: ZRX_ASSET_DATA,
metaData: ZRX_META_DATA,
};
@@ -45,4 +46,32 @@ describe('assetDataUtil', () => {
).toThrowError(ZeroExInstantError.AssetMetaDataNotAvailable);
});
});
+ describe('assetBuyerErrorMessage', () => {
+ it('should return message for InsufficientAssetLiquidity', () => {
+ const insufficientAssetError = new Error(AssetBuyerError.InsufficientAssetLiquidity);
+ expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, insufficientAssetError)).toEqual(
+ 'Not enough ZRX available',
+ );
+ });
+ it('should return message for InsufficientAssetLiquidity', () => {
+ const insufficientZrxError = new Error(AssetBuyerError.InsufficientZrxLiquidity);
+ expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, insufficientZrxError)).toEqual(
+ 'Not enough ZRX available',
+ );
+ });
+ it('should message for StandardRelayerApiError', () => {
+ const standardRelayerError = new Error(AssetBuyerError.StandardRelayerApiError);
+ expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, standardRelayerError)).toEqual(
+ 'ZRX is currently unavailable',
+ );
+ });
+ it('should return error for AssetUnavailable error', () => {
+ const assetUnavailableError = new Error(
+ `${AssetBuyerError.AssetUnavailable}: For assetData ${ZRX_ASSET_DATA}`,
+ );
+ expect(assetUtils.assetBuyerErrorMessage(ZRX_ASSET, assetUnavailableError)).toEqual(
+ 'ZRX is currently unavailable',
+ );
+ });
+ });
});
diff --git a/packages/instant/tsconfig.json b/packages/instant/tsconfig.json
index 14b0ad8f7..2b3c11c9f 100644
--- a/packages/instant/tsconfig.json
+++ b/packages/instant/tsconfig.json
@@ -5,8 +5,10 @@
"rootDir": "src",
"jsx": "react",
"noImplicitAny": true,
- "allowSyntheticDefaultImports": true
+ "allowSyntheticDefaultImports": true,
+ "declaration": false,
+ "declarationMap": false,
+ "composite": false
},
- "include": ["./src/**/*"],
- "exclude": ["./src/index.umd.ts"]
+ "include": ["./src/**/*"]
}
diff --git a/packages/instant/webpack.config.js b/packages/instant/webpack.config.js
index 41276809c..e74cf36d9 100644
--- a/packages/instant/webpack.config.js
+++ b/packages/instant/webpack.config.js
@@ -1,32 +1,119 @@
const childProcess = require('child_process');
const ip = require('ip');
const path = require('path');
+const RollbarSourceMapPlugin = require('rollbar-sourcemap-webpack-plugin');
const webpack = require('webpack');
-// The common js bundle (not this one) is built using tsc.
-// The umd bundle (this one) has a different entrypoint.
-
const GIT_SHA = childProcess
.execSync('git rev-parse HEAD')
.toString()
.trim();
-const HEAP_PRODUCTION_ENV_VAR_NAME = 'INSTANT_HEAP_ANALYTICS_ID_PRODUCTION';
-const HEAP_DEVELOPMENT_ENV_VAR_NAME = 'INSTANT_HEAP_ANALYTICS_ID_DEVELOPMENT';
-const getHeapAnalyticsId = modeName => {
- if (modeName === 'production') {
- return process.env[HEAP_PRODUCTION_ENV_VAR_NAME];
+const DISCHARGE_TARGETS_THAT_REQUIRED_HEAP = ['production', 'staging', 'dogfood'];
+const getHeapConfigForDischargeTarget = dischargeTarget => {
+ return {
+ heapAnalyticsIdEnvName:
+ dischargeTarget === 'production'
+ ? 'INSTANT_HEAP_ANALYTICS_ID_PRODUCTION'
+ : 'INSTANT_HEAP_ANALYTICS_ID_DEVELOPMENT',
+ heapAnalyticsIdRequired: DISCHARGE_TARGETS_THAT_REQUIRED_HEAP.includes(dischargeTarget),
+ };
+};
+
+const DISCHARGE_TARGETS_THAT_REQUIRE_ROLLBAR = ['production', 'staging', 'dogfood'];
+const getRollbarConfigForDischargeTarget = dischargeTarget => {
+ if (DISCHARGE_TARGETS_THAT_REQUIRE_ROLLBAR.includes(dischargeTarget)) {
+ const rollbarSourceMapPublicPath =
+ dischargeTarget === 'production'
+ ? 'https://instant.0xproject.com'
+ : `http://0x-instant-${dischargeTarget}.s3-website-us-east-1.amazonaws.com`;
+
+ return {
+ rollbarSourceMapPublicPath,
+ rollbarRequired: true,
+ };
}
- if (modeName === 'development') {
- return process.env[HEAP_DEVELOPMENT_ENV_VAR_NAME];
+ return {
+ rollbarRequired: false,
+ };
+};
+
+const ROLLBAR_CLIENT_TOKEN_ENV_VAR_NAME = 'INSTANT_ROLLBAR_CLIENT_TOKEN';
+const ROLLBAR_PUBLISH_TOKEN_ENV_VAR_NAME = 'INSTANT_ROLLBAR_PUBLISH_TOKEN';
+const getRollbarTokens = (dischargeTarget, rollbarRequired) => {
+ const clientToken = process.env[ROLLBAR_CLIENT_TOKEN_ENV_VAR_NAME];
+ const publishToken = process.env[ROLLBAR_PUBLISH_TOKEN_ENV_VAR_NAME];
+
+ if (rollbarRequired) {
+ if (!clientToken) {
+ throw new Error(
+ `Rollbar client token required for ${dischargeTarget}, please set env var ${ROLLBAR_CLIENT_TOKEN_ENV_VAR_NAME}`,
+ );
+ }
+ if (!publishToken) {
+ throw new Error(
+ `Rollbar publish token required for ${dischargeTarget}, please set env var ${ROLLBAR_PUBLISH_TOKEN_ENV_VAR_NAME}`,
+ );
+ }
}
- return undefined;
+ return { clientToken, publishToken };
};
-module.exports = (env, argv) => {
+const generateConfig = (dischargeTarget, heapConfigOptions, rollbarConfigOptions, nodeEnv) => {
const outputPath = process.env.WEBPACK_OUTPUT_PATH || 'umd';
+
+ const { heapAnalyticsIdEnvName, heapAnalyticsIdRequired } = heapConfigOptions;
+ const heapAnalyticsId = process.env[heapAnalyticsIdEnvName];
+ if (heapAnalyticsIdRequired && !heapAnalyticsId) {
+ throw new Error(
+ `Must define heap analytics id in ENV var ${heapAnalyticsIdEnvName} when building for ${dischargeTarget}`,
+ );
+ }
+ const heapEnabled = heapAnalyticsId && (nodeEnv !== 'development' || process.env.INSTANT_HEAP_FORCE_DEVELOPMENT);
+
+ const rollbarTokens = getRollbarTokens(dischargeTarget, rollbarConfigOptions.rollbarRequired);
+ const rollbarEnabled =
+ rollbarTokens.clientToken && (nodeEnv !== 'development' || process.env.INSTANT_ROLLBAR_FORCE_DEVELOPMENT);
+
+ let rollbarPlugin;
+ if (rollbarConfigOptions.rollbarRequired) {
+ if (!rollbarEnabled || !rollbarTokens.publishToken || !rollbarConfigOptions.rollbarSourceMapPublicPath) {
+ throw new Error(`Rollbar required for ${dischargeTarget} but not configured`);
+ }
+ rollbarPlugin = new RollbarSourceMapPlugin({
+ accessToken: rollbarTokens.publishToken,
+ version: GIT_SHA,
+ publicPath: rollbarConfigOptions.rollbarSourceMapPublicPath,
+ });
+ }
+
+ const envVars = {
+ GIT_SHA: JSON.stringify(GIT_SHA),
+ NPM_PACKAGE_VERSION: JSON.stringify(process.env.npm_package_version),
+ ROLLBAR_ENABLED: rollbarEnabled,
+ HEAP_ENABLED: heapEnabled
+ };
+ if (dischargeTarget) {
+ envVars.INSTANT_DISCHARGE_TARGET = JSON.stringify(dischargeTarget);
+ }
+ if (heapAnalyticsId) {
+ envVars.HEAP_ANALYTICS_ID = JSON.stringify(heapAnalyticsId);
+ }
+ if (rollbarTokens.clientToken) {
+ envVars.ROLLBAR_CLIENT_TOKEN = JSON.stringify(rollbarTokens.clientToken);
+ }
+
+ const plugins = [
+ new webpack.DefinePlugin({
+ 'process.env': envVars,
+ }),
+ ];
+ if (rollbarPlugin) {
+ plugins.push(rollbarPlugin);
+ }
+
const config = {
entry: {
instant: './src/index.umd.ts',
@@ -37,15 +124,7 @@ module.exports = (env, argv) => {
library: 'zeroExInstant',
libraryTarget: 'umd',
},
- plugins: [
- new webpack.DefinePlugin({
- 'process.env': {
- GIT_SHA: JSON.stringify(GIT_SHA),
- HEAP_ANALYTICS_ID: getHeapAnalyticsId(argv.mode),
- NPM_PACKAGE_VERSION: JSON.stringify(process.env.npm_package_version),
- },
- }),
- ],
+ plugins,
devtool: 'source-map',
resolve: {
extensions: ['.js', '.json', '.ts', '.tsx'],
@@ -60,6 +139,15 @@ module.exports = (env, argv) => {
test: /\.svg$/,
loader: 'svg-react-loader',
},
+ {
+ test: /\.js$/,
+ loader: 'source-map-loader',
+ exclude: [
+ // instead of /\/node_modules\//
+ path.join(process.cwd(), 'node_modules'),
+ path.join(process.cwd(), '../..', 'node_modules'),
+ ],
+ },
],
},
devServer: {
@@ -79,3 +167,10 @@ module.exports = (env, argv) => {
};
return config;
};
+
+module.exports = (env, argv) => {
+ const dischargeTarget = env ? env.discharge_target : undefined;
+ const heapConfigOptions = getHeapConfigForDischargeTarget(dischargeTarget);
+ const rollbarConfigOptions = getRollbarConfigForDischargeTarget(dischargeTarget);
+ return generateConfig(dischargeTarget, heapConfigOptions, rollbarConfigOptions, argv.mode);
+};
diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json
index 332fbb466..7622fa5d4 100644
--- a/packages/metacoin/package.json
+++ b/packages/metacoin/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/metacoin",
- "version": "0.0.29",
+ "version": "0.0.30",
"engines": {
"node": ">=6.12"
},
@@ -30,15 +30,15 @@
"license": "Apache-2.0",
"dependencies": {
"@0x/abi-gen": "^1.0.17",
- "@0x/abi-gen-templates": "^1.0.0",
- "@0x/base-contract": "^3.0.7",
- "@0x/sol-cov": "^2.1.13",
- "@0x/subproviders": "^2.1.5",
+ "@0x/abi-gen-templates": "^1.0.1",
+ "@0x/base-contract": "^3.0.8",
+ "@0x/sol-cov": "^2.1.14",
+ "@0x/subproviders": "^2.1.6",
"@0x/tslint-config": "^1.0.10",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@types/mocha": "^5.2.2",
"copyfiles": "^2.0.0",
"ethereum-types": "^1.1.2",
@@ -47,8 +47,8 @@
"run-s": "^0.0.0"
},
"devDependencies": {
- "@0x/dev-utils": "^1.0.18",
- "@0x/sol-compiler": "^1.1.13",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/sol-compiler": "^1.1.14",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^2.0.1",
diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json
index 07031fc09..56705fc1a 100644
--- a/packages/migrations/CHANGELOG.json
+++ b/packages/migrations/CHANGELOG.json
@@ -14,7 +14,8 @@
"note": "Fund the Forwarder with ZRX for fees.",
"pr": 1309
}
- ]
+ ],
+ "timestamp": 1543401373
},
{
"version": "2.1.0",
diff --git a/packages/migrations/CHANGELOG.md b/packages/migrations/CHANGELOG.md
index 986e224b0..3808b2d3d 100644
--- a/packages/migrations/CHANGELOG.md
+++ b/packages/migrations/CHANGELOG.md
@@ -5,6 +5,12 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v2.2.0 - _November 28, 2018_
+
+ * Add CLI `0x-migrate` for running the 0x migrations in a language-agnostic way (#1324)
+ * Deploy testnet Exchange arfitact. Previously mainnet Exchange artifact was deployed. (#1309)
+ * Fund the Forwarder with ZRX for fees. (#1309)
+
## v2.1.0 - _November 21, 2018_
* Export all type declarations used by the public interface, as well as the `ContractAddresses` mapping (#1301)
diff --git a/packages/migrations/package.json b/packages/migrations/package.json
index b006a470a..f4dd1f9f9 100644
--- a/packages/migrations/package.json
+++ b/packages/migrations/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/migrations",
- "version": "2.1.0",
+ "version": "2.2.0",
"engines": {
"node": ">=6.12"
},
@@ -26,7 +26,7 @@
},
"license": "Apache-2.0",
"devDependencies": {
- "@0x/dev-utils": "^1.0.18",
+ "@0x/dev-utils": "^1.0.19",
"@0x/tslint-config": "^1.0.10",
"@0x/types": "^1.3.0",
"@types/yargs": "^10.0.0",
@@ -39,16 +39,16 @@
"yargs": "^10.0.3"
},
"dependencies": {
- "@0x/abi-gen-wrappers": "^1.1.0",
- "@0x/base-contract": "^3.0.7",
- "@0x/contract-addresses": "^1.2.0",
- "@0x/contract-artifacts": "^1.1.0",
- "@0x/order-utils": "^3.0.3",
- "@0x/sol-compiler": "^1.1.13",
- "@0x/subproviders": "^2.1.5",
+ "@0x/abi-gen-wrappers": "^2.0.0",
+ "@0x/base-contract": "^3.0.8",
+ "@0x/contract-addresses": "^2.0.0",
+ "@0x/contract-artifacts": "^1.1.2",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/sol-compiler": "^1.1.14",
+ "@0x/subproviders": "^2.1.6",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@ledgerhq/hw-app-eth": "^4.3.0",
"ethereum-types": "^1.1.2",
"ethers": "~4.0.4",
diff --git a/packages/monorepo-scripts/CHANGELOG.json b/packages/monorepo-scripts/CHANGELOG.json
index 170a97a33..428168437 100644
--- a/packages/monorepo-scripts/CHANGELOG.json
+++ b/packages/monorepo-scripts/CHANGELOG.json
@@ -13,6 +13,10 @@
{
"note": "Add ForwarderError to the IGNORED_EXCESSIVE_TYPES array",
"pr": 1147
+ },
+ {
+ "note": "Fix a bug when hardcoded CHANGELOG paths cause fetching release notes to fail",
+ "pr": 1311
}
]
},
diff --git a/packages/monorepo-scripts/src/prepublish_checks.ts b/packages/monorepo-scripts/src/prepublish_checks.ts
index 60cdccf1d..36e61714b 100644
--- a/packages/monorepo-scripts/src/prepublish_checks.ts
+++ b/packages/monorepo-scripts/src/prepublish_checks.ts
@@ -17,7 +17,6 @@ async function prepublishChecksAsync(): Promise<void> {
await checkChangelogFormatAsync(updatedPublicPackages);
await checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicPackages);
await checkPublishRequiredSetupAsync();
- checkRequiredEnvVariables();
}
async function checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicPackages: Package[]): Promise<void> {
@@ -184,16 +183,6 @@ async function checkPublishRequiredSetupAsync(): Promise<void> {
}
}
-const checkRequiredEnvVariables = () => {
- utils.log('Checking required environment variables...');
- const requiredEnvVars = ['INSTANT_HEAP_ANALYTICS_ID_PRODUCTION'];
- requiredEnvVars.forEach(requiredEnvVarName => {
- if (_.isUndefined(process.env[requiredEnvVarName])) {
- throw new Error(`Must have ${requiredEnvVarName} set`);
- }
- });
-};
-
prepublishChecksAsync().catch(err => {
utils.log(err);
process.exit(1);
diff --git a/packages/monorepo-scripts/src/utils/github_release_utils.ts b/packages/monorepo-scripts/src/utils/github_release_utils.ts
index 7434d397e..e63244b46 100644
--- a/packages/monorepo-scripts/src/utils/github_release_utils.ts
+++ b/packages/monorepo-scripts/src/utils/github_release_utils.ts
@@ -41,7 +41,7 @@ export async function publishReleaseNotesAsync(packagesToPublish: Package[], isD
let assets: string[] = [];
let aggregateNotes = '';
_.each(packagesToPublish, pkg => {
- aggregateNotes += getReleaseNotesForPackage(pkg.packageJson.name);
+ aggregateNotes += getReleaseNotesForPackage(pkg.location, pkg.packageJson.name);
const packageAssets = _.get(pkg.packageJson, 'config.postpublish.assets');
if (!_.isUndefined(packageAssets)) {
@@ -88,14 +88,8 @@ function adjustAssetPaths(assets: string[]): string[] {
return finalAssets;
}
-function getReleaseNotesForPackage(packageName: string): string {
- const packageNameWithoutNamespace = packageName.replace('@0x/', '');
- const changelogJSONPath = path.join(
- constants.monorepoRootPath,
- 'packages',
- packageNameWithoutNamespace,
- 'CHANGELOG.json',
- );
+function getReleaseNotesForPackage(packageLocation: string, packageName: string): string {
+ const changelogJSONPath = path.join(packageLocation, 'CHANGELOG.json');
const changelogJSON = readFileSync(changelogJSONPath, 'utf-8');
const changelogs = JSON.parse(changelogJSON);
const latestLog = changelogs[0];
diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json
index a4f5dc622..6c8fd6239 100644
--- a/packages/order-utils/CHANGELOG.json
+++ b/packages/order-utils/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "3.0.4",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "3.0.3",
"changes": [
diff --git a/packages/order-utils/CHANGELOG.md b/packages/order-utils/CHANGELOG.md
index b863cbc03..5eae590b5 100644
--- a/packages/order-utils/CHANGELOG.md
+++ b/packages/order-utils/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v3.0.4 - _November 28, 2018_
+
+ * Dependencies updated
+
## v3.0.3 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json
index e032d6e7d..50229dafb 100644
--- a/packages/order-utils/package.json
+++ b/packages/order-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/order-utils",
- "version": "3.0.3",
+ "version": "3.0.4",
"engines": {
"node": ">=6.12"
},
@@ -35,7 +35,7 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md",
"devDependencies": {
- "@0x/dev-utils": "^1.0.18",
+ "@0x/dev-utils": "^1.0.19",
"@0x/tslint-config": "^1.0.10",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
@@ -53,15 +53,15 @@
"typescript": "3.0.1"
},
"dependencies": {
- "@0x/abi-gen-wrappers": "^1.1.0",
+ "@0x/abi-gen-wrappers": "^2.0.0",
"@0x/assert": "^1.0.18",
- "@0x/base-contract": "^3.0.7",
- "@0x/contract-artifacts": "^1.1.0",
+ "@0x/base-contract": "^3.0.8",
+ "@0x/contract-artifacts": "^1.1.2",
"@0x/json-schemas": "^2.1.2",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@types/node": "*",
"bn.js": "^4.11.8",
"ethereum-types": "^1.1.2",
diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json
index ca2de9831..4e56dc400 100644
--- a/packages/order-watcher/CHANGELOG.json
+++ b/packages/order-watcher/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "2.2.6",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "2.2.5",
"changes": [
diff --git a/packages/order-watcher/CHANGELOG.md b/packages/order-watcher/CHANGELOG.md
index 7ae47fdda..37b4a7438 100644
--- a/packages/order-watcher/CHANGELOG.md
+++ b/packages/order-watcher/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v2.2.6 - _November 28, 2018_
+
+ * Dependencies updated
+
## v2.2.5 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json
index 4257bd2a8..9a51203f4 100644
--- a/packages/order-watcher/package.json
+++ b/packages/order-watcher/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/order-watcher",
- "version": "2.2.5",
+ "version": "2.2.6",
"description": "An order watcher daemon that watches for order validity",
"keywords": [
"0x",
@@ -33,8 +33,8 @@
"node": ">=6.0.0"
},
"devDependencies": {
- "@0x/dev-utils": "^1.0.18",
- "@0x/migrations": "^2.1.0",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/migrations": "^2.2.0",
"@0x/tslint-config": "^1.0.10",
"@types/bintrees": "^1.0.2",
"@types/lodash": "4.14.104",
@@ -57,19 +57,19 @@
"typescript": "3.0.1"
},
"dependencies": {
- "@0x/abi-gen-wrappers": "^1.1.0",
+ "@0x/abi-gen-wrappers": "^2.0.0",
"@0x/assert": "^1.0.18",
- "@0x/base-contract": "^3.0.7",
- "@0x/contract-addresses": "^1.2.0",
- "@0x/contract-artifacts": "^1.1.0",
- "@0x/contract-wrappers": "^4.1.0",
- "@0x/fill-scenarios": "^1.0.13",
+ "@0x/base-contract": "^3.0.8",
+ "@0x/contract-addresses": "^2.0.0",
+ "@0x/contract-artifacts": "^1.1.2",
+ "@0x/contract-wrappers": "^4.1.1",
+ "@0x/fill-scenarios": "^1.0.14",
"@0x/json-schemas": "^2.1.2",
- "@0x/order-utils": "^3.0.3",
+ "@0x/order-utils": "^3.0.4",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"bintrees": "^1.0.2",
"ethereum-types": "^1.1.2",
"ethereumjs-blockstream": "6.0.0",
diff --git a/packages/react-docs/CHANGELOG.json b/packages/react-docs/CHANGELOG.json
index cecc270eb..d456a3b53 100644
--- a/packages/react-docs/CHANGELOG.json
+++ b/packages/react-docs/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "1.0.20",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "1.0.19",
"changes": [
diff --git a/packages/react-docs/CHANGELOG.md b/packages/react-docs/CHANGELOG.md
index f0a56191d..e48f43fb8 100644
--- a/packages/react-docs/CHANGELOG.md
+++ b/packages/react-docs/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v1.0.20 - _November 28, 2018_
+
+ * Dependencies updated
+
## v1.0.19 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json
index 6e819ee72..968ac4e34 100644
--- a/packages/react-docs/package.json
+++ b/packages/react-docs/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/react-docs",
- "version": "1.0.19",
+ "version": "1.0.20",
"engines": {
"node": ">=6.12"
},
@@ -24,7 +24,7 @@
"url": "https://github.com/0xProject/0x-monorepo.git"
},
"devDependencies": {
- "@0x/dev-utils": "^1.0.18",
+ "@0x/dev-utils": "^1.0.19",
"@0x/tslint-config": "^1.0.10",
"@types/compare-versions": "^3.0.0",
"@types/styled-components": "^4.0.0",
@@ -34,7 +34,7 @@
"typescript": "3.0.1"
},
"dependencies": {
- "@0x/react-shared": "^1.0.22",
+ "@0x/react-shared": "^1.0.23",
"@0x/types": "^1.3.0",
"@0x/utils": "^2.0.6",
"@types/lodash": "4.14.104",
diff --git a/packages/react-shared/CHANGELOG.json b/packages/react-shared/CHANGELOG.json
index bcbf2d9f9..a376bae29 100644
--- a/packages/react-shared/CHANGELOG.json
+++ b/packages/react-shared/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "1.0.23",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "1.0.22",
"changes": [
diff --git a/packages/react-shared/CHANGELOG.md b/packages/react-shared/CHANGELOG.md
index c6fb9e479..a983e0af2 100644
--- a/packages/react-shared/CHANGELOG.md
+++ b/packages/react-shared/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v1.0.23 - _November 28, 2018_
+
+ * Dependencies updated
+
## v1.0.22 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/react-shared/package.json b/packages/react-shared/package.json
index 34412e14e..b5816ad98 100644
--- a/packages/react-shared/package.json
+++ b/packages/react-shared/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/react-shared",
- "version": "1.0.22",
+ "version": "1.0.23",
"engines": {
"node": ">=6.12"
},
@@ -25,7 +25,7 @@
"url": "https://github.com/0xProject/0x-monorepo.git"
},
"devDependencies": {
- "@0x/dev-utils": "^1.0.18",
+ "@0x/dev-utils": "^1.0.19",
"@0x/tslint-config": "^1.0.10",
"make-promises-safe": "^1.1.0",
"shx": "^0.2.2",
diff --git a/packages/sol-compiler/CHANGELOG.json b/packages/sol-compiler/CHANGELOG.json
index 2ca983c59..fe077b6cc 100644
--- a/packages/sol-compiler/CHANGELOG.json
+++ b/packages/sol-compiler/CHANGELOG.json
@@ -1,5 +1,23 @@
[
{
+ "version": "1.1.15",
+ "changes": [
+ {
+ "note": "Fix bug where we were appending base path to absolute imports (e.g NPM imports)",
+ "pr": 1311
+ }
+ ]
+ },
+ {
+ "timestamp": 1543401373,
+ "version": "1.1.14",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "1.1.13",
"changes": [
diff --git a/packages/sol-compiler/CHANGELOG.md b/packages/sol-compiler/CHANGELOG.md
index e535df64e..a1782bb3b 100644
--- a/packages/sol-compiler/CHANGELOG.md
+++ b/packages/sol-compiler/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v1.1.14 - _November 28, 2018_
+
+ * Dependencies updated
+
## v1.1.13 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/sol-compiler/package.json b/packages/sol-compiler/package.json
index bcc235866..d27c0ee31 100644
--- a/packages/sol-compiler/package.json
+++ b/packages/sol-compiler/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/sol-compiler",
- "version": "1.1.13",
+ "version": "1.1.14",
"engines": {
"node": ">=6.12"
},
@@ -42,7 +42,7 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/sol-compiler/README.md",
"devDependencies": {
- "@0x/dev-utils": "^1.0.18",
+ "@0x/dev-utils": "^1.0.19",
"@0x/tslint-config": "^1.0.10",
"@types/mkdirp": "^0.5.2",
"@types/require-from-string": "^1.2.0",
@@ -71,7 +71,7 @@
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@types/yargs": "^11.0.0",
"chalk": "^2.3.0",
"ethereum-types": "^1.1.2",
diff --git a/packages/sol-compiler/src/compiler.ts b/packages/sol-compiler/src/compiler.ts
index 8ee7fa4a9..cba67f292 100644
--- a/packages/sol-compiler/src/compiler.ts
+++ b/packages/sol-compiler/src/compiler.ts
@@ -394,7 +394,14 @@ export class Compiler {
//
const lastPathSeparatorPos = contractPath.lastIndexOf('/');
const contractFolder = lastPathSeparatorPos === -1 ? '' : contractPath.slice(0, lastPathSeparatorPos + 1);
- importPath = path.resolve('/' + contractFolder, importPath).replace('/', '');
+ if (importPath.startsWith('.')) {
+ /**
+ * Some imports path are relative ("../Token.sol", "./Wallet.sol")
+ * while others are absolute ("Token.sol", "@0x/contracts/Wallet.sol")
+ * And we need to append the base path for relative imports.
+ */
+ importPath = path.resolve('/' + contractFolder, importPath).replace('/', '');
+ }
if (_.isUndefined(sourcesToAppendTo[importPath])) {
sourcesToAppendTo[importPath] = { id: fullSources[importPath].id };
diff --git a/packages/sol-cov/CHANGELOG.json b/packages/sol-cov/CHANGELOG.json
index 3dd04be8c..bc8aa71e1 100644
--- a/packages/sol-cov/CHANGELOG.json
+++ b/packages/sol-cov/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "2.1.14",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "2.1.13",
"changes": [
diff --git a/packages/sol-cov/CHANGELOG.md b/packages/sol-cov/CHANGELOG.md
index e56a1393e..25ba93026 100644
--- a/packages/sol-cov/CHANGELOG.md
+++ b/packages/sol-cov/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v2.1.14 - _November 28, 2018_
+
+ * Dependencies updated
+
## v2.1.13 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json
index 9dcb3b854..73c11980f 100644
--- a/packages/sol-cov/package.json
+++ b/packages/sol-cov/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/sol-cov",
- "version": "2.1.13",
+ "version": "2.1.14",
"engines": {
"node": ">=6.12"
},
@@ -42,12 +42,12 @@
},
"homepage": "https://github.com/0xProject/0x.js/packages/sol-cov/README.md",
"dependencies": {
- "@0x/dev-utils": "^1.0.18",
- "@0x/sol-compiler": "^1.1.13",
- "@0x/subproviders": "^2.1.5",
+ "@0x/dev-utils": "^1.0.19",
+ "@0x/sol-compiler": "^1.1.14",
+ "@0x/subproviders": "^2.1.6",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@types/solidity-parser-antlr": "^0.2.0",
"ethereum-types": "^1.1.2",
"ethereumjs-util": "^5.1.1",
diff --git a/packages/sol-doc/CHANGELOG.json b/packages/sol-doc/CHANGELOG.json
index c3dcc81f1..332aeb025 100644
--- a/packages/sol-doc/CHANGELOG.json
+++ b/packages/sol-doc/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "1.0.9",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "1.0.8",
"changes": [
diff --git a/packages/sol-doc/CHANGELOG.md b/packages/sol-doc/CHANGELOG.md
index a7a7fa0fa..5a1df59c7 100644
--- a/packages/sol-doc/CHANGELOG.md
+++ b/packages/sol-doc/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v1.0.9 - _November 28, 2018_
+
+ * Dependencies updated
+
## v1.0.8 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/sol-doc/package.json b/packages/sol-doc/package.json
index 6bf76d47a..edf1707d6 100644
--- a/packages/sol-doc/package.json
+++ b/packages/sol-doc/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/sol-doc",
- "version": "1.0.8",
+ "version": "1.0.9",
"description": "Solidity documentation generator",
"main": "lib/src/index.js",
"types": "lib/src/index.d.js",
@@ -25,7 +25,7 @@
"author": "F. Eugene Aumson",
"license": "Apache-2.0",
"dependencies": {
- "@0x/sol-compiler": "^1.1.13",
+ "@0x/sol-compiler": "^1.1.14",
"@0x/types": "^1.3.0",
"@0x/utils": "^2.0.6",
"ethereum-types": "^1.1.2",
diff --git a/packages/sol-resolver/CHANGELOG.json b/packages/sol-resolver/CHANGELOG.json
index fdfb4009b..4c9e612d7 100644
--- a/packages/sol-resolver/CHANGELOG.json
+++ b/packages/sol-resolver/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "1.1.0",
+ "changes": [
+ {
+ "note": "NPMResolver now supports scoped packages",
+ "pr": 1311
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "1.0.17",
"changes": [
diff --git a/packages/sol-resolver/src/resolvers/npm_resolver.ts b/packages/sol-resolver/src/resolvers/npm_resolver.ts
index a2df0dcad..eeb2b5493 100644
--- a/packages/sol-resolver/src/resolvers/npm_resolver.ts
+++ b/packages/sol-resolver/src/resolvers/npm_resolver.ts
@@ -1,4 +1,5 @@
import * as fs from 'fs';
+import * as _ from 'lodash';
import * as path from 'path';
import { ContractSource } from '../types';
@@ -13,12 +14,22 @@ export class NPMResolver extends Resolver {
}
public resolveIfExists(importPath: string): ContractSource | undefined {
if (!importPath.startsWith('/')) {
- const [packageName, ...other] = importPath.split('/');
+ let packageName;
+ let packageScopeIfExists;
+ let other;
+ if (_.startsWith(importPath, '@')) {
+ [packageScopeIfExists, packageName, ...other] = importPath.split('/');
+ } else {
+ [packageName, ...other] = importPath.split('/');
+ }
const pathWithinPackage = path.join(...other);
let currentPath = this._packagePath;
const ROOT_PATH = '/';
while (currentPath !== ROOT_PATH) {
- const lookupPath = path.join(currentPath, 'node_modules', packageName, pathWithinPackage);
+ const packagePath = _.isUndefined(packageScopeIfExists)
+ ? packageName
+ : path.join(packageScopeIfExists, packageName);
+ const lookupPath = path.join(currentPath, 'node_modules', packagePath, pathWithinPackage);
if (fs.existsSync(lookupPath) && fs.lstatSync(lookupPath).isFile()) {
const fileContent = fs.readFileSync(lookupPath).toString();
return {
diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json
index 62c495b49..6da170be3 100644
--- a/packages/subproviders/CHANGELOG.json
+++ b/packages/subproviders/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "timestamp": 1543401373,
+ "version": "2.1.6",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "2.1.5",
"changes": [
diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md
index 0251b6d9a..01dd8d652 100644
--- a/packages/subproviders/CHANGELOG.md
+++ b/packages/subproviders/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v2.1.6 - _November 28, 2018_
+
+ * Dependencies updated
+
## v2.1.5 - _November 21, 2018_
* Dependencies updated
diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json
index 3b367054b..86f3738af 100644
--- a/packages/subproviders/package.json
+++ b/packages/subproviders/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/subproviders",
- "version": "2.1.5",
+ "version": "2.1.6",
"engines": {
"node": ">=6.12"
},
@@ -33,7 +33,7 @@
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"@ledgerhq/hw-app-eth": "^4.3.0",
"@ledgerhq/hw-transport-u2f": "4.24.0",
"@types/eth-lightwallet": "^3.0.0",
diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json
index cdfd1b7ff..8c4b942a3 100644
--- a/packages/testnet-faucets/package.json
+++ b/packages/testnet-faucets/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@0x/testnet-faucets",
- "version": "1.0.57",
+ "version": "1.0.58",
"engines": {
"node": ">=6.12"
},
@@ -18,11 +18,11 @@
"author": "Fabio Berger",
"license": "Apache-2.0",
"dependencies": {
- "0x.js": "^2.0.5",
- "@0x/subproviders": "^2.1.5",
+ "0x.js": "^2.0.6",
+ "@0x/subproviders": "^2.1.6",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
+ "@0x/web3-wrapper": "^3.1.6",
"body-parser": "^1.17.1",
"ethereum-types": "^1.1.2",
"ethereumjs-tx": "^1.3.5",
diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json
index 0b32b60f0..b09859101 100644
--- a/packages/types/CHANGELOG.json
+++ b/packages/types/CHANGELOG.json
@@ -1,5 +1,18 @@
[
{
+ "version": "1.4.0",
+ "changes": [
+ {
+ "note": "Add `LengthMismatch` and `LengthGreaterThan3Required` revert reasons",
+ "pr": 1224
+ },
+ {
+ "note": "Add RevertReasons for DutchAuction contract",
+ "pr": 1225
+ }
+ ]
+ },
+ {
"version": "1.3.0",
"changes": [
{
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 1a86f45e6..6b728af71 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -195,6 +195,7 @@ export enum RevertReason {
FailedExecution = 'FAILED_EXECUTION',
AssetProxyAlreadyExists = 'ASSET_PROXY_ALREADY_EXISTS',
LengthGreaterThan0Required = 'LENGTH_GREATER_THAN_0_REQUIRED',
+ LengthGreaterThan3Required = 'LENGTH_GREATER_THAN_3_REQUIRED',
LengthGreaterThan131Required = 'LENGTH_GREATER_THAN_131_REQUIRED',
Length0Required = 'LENGTH_0_REQUIRED',
Length65Required = 'LENGTH_65_REQUIRED',
@@ -209,6 +210,7 @@ export enum RevertReason {
MakerNotWhitelisted = 'MAKER_NOT_WHITELISTED',
TakerNotWhitelisted = 'TAKER_NOT_WHITELISTED',
AssetProxyDoesNotExist = 'ASSET_PROXY_DOES_NOT_EXIST',
+ LengthMismatch = 'LENGTH_MISMATCH',
LibBytesGreaterThanZeroLengthRequired = 'GREATER_THAN_ZERO_LENGTH_REQUIRED',
LibBytesGreaterOrEqualTo4LengthRequired = 'GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED',
LibBytesGreaterOrEqualTo20LengthRequired = 'GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED',
@@ -235,6 +237,12 @@ export enum RevertReason {
TxFullyConfirmed = 'TX_FULLY_CONFIRMED',
TxNotFullyConfirmed = 'TX_NOT_FULLY_CONFIRMED',
TimeLockIncomplete = 'TIME_LOCK_INCOMPLETE',
+ // DutchAuction
+ AuctionInvalidAmount = 'INVALID_AMOUNT',
+ AuctionExpired = 'AUCTION_EXPIRED',
+ AuctionNotStarted = 'AUCTION_NOT_STARTED',
+ AuctionInvalidBeginTime = 'INVALID_BEGIN_TIME',
+ InvalidAssetData = 'INVALID_ASSET_DATA',
}
export enum StatusCodes {
diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json
index 8c6fb124f..08801a891 100644
--- a/packages/utils/CHANGELOG.json
+++ b/packages/utils/CHANGELOG.json
@@ -1,5 +1,15 @@
[
{
+ "timestamp": 1543448882,
+ "version": "2.0.7",
+ "changes": [
+ {
+ "note":
+ "Optimized ABI Encoder/Decoder. Generates compressed calldata to save gas. Generates human-readable calldata to aid development."
+ }
+ ]
+ },
+ {
"timestamp": 1542821676,
"version": "2.0.6",
"changes": [
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 8dc1f0739..1f4d85843 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -33,6 +33,9 @@
"@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42",
"chai": "^4.0.1",
+ "chai-as-promised": "^7.1.0",
+ "chai-bignumber": "^2.0.1",
+ "dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^4.1.0",
"npm-run-all": "^4.1.2",
@@ -57,4 +60,4 @@
"publishConfig": {
"access": "public"
}
-}
+} \ No newline at end of file
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts
new file mode 100644
index 000000000..13cc87e2a
--- /dev/null
+++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts
@@ -0,0 +1,58 @@
+import { DataItem } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { Calldata } from '../calldata/calldata';
+import { CalldataBlock } from '../calldata/calldata_block';
+import { RawCalldata } from '../calldata/raw_calldata';
+import { constants } from '../utils/constants';
+import { DecodingRules, EncodingRules } from '../utils/rules';
+
+import { DataTypeFactory } from './interfaces';
+
+export abstract class DataType {
+ private readonly _dataItem: DataItem;
+ private readonly _factory: DataTypeFactory;
+
+ constructor(dataItem: DataItem, factory: DataTypeFactory) {
+ this._dataItem = dataItem;
+ this._factory = factory;
+ }
+
+ public getDataItem(): DataItem {
+ return this._dataItem;
+ }
+
+ public getFactory(): DataTypeFactory {
+ return this._factory;
+ }
+
+ public encode(value: any, rules?: EncodingRules, selector?: string): string {
+ const rules_ = _.isUndefined(rules) ? constants.DEFAULT_ENCODING_RULES : rules;
+ const calldata = new Calldata(rules_);
+ if (!_.isUndefined(selector)) {
+ calldata.setSelector(selector);
+ }
+ const block = this.generateCalldataBlock(value);
+ calldata.setRoot(block);
+ const encodedCalldata = calldata.toString();
+ return encodedCalldata;
+ }
+
+ public decode(calldata: string, rules?: DecodingRules, selector?: string): any {
+ if (!_.isUndefined(selector) && !_.startsWith(calldata, selector)) {
+ throw new Error(
+ `Tried to decode calldata, but it was missing the function selector. Expected prefix '${selector}'. Got '${calldata}'.`,
+ );
+ }
+ const hasSelector = !_.isUndefined(selector);
+ const rawCalldata = new RawCalldata(calldata, hasSelector);
+ const rules_ = _.isUndefined(rules) ? constants.DEFAULT_DECODING_RULES : rules;
+ const value = this.generateValue(rawCalldata, rules_);
+ return value;
+ }
+
+ public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock;
+ public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any;
+ public abstract getSignature(): string;
+ public abstract isStatic(): boolean;
+}
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts
new file mode 100644
index 000000000..2f2f60871
--- /dev/null
+++ b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts
@@ -0,0 +1,19 @@
+import { DataItem } from 'ethereum-types';
+
+import { RawCalldata } from '../calldata/raw_calldata';
+
+import { DataType } from './data_type';
+
+export interface DataTypeFactory {
+ create: (dataItem: DataItem, parentDataType?: DataType) => DataType;
+}
+
+export interface DataTypeStaticInterface {
+ matchType: (type: string) => boolean;
+ encodeValue: (value: any) => Buffer;
+ decodeValue: (rawCalldata: RawCalldata) => any;
+}
+
+export interface MemberIndexByName {
+ [key: string]: number;
+}
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts
new file mode 100644
index 000000000..a091e55b9
--- /dev/null
+++ b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts
@@ -0,0 +1,40 @@
+import { DataItem } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { BlobCalldataBlock } from '../../calldata/blocks/blob';
+import { CalldataBlock } from '../../calldata/calldata_block';
+import { RawCalldata } from '../../calldata/raw_calldata';
+import { DecodingRules } from '../../utils/rules';
+
+import { DataType } from '../data_type';
+import { DataTypeFactory } from '../interfaces';
+
+export abstract class AbstractBlobDataType extends DataType {
+ protected _sizeKnownAtCompileTime: boolean;
+
+ public constructor(dataItem: DataItem, factory: DataTypeFactory, sizeKnownAtCompileTime: boolean) {
+ super(dataItem, factory);
+ this._sizeKnownAtCompileTime = sizeKnownAtCompileTime;
+ }
+
+ public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): BlobCalldataBlock {
+ const encodedValue = this.encodeValue(value);
+ const name = this.getDataItem().name;
+ const signature = this.getSignature();
+ const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName();
+ const block = new BlobCalldataBlock(name, signature, parentName, encodedValue);
+ return block;
+ }
+
+ public generateValue(calldata: RawCalldata, rules: DecodingRules): any {
+ const value = this.decodeValue(calldata);
+ return value;
+ }
+
+ public isStatic(): boolean {
+ return this._sizeKnownAtCompileTime;
+ }
+
+ public abstract encodeValue(value: any): Buffer;
+ public abstract decodeValue(calldata: RawCalldata): any;
+}
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts
new file mode 100644
index 000000000..0f3c55280
--- /dev/null
+++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts
@@ -0,0 +1,54 @@
+import { DataItem } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { PointerCalldataBlock } from '../../calldata/blocks/pointer';
+import { CalldataBlock } from '../../calldata/calldata_block';
+import { RawCalldata } from '../../calldata/raw_calldata';
+import { constants } from '../../utils/constants';
+import { DecodingRules } from '../../utils/rules';
+
+import { DataType } from '../data_type';
+import { DataTypeFactory } from '../interfaces';
+
+export abstract class AbstractPointerDataType extends DataType {
+ protected _destination: DataType;
+ protected _parent: DataType;
+
+ public constructor(dataItem: DataItem, factory: DataTypeFactory, destination: DataType, parent: DataType) {
+ super(dataItem, factory);
+ this._destination = destination;
+ this._parent = parent;
+ }
+
+ public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PointerCalldataBlock {
+ if (_.isUndefined(parentBlock)) {
+ throw new Error(`DependentDataType requires a parent block to generate its block`);
+ }
+ const destinationBlock = this._destination.generateCalldataBlock(value, parentBlock);
+ const name = this.getDataItem().name;
+ const signature = this.getSignature();
+ const parentName = parentBlock.getName();
+ const block = new PointerCalldataBlock(name, signature, parentName, destinationBlock, parentBlock);
+ return block;
+ }
+
+ public generateValue(calldata: RawCalldata, rules: DecodingRules): any {
+ const destinationOffsetBuf = calldata.popWord();
+ const destinationOffsetHex = ethUtil.bufferToHex(destinationOffsetBuf);
+ const destinationOffsetRelative = parseInt(destinationOffsetHex, constants.HEX_BASE);
+ const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative);
+ const currentOffset = calldata.getOffset();
+ calldata.setOffset(destinationOffsetAbsolute);
+ const value = this._destination.generateValue(calldata, rules);
+ calldata.setOffset(currentOffset);
+ return value;
+ }
+
+ // Disable prefer-function-over-method for inherited abstract method.
+ /* tslint:disable prefer-function-over-method */
+ public isStatic(): boolean {
+ return true;
+ }
+ /* tslint:enable prefer-function-over-method */
+}
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
new file mode 100644
index 000000000..089d04659
--- /dev/null
+++ b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
@@ -0,0 +1,218 @@
+import { DataItem } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { BigNumber } from '../../../configured_bignumber';
+import { SetCalldataBlock } from '../../calldata/blocks/set';
+import { CalldataBlock } from '../../calldata/calldata_block';
+import { RawCalldata } from '../../calldata/raw_calldata';
+import { constants } from '../../utils/constants';
+import { DecodingRules } from '../../utils/rules';
+
+import { DataType } from '../data_type';
+import { DataTypeFactory, MemberIndexByName } from '../interfaces';
+
+import { AbstractPointerDataType } from './pointer';
+
+export abstract class AbstractSetDataType extends DataType {
+ protected readonly _arrayLength: number | undefined;
+ protected readonly _arrayElementType: string | undefined;
+ private readonly _memberIndexByName: MemberIndexByName;
+ private readonly _members: DataType[];
+ private readonly _isArray: boolean;
+
+ public constructor(
+ dataItem: DataItem,
+ factory: DataTypeFactory,
+ isArray: boolean = false,
+ arrayLength?: number,
+ arrayElementType?: string,
+ ) {
+ super(dataItem, factory);
+ this._memberIndexByName = {};
+ this._members = [];
+ this._isArray = isArray;
+ this._arrayLength = arrayLength;
+ this._arrayElementType = arrayElementType;
+ if (isArray && !_.isUndefined(arrayLength)) {
+ [this._members, this._memberIndexByName] = this._createMembersWithLength(dataItem, arrayLength);
+ } else if (!isArray) {
+ [this._members, this._memberIndexByName] = this._createMembersWithKeys(dataItem);
+ }
+ }
+
+ public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): SetCalldataBlock {
+ const block =
+ value instanceof Array
+ ? this._generateCalldataBlockFromArray(value, parentBlock)
+ : this._generateCalldataBlockFromObject(value, parentBlock);
+ return block;
+ }
+
+ public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object {
+ let members = this._members;
+ // Case 1: This is an array of undefined length, which means that `this._members` was not
+ // populated in the constructor. So we must construct the set of members now.
+ if (this._isArray && _.isUndefined(this._arrayLength)) {
+ const arrayLengthBuf = calldata.popWord();
+ const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf);
+ const arrayLength = new BigNumber(arrayLengthHex, constants.HEX_BASE);
+ [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber());
+ }
+ // Create a new scope in the calldata, before descending into the members of this set.
+ calldata.startScope();
+ let value: any[] | object;
+ if (rules.structsAsObjects && !this._isArray) {
+ // Construct an object with values for each member of the set.
+ value = {};
+ _.each(this._memberIndexByName, (idx: number, key: string) => {
+ const member = this._members[idx];
+ const memberValue = member.generateValue(calldata, rules);
+ (value as { [key: string]: any })[key] = memberValue;
+ });
+ } else {
+ // Construct an array with values for each member of the set.
+ value = [];
+ _.each(members, (member: DataType, idx: number) => {
+ const memberValue = member.generateValue(calldata, rules);
+ (value as any[]).push(memberValue);
+ });
+ }
+ // Close this scope and return tetheh value.
+ calldata.endScope();
+ return value;
+ }
+
+ public isStatic(): boolean {
+ // An array with an undefined length is never static.
+ if (this._isArray && _.isUndefined(this._arrayLength)) {
+ return false;
+ }
+ // If any member of the set is a pointer then the set is not static.
+ const dependentMember = _.find(this._members, (member: DataType) => {
+ return member instanceof AbstractPointerDataType;
+ });
+ const isStatic = _.isUndefined(dependentMember);
+ return isStatic;
+ }
+
+ protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): SetCalldataBlock {
+ // Sanity check: if the set has a defined length then `value` must have the same length.
+ if (!_.isUndefined(this._arrayLength) && value.length !== this._arrayLength) {
+ throw new Error(
+ `Expected array of ${JSON.stringify(
+ this._arrayLength,
+ )} elements, but got array of length ${JSON.stringify(value.length)}`,
+ );
+ }
+ // Create a new calldata block for this set.
+ const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName();
+ const block = new SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName);
+ // If this set has an undefined length then set its header to be the number of elements.
+ let members = this._members;
+ if (this._isArray && _.isUndefined(this._arrayLength)) {
+ [members] = this._createMembersWithLength(this.getDataItem(), value.length);
+ const lenBuf = ethUtil.setLengthLeft(
+ ethUtil.toBuffer(`0x${value.length.toString(constants.HEX_BASE)}`),
+ constants.EVM_WORD_WIDTH_IN_BYTES,
+ );
+ block.setHeader(lenBuf);
+ }
+ // Create blocks for members of set.
+ const memberCalldataBlocks: CalldataBlock[] = [];
+ _.each(members, (member: DataType, idx: number) => {
+ const memberBlock = member.generateCalldataBlock(value[idx], block);
+ memberCalldataBlocks.push(memberBlock);
+ });
+ block.setMembers(memberCalldataBlocks);
+ return block;
+ }
+
+ protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): SetCalldataBlock {
+ // Create a new calldata block for this set.
+ const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName();
+ const block = new SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName);
+ // Create blocks for members of set.
+ const memberCalldataBlocks: CalldataBlock[] = [];
+ const childMap = _.cloneDeep(this._memberIndexByName);
+ _.forOwn(obj, (value: any, key: string) => {
+ if (!(key in childMap)) {
+ throw new Error(
+ `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`,
+ );
+ }
+ const memberBlock = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, block);
+ memberCalldataBlocks.push(memberBlock);
+ delete childMap[key];
+ });
+ // Sanity check that all members have been included.
+ if (Object.keys(childMap).length !== 0) {
+ throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`);
+ }
+ // Associate member blocks with Set block.
+ block.setMembers(memberCalldataBlocks);
+ return block;
+ }
+
+ protected _computeSignatureOfMembers(): string {
+ // Compute signature of members
+ let signature = `(`;
+ _.each(this._members, (member: DataType, i: number) => {
+ signature += member.getSignature();
+ if (i < this._members.length - 1) {
+ signature += ',';
+ }
+ });
+ signature += ')';
+ return signature;
+ }
+
+ private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberIndexByName] {
+ // Sanity check
+ if (_.isUndefined(dataItem.components)) {
+ throw new Error(
+ `Tried to create a set using key/value pairs, but no components were defined by the input DataItem '${
+ dataItem.name
+ }'.`,
+ );
+ }
+ // Create one member for each component of `dataItem`
+ const members: DataType[] = [];
+ const memberIndexByName: MemberIndexByName = {};
+ _.each(dataItem.components, (memberItem: DataItem) => {
+ const childDataItem: DataItem = {
+ type: memberItem.type,
+ name: `${dataItem.name}.${memberItem.name}`,
+ };
+ const components = memberItem.components;
+ if (!_.isUndefined(components)) {
+ childDataItem.components = components;
+ }
+ const child = this.getFactory().create(childDataItem, this);
+ memberIndexByName[memberItem.name] = members.length;
+ members.push(child);
+ });
+ return [members, memberIndexByName];
+ }
+
+ private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberIndexByName] {
+ // Create `length` members, deriving the type from `dataItem`
+ const members: DataType[] = [];
+ const memberIndexByName: MemberIndexByName = {};
+ const range = _.range(length);
+ _.each(range, (idx: number) => {
+ const memberDataItem: DataItem = {
+ type: _.isUndefined(this._arrayElementType) ? '' : this._arrayElementType,
+ name: `${dataItem.name}[${idx.toString(constants.DEC_BASE)}]`,
+ };
+ const components = dataItem.components;
+ if (!_.isUndefined(components)) {
+ memberDataItem.components = components;
+ }
+ const memberType = this.getFactory().create(memberDataItem, this);
+ memberIndexByName[idx.toString(constants.DEC_BASE)] = members.length;
+ members.push(memberType);
+ });
+ return [members, memberIndexByName];
+ }
+}
diff --git a/packages/utils/src/abi_encoder/calldata/blocks/blob.ts b/packages/utils/src/abi_encoder/calldata/blocks/blob.ts
new file mode 100644
index 000000000..219ea6c61
--- /dev/null
+++ b/packages/utils/src/abi_encoder/calldata/blocks/blob.ts
@@ -0,0 +1,20 @@
+import { CalldataBlock } from '../calldata_block';
+
+export class BlobCalldataBlock extends CalldataBlock {
+ private readonly _blob: Buffer;
+
+ constructor(name: string, signature: string, parentName: string, blob: Buffer) {
+ const headerSizeInBytes = 0;
+ const bodySizeInBytes = blob.byteLength;
+ super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes);
+ this._blob = blob;
+ }
+
+ public toBuffer(): Buffer {
+ return this._blob;
+ }
+
+ public getRawData(): Buffer {
+ return this._blob;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts
new file mode 100644
index 000000000..72d6a3173
--- /dev/null
+++ b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts
@@ -0,0 +1,61 @@
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { constants } from '../../utils/constants';
+
+import { CalldataBlock } from '../calldata_block';
+
+export class PointerCalldataBlock extends CalldataBlock {
+ public static readonly RAW_DATA_START = new Buffer('<');
+ public static readonly RAW_DATA_END = new Buffer('>');
+ private static readonly _DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32;
+ private static readonly _EMPTY_HEADER_SIZE = 0;
+ private readonly _parent: CalldataBlock;
+ private readonly _dependency: CalldataBlock;
+ private _aliasFor: CalldataBlock | undefined;
+
+ constructor(name: string, signature: string, parentName: string, dependency: CalldataBlock, parent: CalldataBlock) {
+ const headerSizeInBytes = PointerCalldataBlock._EMPTY_HEADER_SIZE;
+ const bodySizeInBytes = PointerCalldataBlock._DEPENDENT_PAYLOAD_SIZE_IN_BYTES;
+ super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes);
+ this._parent = parent;
+ this._dependency = dependency;
+ this._aliasFor = undefined;
+ }
+
+ public toBuffer(): Buffer {
+ const destinationOffset = !_.isUndefined(this._aliasFor)
+ ? this._aliasFor.getOffsetInBytes()
+ : this._dependency.getOffsetInBytes();
+ const parentOffset = this._parent.getOffsetInBytes();
+ const parentHeaderSize = this._parent.getHeaderSizeInBytes();
+ const pointer: number = destinationOffset - (parentOffset + parentHeaderSize);
+ const pointerHex = `0x${pointer.toString(constants.HEX_BASE)}`;
+ const pointerBuf = ethUtil.toBuffer(pointerHex);
+ const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, constants.EVM_WORD_WIDTH_IN_BYTES);
+ return pointerBufPadded;
+ }
+
+ public getDependency(): CalldataBlock {
+ return this._dependency;
+ }
+
+ public setAlias(block: CalldataBlock): void {
+ this._aliasFor = block;
+ this._setName(`${this.getName()} (alias for ${block.getName()})`);
+ }
+
+ public getAlias(): CalldataBlock | undefined {
+ return this._aliasFor;
+ }
+
+ public getRawData(): Buffer {
+ const dependencyRawData = this._dependency.getRawData();
+ const rawDataComponents: Buffer[] = [];
+ rawDataComponents.push(PointerCalldataBlock.RAW_DATA_START);
+ rawDataComponents.push(dependencyRawData);
+ rawDataComponents.push(PointerCalldataBlock.RAW_DATA_END);
+ const rawData = Buffer.concat(rawDataComponents);
+ return rawData;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/calldata/blocks/set.ts b/packages/utils/src/abi_encoder/calldata/blocks/set.ts
new file mode 100644
index 000000000..d1abc4986
--- /dev/null
+++ b/packages/utils/src/abi_encoder/calldata/blocks/set.ts
@@ -0,0 +1,47 @@
+import * as _ from 'lodash';
+
+import { CalldataBlock } from '../calldata_block';
+
+export class SetCalldataBlock extends CalldataBlock {
+ private _header: Buffer | undefined;
+ private _members: CalldataBlock[];
+
+ constructor(name: string, signature: string, parentName: string) {
+ super(name, signature, parentName, 0, 0);
+ this._members = [];
+ this._header = undefined;
+ }
+
+ public getRawData(): Buffer {
+ const rawDataComponents: Buffer[] = [];
+ if (!_.isUndefined(this._header)) {
+ rawDataComponents.push(this._header);
+ }
+ _.each(this._members, (member: CalldataBlock) => {
+ const memberBuffer = member.getRawData();
+ rawDataComponents.push(memberBuffer);
+ });
+ const rawData = Buffer.concat(rawDataComponents);
+ return rawData;
+ }
+
+ public setMembers(members: CalldataBlock[]): void {
+ this._members = members;
+ }
+
+ public setHeader(header: Buffer): void {
+ this._setHeaderSize(header.byteLength);
+ this._header = header;
+ }
+
+ public toBuffer(): Buffer {
+ if (!_.isUndefined(this._header)) {
+ return this._header;
+ }
+ return new Buffer('');
+ }
+
+ public getMembers(): CalldataBlock[] {
+ return this._members;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts
new file mode 100644
index 000000000..5f3eee94a
--- /dev/null
+++ b/packages/utils/src/abi_encoder/calldata/calldata.ts
@@ -0,0 +1,243 @@
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { constants } from '../utils/constants';
+import { EncodingRules } from '../utils/rules';
+
+import { PointerCalldataBlock } from './blocks/pointer';
+import { SetCalldataBlock } from './blocks/set';
+import { CalldataBlock } from './calldata_block';
+import { CalldataIterator, ReverseCalldataIterator } from './iterator';
+
+export class Calldata {
+ private readonly _rules: EncodingRules;
+ private _selector: string;
+ private _root: CalldataBlock | undefined;
+
+ public constructor(rules: EncodingRules) {
+ this._rules = rules;
+ this._selector = '';
+ this._root = undefined;
+ }
+ /**
+ * Sets the root calldata block. This block usually corresponds to a Method.
+ */
+ public setRoot(block: CalldataBlock): void {
+ this._root = block;
+ }
+ /**
+ * Sets the selector to be prepended onto the calldata.
+ * If the root block was created by a Method then a selector will likely be set.
+ */
+ public setSelector(selector: string): void {
+ if (!_.startsWith(selector, '0x')) {
+ throw new Error(`Expected selector to be hex. Missing prefix '0x'`);
+ } else if (selector.length !== constants.HEX_SELECTOR_LENGTH_IN_CHARS) {
+ throw new Error(`Invalid selector '${selector}'`);
+ }
+ this._selector = selector;
+ }
+ /**
+ * Iterates through the calldata blocks, starting from the root block, to construct calldata as a hex string.
+ * If the `optimize` flag is set then this calldata will be condensed, to save gas.
+ * If the `annotate` flag is set then this will return human-readable calldata.
+ * If the `annotate` flag is *not* set then this will return EVM-compatible calldata.
+ */
+ public toString(): string {
+ // Sanity check: root block must be set
+ if (_.isUndefined(this._root)) {
+ throw new Error('expected root');
+ }
+ // Optimize, if flag set
+ if (this._rules.optimize) {
+ this._optimize();
+ }
+ // Set offsets
+ const iterator = new CalldataIterator(this._root);
+ let offset = 0;
+ for (const block of iterator) {
+ block.setOffset(offset);
+ offset += block.getSizeInBytes();
+ }
+ // Generate hex string
+ const hexString = this._rules.annotate ? this._toHumanReadableCallData() : this._toEvmCompatibeCallDataHex();
+ return hexString;
+ }
+ /**
+ * There are three types of calldata blocks: Blob, Set and Pointer.
+ * Scenarios arise where distinct pointers resolve to identical values.
+ * We optimize by keeping only one such instance of the identical value, and redirecting all pointers here.
+ * We keep the last such duplicate value because pointers can only be positive (they cannot point backwards).
+ *
+ * Example #1:
+ * function f(string[], string[])
+ * f(["foo", "bar", "blitz"], ["foo", "bar", "blitz"])
+ * The array ["foo", "bar", "blitz"] will only be included in the calldata once.
+ *
+ * Example #2:
+ * function f(string[], string)
+ * f(["foo", "bar", "blitz"], "foo")
+ * The string "foo" will only be included in the calldata once.
+ *
+ * Example #3:
+ * function f((string, uint, bytes), string, uint, bytes)
+ * f(("foo", 5, "0x05"), "foo", 5, "0x05")
+ * The string "foo" and bytes "0x05" will only be included in the calldata once.
+ * The duplicate `uint 5` values cannot be optimized out because they are static values (no pointer points to them).
+ *
+ * @TODO #1:
+ * This optimization strategy handles blocks that are exact duplicates of one another.
+ * But what if some block is a combination of two other blocks? Or a subset of another block?
+ * This optimization problem is not much different from the current implemetation.
+ * Instead of tracking "observed" hashes, at each node we would simply do pattern-matching on the calldata.
+ * This strategy would be applied after assigning offsets to the tree, rather than before (as in this strategy).
+ * Note that one consequence of this strategy is pointers may resolve to offsets that are not word-aligned.
+ * This shouldn't be a problem but further investigation should be done.
+ *
+ * @TODO #2:
+ * To be done as a follow-up to @TODO #1.
+ * Since we optimize from the bottom-up, we could be affecting the outcome of a later potential optimization.
+ * For example, what if by removing one duplicate value we miss out on optimizing another block higher in the tree.
+ * To handle this case, at each node we can store a candidate optimization in a priority queue (sorted by calldata size).
+ * At the end of traversing the tree, the candidate at the front of the queue will be the most optimal output.
+ *
+ */
+ private _optimize(): void {
+ // Step 1/1 Create a reverse iterator (starts from the end of the calldata to the beginning)
+ if (_.isUndefined(this._root)) {
+ throw new Error('expected root');
+ }
+ const iterator = new ReverseCalldataIterator(this._root);
+ // Step 2/2 Iterate over each block, keeping track of which blocks have been seen and pruning redundant blocks.
+ const blocksByHash: { [key: string]: CalldataBlock } = {};
+ for (const block of iterator) {
+ // If a block is a pointer and its value has already been observed, then update
+ // the pointer to resolve to the existing value.
+ if (block instanceof PointerCalldataBlock) {
+ const dependencyBlockHashBuf = block.getDependency().computeHash();
+ const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf);
+ if (dependencyBlockHash in blocksByHash) {
+ const blockWithSameHash = blocksByHash[dependencyBlockHash];
+ if (blockWithSameHash !== block.getDependency()) {
+ block.setAlias(blockWithSameHash);
+ }
+ }
+ continue;
+ }
+ // This block has not been seen. Record its hash.
+ const blockHashBuf = block.computeHash();
+ const blockHash = ethUtil.bufferToHex(blockHashBuf);
+ if (!(blockHash in blocksByHash)) {
+ blocksByHash[blockHash] = block;
+ }
+ }
+ }
+ private _toEvmCompatibeCallDataHex(): string {
+ // Sanity check: must have a root block.
+ if (_.isUndefined(this._root)) {
+ throw new Error('expected root');
+ }
+ // Construct an array of buffers (one buffer for each block).
+ const selectorBuffer = ethUtil.toBuffer(this._selector);
+ const valueBufs: Buffer[] = [selectorBuffer];
+ const iterator = new CalldataIterator(this._root);
+ for (const block of iterator) {
+ valueBufs.push(block.toBuffer());
+ }
+ // Create hex from buffer array.
+ const combinedBuffers = Buffer.concat(valueBufs);
+ const hexValue = ethUtil.bufferToHex(combinedBuffers);
+ return hexValue;
+ }
+ /**
+ * Returns human-readable calldata.
+ *
+ * Example:
+ * simpleFunction(string[], string[])
+ * strings = ["Hello", "World"]
+ * simpleFunction(strings, strings)
+ *
+ * Output:
+ * 0xbb4f12e3
+ * ### simpleFunction
+ * 0x0 0000000000000000000000000000000000000000000000000000000000000040 ptr<array1> (alias for array2)
+ * 0x20 0000000000000000000000000000000000000000000000000000000000000040 ptr<array2>
+ *
+ * 0x40 0000000000000000000000000000000000000000000000000000000000000002 ### array2
+ * 0x60 0000000000000000000000000000000000000000000000000000000000000040 ptr<array2[0]>
+ * 0x80 0000000000000000000000000000000000000000000000000000000000000080 ptr<array2[1]>
+ * 0xa0 0000000000000000000000000000000000000000000000000000000000000005 array2[0]
+ * 0xc0 48656c6c6f000000000000000000000000000000000000000000000000000000
+ * 0xe0 0000000000000000000000000000000000000000000000000000000000000005 array2[1]
+ * 0x100 576f726c64000000000000000000000000000000000000000000000000000000
+ */
+ private _toHumanReadableCallData(): string {
+ // Sanity check: must have a root block.
+ if (_.isUndefined(this._root)) {
+ throw new Error('expected root');
+ }
+ // Constants for constructing annotated string
+ const offsetPadding = 10;
+ const valuePadding = 74;
+ const namePadding = 80;
+ const evmWordStartIndex = 0;
+ const emptySize = 0;
+ // Construct annotated calldata
+ let hexValue = `${this._selector}`;
+ let offset = 0;
+ const functionName: string = this._root.getName();
+ const iterator = new CalldataIterator(this._root);
+ for (const block of iterator) {
+ // Process each block 1 word at a time
+ const size = block.getSizeInBytes();
+ const name = block.getName();
+ const parentName = block.getParentName();
+ const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');
+ // Resulting line will be <offsetStr><valueStr><nameStr>
+ let offsetStr = '';
+ let valueStr = '';
+ let nameStr = '';
+ let lineStr = '';
+ if (size === emptySize) {
+ // This is a Set block with no header.
+ // For example, a tuple or an array with a defined length.
+ offsetStr = ' '.repeat(offsetPadding);
+ valueStr = ' '.repeat(valuePadding);
+ nameStr = `### ${prettyName.padEnd(namePadding)}`;
+ lineStr = `\n${offsetStr}${valueStr}${nameStr}`;
+ } else {
+ // This block has at least one word of value.
+ offsetStr = `0x${offset.toString(constants.HEX_BASE)}`.padEnd(offsetPadding);
+ valueStr = ethUtil
+ .stripHexPrefix(
+ ethUtil.bufferToHex(
+ block.toBuffer().slice(evmWordStartIndex, constants.EVM_WORD_WIDTH_IN_BYTES),
+ ),
+ )
+ .padEnd(valuePadding);
+ if (block instanceof SetCalldataBlock) {
+ nameStr = `### ${prettyName.padEnd(namePadding)}`;
+ lineStr = `\n${offsetStr}${valueStr}${nameStr}`;
+ } else {
+ nameStr = ` ${prettyName.padEnd(namePadding)}`;
+ lineStr = `${offsetStr}${valueStr}${nameStr}`;
+ }
+ }
+ // This block has a value that is more than 1 word.
+ for (let j = constants.EVM_WORD_WIDTH_IN_BYTES; j < size; j += constants.EVM_WORD_WIDTH_IN_BYTES) {
+ offsetStr = `0x${(offset + j).toString(constants.HEX_BASE)}`.padEnd(offsetPadding);
+ valueStr = ethUtil
+ .stripHexPrefix(
+ ethUtil.bufferToHex(block.toBuffer().slice(j, j + constants.EVM_WORD_WIDTH_IN_BYTES)),
+ )
+ .padEnd(valuePadding);
+ nameStr = ' '.repeat(namePadding);
+ lineStr = `${lineStr}\n${offsetStr}${valueStr}${nameStr}`;
+ }
+ // Append to hex value
+ hexValue = `${hexValue}\n${lineStr}`;
+ offset += size;
+ }
+ return hexValue;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/calldata/calldata_block.ts b/packages/utils/src/abi_encoder/calldata/calldata_block.ts
new file mode 100644
index 000000000..35bd994e5
--- /dev/null
+++ b/packages/utils/src/abi_encoder/calldata/calldata_block.ts
@@ -0,0 +1,77 @@
+import * as ethUtil from 'ethereumjs-util';
+
+export abstract class CalldataBlock {
+ private readonly _signature: string;
+ private readonly _parentName: string;
+ private _name: string;
+ private _offsetInBytes: number;
+ private _headerSizeInBytes: number;
+ private _bodySizeInBytes: number;
+
+ constructor(
+ name: string,
+ signature: string,
+ parentName: string,
+ headerSizeInBytes: number,
+ bodySizeInBytes: number,
+ ) {
+ this._name = name;
+ this._signature = signature;
+ this._parentName = parentName;
+ this._offsetInBytes = 0;
+ this._headerSizeInBytes = headerSizeInBytes;
+ this._bodySizeInBytes = bodySizeInBytes;
+ }
+
+ protected _setHeaderSize(headerSizeInBytes: number): void {
+ this._headerSizeInBytes = headerSizeInBytes;
+ }
+
+ protected _setBodySize(bodySizeInBytes: number): void {
+ this._bodySizeInBytes = bodySizeInBytes;
+ }
+
+ protected _setName(name: string): void {
+ this._name = name;
+ }
+
+ public getName(): string {
+ return this._name;
+ }
+
+ public getParentName(): string {
+ return this._parentName;
+ }
+
+ public getSignature(): string {
+ return this._signature;
+ }
+ public getHeaderSizeInBytes(): number {
+ return this._headerSizeInBytes;
+ }
+
+ public getBodySizeInBytes(): number {
+ return this._bodySizeInBytes;
+ }
+
+ public getSizeInBytes(): number {
+ return this.getHeaderSizeInBytes() + this.getBodySizeInBytes();
+ }
+
+ public getOffsetInBytes(): number {
+ return this._offsetInBytes;
+ }
+
+ public setOffset(offsetInBytes: number): void {
+ this._offsetInBytes = offsetInBytes;
+ }
+
+ public computeHash(): Buffer {
+ const rawData = this.getRawData();
+ const hash = ethUtil.sha3(rawData);
+ return hash;
+ }
+
+ public abstract toBuffer(): Buffer;
+ public abstract getRawData(): Buffer;
+}
diff --git a/packages/utils/src/abi_encoder/calldata/iterator.ts b/packages/utils/src/abi_encoder/calldata/iterator.ts
new file mode 100644
index 000000000..333b32b4f
--- /dev/null
+++ b/packages/utils/src/abi_encoder/calldata/iterator.ts
@@ -0,0 +1,114 @@
+/* tslint:disable max-classes-per-file */
+import * as _ from 'lodash';
+
+import { Queue } from '../utils/queue';
+
+import { BlobCalldataBlock } from './blocks/blob';
+import { PointerCalldataBlock } from './blocks/pointer';
+import { SetCalldataBlock } from './blocks/set';
+import { CalldataBlock } from './calldata_block';
+
+/**
+ * Iterator class for Calldata Blocks. Blocks follows the order
+ * they should be put into calldata that is passed to he EVM.
+ *
+ * Example #1:
+ * Let root = Set {
+ * Blob{} A,
+ * Pointer {
+ * Blob{} a
+ * } B,
+ * Blob{} C
+ * }
+ * It will iterate as follows: [A, B, C, B.a]
+ *
+ * Example #2:
+ * Let root = Set {
+ * Blob{} A,
+ * Pointer {
+ * Blob{} a
+ * Pointer {
+ * Blob{} b
+ * }
+ * } B,
+ * Pointer {
+ * Blob{} c
+ * } C
+ * }
+ * It will iterate as follows: [A, B, C, B.a, B.b, C.c]
+ */
+abstract class BaseIterator implements Iterable<CalldataBlock> {
+ protected readonly _root: CalldataBlock;
+ protected readonly _queue: Queue<CalldataBlock>;
+
+ private static _createQueue(block: CalldataBlock): Queue<CalldataBlock> {
+ const queue = new Queue<CalldataBlock>();
+ // Base case
+ if (!(block instanceof SetCalldataBlock)) {
+ queue.pushBack(block);
+ return queue;
+ }
+ // This is a set; add members
+ const set = block;
+ _.eachRight(set.getMembers(), (member: CalldataBlock) => {
+ queue.mergeFront(BaseIterator._createQueue(member));
+ });
+ // Add children
+ _.each(set.getMembers(), (member: CalldataBlock) => {
+ // Traverse child if it is a unique pointer.
+ // A pointer that is an alias for another pointer is ignored.
+ if (member instanceof PointerCalldataBlock && _.isUndefined(member.getAlias())) {
+ const dependency = member.getDependency();
+ queue.mergeBack(BaseIterator._createQueue(dependency));
+ }
+ });
+ // Put set block at the front of the queue
+ queue.pushFront(set);
+ return queue;
+ }
+
+ public constructor(root: CalldataBlock) {
+ this._root = root;
+ this._queue = BaseIterator._createQueue(root);
+ }
+
+ public [Symbol.iterator](): { next: () => IteratorResult<CalldataBlock> } {
+ return {
+ next: () => {
+ const nextBlock = this.nextBlock();
+ if (!_.isUndefined(nextBlock)) {
+ return {
+ value: nextBlock,
+ done: false,
+ };
+ }
+ return {
+ done: true,
+ value: new BlobCalldataBlock('', '', '', new Buffer('')),
+ };
+ },
+ };
+ }
+
+ public abstract nextBlock(): CalldataBlock | undefined;
+}
+
+export class CalldataIterator extends BaseIterator {
+ public constructor(root: CalldataBlock) {
+ super(root);
+ }
+
+ public nextBlock(): CalldataBlock | undefined {
+ return this._queue.popFront();
+ }
+}
+
+export class ReverseCalldataIterator extends BaseIterator {
+ public constructor(root: CalldataBlock) {
+ super(root);
+ }
+
+ public nextBlock(): CalldataBlock | undefined {
+ return this._queue.popBack();
+ }
+}
diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts
new file mode 100644
index 000000000..189841989
--- /dev/null
+++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts
@@ -0,0 +1,82 @@
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { constants } from '../utils/constants';
+import { Queue } from '../utils/queue';
+
+export class RawCalldata {
+ private static readonly _INITIAL_OFFSET = 0;
+ private readonly _value: Buffer;
+ private readonly _selector: string;
+ private readonly _scopes: Queue<number>;
+ private _offset: number;
+
+ public constructor(value: string | Buffer, hasSelector: boolean = true) {
+ // Sanity check
+ if (typeof value === 'string' && !_.startsWith(value, '0x')) {
+ throw new Error(`Expected raw calldata to start with '0x'`);
+ }
+ // Construct initial values
+ this._value = ethUtil.toBuffer(value);
+ this._selector = '0x';
+ this._scopes = new Queue<number>();
+ this._scopes.pushBack(RawCalldata._INITIAL_OFFSET);
+ this._offset = RawCalldata._INITIAL_OFFSET;
+ // If there's a selector then slice it
+ if (hasSelector) {
+ const selectorBuf = this._value.slice(constants.HEX_SELECTOR_LENGTH_IN_BYTES);
+ this._value = this._value.slice(constants.HEX_SELECTOR_LENGTH_IN_BYTES);
+ this._selector = ethUtil.bufferToHex(selectorBuf);
+ }
+ }
+
+ public popBytes(lengthInBytes: number): Buffer {
+ const value = this._value.slice(this._offset, this._offset + lengthInBytes);
+ this.setOffset(this._offset + lengthInBytes);
+ return value;
+ }
+
+ public popWord(): Buffer {
+ const wordInBytes = 32;
+ return this.popBytes(wordInBytes);
+ }
+
+ public popWords(length: number): Buffer {
+ const wordInBytes = 32;
+ return this.popBytes(length * wordInBytes);
+ }
+
+ public readBytes(from: number, to: number): Buffer {
+ const value = this._value.slice(from, to);
+ return value;
+ }
+
+ public setOffset(offsetInBytes: number): void {
+ this._offset = offsetInBytes;
+ }
+
+ public startScope(): void {
+ this._scopes.pushFront(this._offset);
+ }
+
+ public endScope(): void {
+ this._scopes.popFront();
+ }
+
+ public getOffset(): number {
+ return this._offset;
+ }
+
+ public toAbsoluteOffset(relativeOffset: number): number {
+ const scopeOffset = this._scopes.peekFront();
+ if (_.isUndefined(scopeOffset)) {
+ throw new Error(`Tried to access undefined scope.`);
+ }
+ const absoluteOffset = relativeOffset + scopeOffset;
+ return absoluteOffset;
+ }
+
+ public getSelector(): string {
+ return this._selector;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts
new file mode 100644
index 000000000..4cc124e0a
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts
@@ -0,0 +1,132 @@
+/* tslint:disable max-classes-per-file */
+import { DataItem, MethodAbi } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { DataType } from './abstract_data_types/data_type';
+import { DataTypeFactory } from './abstract_data_types/interfaces';
+import { AddressDataType } from './evm_data_types/address';
+import { ArrayDataType } from './evm_data_types/array';
+import { BoolDataType } from './evm_data_types/bool';
+import { DynamicBytesDataType } from './evm_data_types/dynamic_bytes';
+import { IntDataType } from './evm_data_types/int';
+import { MethodDataType } from './evm_data_types/method';
+import { PointerDataType } from './evm_data_types/pointer';
+import { StaticBytesDataType } from './evm_data_types/static_bytes';
+import { StringDataType } from './evm_data_types/string';
+import { TupleDataType } from './evm_data_types/tuple';
+import { UIntDataType } from './evm_data_types/uint';
+
+export class Address extends AddressDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class Bool extends BoolDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class Int extends IntDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class UInt extends UIntDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class StaticBytes extends StaticBytesDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class DynamicBytes extends DynamicBytesDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class String extends StringDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class Pointer extends PointerDataType {
+ public constructor(destDataType: DataType, parentDataType: DataType) {
+ super(destDataType, parentDataType, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class Tuple extends TupleDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class Array extends ArrayDataType {
+ public constructor(dataItem: DataItem) {
+ super(dataItem, EvmDataTypeFactory.getInstance());
+ }
+}
+
+export class Method extends MethodDataType {
+ public constructor(abi: MethodAbi) {
+ super(abi, EvmDataTypeFactory.getInstance());
+ }
+}
+
+/* tslint:disable no-construct */
+export class EvmDataTypeFactory implements DataTypeFactory {
+ private static _instance: DataTypeFactory;
+
+ public static getInstance(): DataTypeFactory {
+ if (!EvmDataTypeFactory._instance) {
+ EvmDataTypeFactory._instance = new EvmDataTypeFactory();
+ }
+ return EvmDataTypeFactory._instance;
+ }
+
+ /* tslint:disable prefer-function-over-method */
+ public create(dataItem: DataItem, parentDataType?: DataType): DataType {
+ // Create data type
+ let dataType: undefined | DataType;
+ if (Array.matchType(dataItem.type)) {
+ dataType = new Array(dataItem);
+ } else if (Address.matchType(dataItem.type)) {
+ dataType = new Address(dataItem);
+ } else if (Bool.matchType(dataItem.type)) {
+ dataType = new Bool(dataItem);
+ } else if (Int.matchType(dataItem.type)) {
+ dataType = new Int(dataItem);
+ } else if (UInt.matchType(dataItem.type)) {
+ dataType = new UInt(dataItem);
+ } else if (StaticBytes.matchType(dataItem.type)) {
+ dataType = new StaticBytes(dataItem);
+ } else if (Tuple.matchType(dataItem.type)) {
+ dataType = new Tuple(dataItem);
+ } else if (DynamicBytes.matchType(dataItem.type)) {
+ dataType = new DynamicBytes(dataItem);
+ } else if (String.matchType(dataItem.type)) {
+ dataType = new String(dataItem);
+ }
+ // @TODO: DataTypeement Fixed/UFixed types
+ if (_.isUndefined(dataType)) {
+ throw new Error(`Unrecognized data type: '${dataItem.type}'`);
+ } else if (!_.isUndefined(parentDataType) && !dataType.isStatic()) {
+ const pointerToDataType = new Pointer(dataType, parentDataType);
+ return pointerToDataType;
+ }
+ return dataType;
+ }
+ /* tslint:enable prefer-function-over-method */
+
+ private constructor() {}
+}
+/* tslint:enable no-construct */
diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts
new file mode 100644
index 000000000..88846b1fa
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts
@@ -0,0 +1,49 @@
+import { DataItem, SolidityTypes } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractBlobDataType } from '../abstract_data_types/types/blob';
+import { RawCalldata } from '../calldata/raw_calldata';
+import { constants } from '../utils/constants';
+
+export class AddressDataType extends AbstractBlobDataType {
+ private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
+ private static readonly _ADDRESS_SIZE_IN_BYTES = 20;
+ private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = constants.EVM_WORD_WIDTH_IN_BYTES -
+ AddressDataType._ADDRESS_SIZE_IN_BYTES;
+
+ public static matchType(type: string): boolean {
+ return type === SolidityTypes.Address;
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ super(dataItem, dataTypeFactory, AddressDataType._SIZE_KNOWN_AT_COMPILE_TIME);
+ if (!AddressDataType.matchType(dataItem.type)) {
+ throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`);
+ }
+ }
+
+ // Disable prefer-function-over-method for inherited abstract methods.
+ /* tslint:disable prefer-function-over-method */
+ public encodeValue(value: string): Buffer {
+ if (!ethUtil.isValidAddress(value)) {
+ throw new Error(`Invalid address: '${value}'`);
+ }
+ const valueBuf = ethUtil.toBuffer(value);
+ const encodedValueBuf = ethUtil.setLengthLeft(valueBuf, constants.EVM_WORD_WIDTH_IN_BYTES);
+ return encodedValueBuf;
+ }
+
+ public decodeValue(calldata: RawCalldata): string {
+ const valueBufPadded = calldata.popWord();
+ const valueBuf = valueBufPadded.slice(AddressDataType._DECODED_ADDRESS_OFFSET_IN_BYTES);
+ const value = ethUtil.bufferToHex(valueBuf);
+ return value;
+ }
+
+ public getSignature(): string {
+ return SolidityTypes.Address;
+ }
+ /* tslint:enable prefer-function-over-method */
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts
new file mode 100644
index 000000000..7595cb667
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts
@@ -0,0 +1,64 @@
+import { DataItem } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractSetDataType } from '../abstract_data_types/types/set';
+import { constants } from '../utils/constants';
+
+export class ArrayDataType extends AbstractSetDataType {
+ private static readonly _MATCHER = RegExp('^(.+)\\[([0-9]*)\\]$');
+ private readonly _arraySignature: string;
+ private readonly _elementType: string;
+
+ public static matchType(type: string): boolean {
+ return ArrayDataType._MATCHER.test(type);
+ }
+
+ private static _decodeElementTypeAndLengthFromType(type: string): [string, undefined | number] {
+ const matches = ArrayDataType._MATCHER.exec(type);
+ if (_.isNull(matches) || matches.length !== 3) {
+ throw new Error(`Could not parse array: ${type}`);
+ } else if (_.isUndefined(matches[1])) {
+ throw new Error(`Could not parse array type: ${type}`);
+ } else if (_.isUndefined(matches[2])) {
+ throw new Error(`Could not parse array length: ${type}`);
+ }
+ const arrayElementType = matches[1];
+ const arrayLength = _.isEmpty(matches[2]) ? undefined : parseInt(matches[2], constants.DEC_BASE);
+ return [arrayElementType, arrayLength];
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ // Construct parent
+ const isArray = true;
+ const [arrayElementType, arrayLength] = ArrayDataType._decodeElementTypeAndLengthFromType(dataItem.type);
+ super(dataItem, dataTypeFactory, isArray, arrayLength, arrayElementType);
+ // Set array properties
+ this._elementType = arrayElementType;
+ this._arraySignature = this._computeSignature();
+ }
+
+ public getSignature(): string {
+ return this._arraySignature;
+ }
+
+ private _computeSignature(): string {
+ // Compute signature for a single array element
+ const elementDataItem: DataItem = {
+ type: this._elementType,
+ name: 'N/A',
+ };
+ const elementComponents = this.getDataItem().components;
+ if (!_.isUndefined(elementComponents)) {
+ elementDataItem.components = elementComponents;
+ }
+ const elementDataType = this.getFactory().create(elementDataItem);
+ const elementSignature = elementDataType.getSignature();
+ // Construct signature for array of type `element`
+ if (_.isUndefined(this._arrayLength)) {
+ return `${elementSignature}[]`;
+ } else {
+ return `${elementSignature}[${this._arrayLength}]`;
+ }
+ }
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts
new file mode 100644
index 000000000..d713d5a94
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts
@@ -0,0 +1,53 @@
+import { DataItem, SolidityTypes } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { BigNumber } from '../../configured_bignumber';
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractBlobDataType } from '../abstract_data_types/types/blob';
+import { RawCalldata } from '../calldata/raw_calldata';
+import { constants } from '../utils/constants';
+
+export class BoolDataType extends AbstractBlobDataType {
+ private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
+
+ public static matchType(type: string): boolean {
+ return type === SolidityTypes.Bool;
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ super(dataItem, dataTypeFactory, BoolDataType._SIZE_KNOWN_AT_COMPILE_TIME);
+ if (!BoolDataType.matchType(dataItem.type)) {
+ throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`);
+ }
+ }
+
+ // Disable prefer-function-over-method for inherited abstract methods.
+ /* tslint:disable prefer-function-over-method */
+ public encodeValue(value: boolean): Buffer {
+ const encodedValue = value ? '0x1' : '0x0';
+ const encodedValueBuf = ethUtil.setLengthLeft(
+ ethUtil.toBuffer(encodedValue),
+ constants.EVM_WORD_WIDTH_IN_BYTES,
+ );
+ return encodedValueBuf;
+ }
+
+ public decodeValue(calldata: RawCalldata): boolean {
+ const valueBuf = calldata.popWord();
+ const valueHex = ethUtil.bufferToHex(valueBuf);
+ const valueNumber = new BigNumber(valueHex, constants.HEX_BASE);
+ if (!(valueNumber.equals(0) || valueNumber.equals(1))) {
+ throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`);
+ }
+ /* tslint:disable boolean-naming */
+ const value: boolean = !valueNumber.equals(0);
+ /* tslint:enable boolean-naming */
+ return value;
+ }
+
+ public getSignature(): string {
+ return SolidityTypes.Bool;
+ }
+ /* tslint:enable prefer-function-over-method */
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts
new file mode 100644
index 000000000..5277efd6c
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts
@@ -0,0 +1,72 @@
+import { DataItem, SolidityTypes } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractBlobDataType } from '../abstract_data_types/types/blob';
+import { RawCalldata } from '../calldata/raw_calldata';
+import { constants } from '../utils/constants';
+
+export class DynamicBytesDataType extends AbstractBlobDataType {
+ private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false;
+
+ public static matchType(type: string): boolean {
+ return type === SolidityTypes.Bytes;
+ }
+
+ private static _sanityCheckValue(value: string | Buffer): void {
+ if (typeof value !== 'string') {
+ return;
+ }
+ if (!_.startsWith(value, '0x')) {
+ throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`);
+ } else if (value.length % 2 !== 0) {
+ throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`);
+ }
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ super(dataItem, dataTypeFactory, DynamicBytesDataType._SIZE_KNOWN_AT_COMPILE_TIME);
+ if (!DynamicBytesDataType.matchType(dataItem.type)) {
+ throw new Error(`Tried to instantiate Dynamic Bytes with bad input: ${dataItem}`);
+ }
+ }
+
+ // Disable prefer-function-over-method for inherited abstract methods.
+ /* tslint:disable prefer-function-over-method */
+ public encodeValue(value: string | Buffer): Buffer {
+ // Encoded value is of the form: <length><value>, with each field padded to be word-aligned.
+ // 1/3 Construct the length
+ const valueBuf = ethUtil.toBuffer(value);
+ const wordsToStoreValuePadded = Math.ceil(valueBuf.byteLength / constants.EVM_WORD_WIDTH_IN_BYTES);
+ const bytesToStoreValuePadded = wordsToStoreValuePadded * constants.EVM_WORD_WIDTH_IN_BYTES;
+ const lengthBuf = ethUtil.toBuffer(valueBuf.byteLength);
+ const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, constants.EVM_WORD_WIDTH_IN_BYTES);
+ // 2/3 Construct the value
+ DynamicBytesDataType._sanityCheckValue(value);
+ const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded);
+ // 3/3 Combine length and value
+ const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]);
+ return encodedValue;
+ }
+
+ public decodeValue(calldata: RawCalldata): string {
+ // Encoded value is of the form: <length><value>, with each field padded to be word-aligned.
+ // 1/2 Decode length
+ const lengthBuf = calldata.popWord();
+ const lengthHex = ethUtil.bufferToHex(lengthBuf);
+ const length = parseInt(lengthHex, constants.HEX_BASE);
+ // 2/2 Decode value
+ const wordsToStoreValuePadded = Math.ceil(length / constants.EVM_WORD_WIDTH_IN_BYTES);
+ const valueBufPadded = calldata.popWords(wordsToStoreValuePadded);
+ const valueBuf = valueBufPadded.slice(0, length);
+ const value = ethUtil.bufferToHex(valueBuf);
+ DynamicBytesDataType._sanityCheckValue(value);
+ return value;
+ }
+
+ public getSignature(): string {
+ return SolidityTypes.Bytes;
+ }
+ /* tslint:enable prefer-function-over-method */
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts
new file mode 100644
index 000000000..f1dcf5ea1
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts
@@ -0,0 +1,59 @@
+import { DataItem, SolidityTypes } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { BigNumber } from '../../configured_bignumber';
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractBlobDataType } from '../abstract_data_types/types/blob';
+import { RawCalldata } from '../calldata/raw_calldata';
+import { constants } from '../utils/constants';
+import * as EncoderMath from '../utils/math';
+
+export class IntDataType extends AbstractBlobDataType {
+ private static readonly _MATCHER = RegExp(
+ '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$',
+ );
+ private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
+ private static readonly _MAX_WIDTH: number = 256;
+ private static readonly _DEFAULT_WIDTH: number = IntDataType._MAX_WIDTH;
+ private readonly _width: number;
+ private readonly _minValue: BigNumber;
+ private readonly _maxValue: BigNumber;
+
+ public static matchType(type: string): boolean {
+ return IntDataType._MATCHER.test(type);
+ }
+
+ private static _decodeWidthFromType(type: string): number {
+ const matches = IntDataType._MATCHER.exec(type);
+ const width =
+ !_.isNull(matches) && matches.length === 2 && !_.isUndefined(matches[1])
+ ? parseInt(matches[1], constants.DEC_BASE)
+ : IntDataType._DEFAULT_WIDTH;
+ return width;
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ super(dataItem, dataTypeFactory, IntDataType._SIZE_KNOWN_AT_COMPILE_TIME);
+ if (!IntDataType.matchType(dataItem.type)) {
+ throw new Error(`Tried to instantiate Int with bad input: ${dataItem}`);
+ }
+ this._width = IntDataType._decodeWidthFromType(dataItem.type);
+ this._minValue = new BigNumber(2).toPower(this._width - 1).times(-1);
+ this._maxValue = new BigNumber(2).toPower(this._width - 1).sub(1);
+ }
+
+ public encodeValue(value: BigNumber | string | number): Buffer {
+ const encodedValue = EncoderMath.safeEncodeNumericValue(value, this._minValue, this._maxValue);
+ return encodedValue;
+ }
+
+ public decodeValue(calldata: RawCalldata): BigNumber {
+ const valueBuf = calldata.popWord();
+ const value = EncoderMath.safeDecodeNumericValue(valueBuf, this._minValue, this._maxValue);
+ return value;
+ }
+
+ public getSignature(): string {
+ return `${SolidityTypes.Int}${this._width}`;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts
new file mode 100644
index 000000000..b1cd1377f
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts
@@ -0,0 +1,72 @@
+import { DataItem, MethodAbi } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { DataType } from '../abstract_data_types/data_type';
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractSetDataType } from '../abstract_data_types/types/set';
+import { constants } from '../utils/constants';
+import { DecodingRules, EncodingRules } from '../utils/rules';
+
+import { TupleDataType } from './tuple';
+
+export class MethodDataType extends AbstractSetDataType {
+ private readonly _methodSignature: string;
+ private readonly _methodSelector: string;
+ private readonly _returnDataType: DataType;
+
+ public constructor(abi: MethodAbi, dataTypeFactory: DataTypeFactory) {
+ const methodDataItem = { type: 'method', name: abi.name, components: abi.inputs };
+ super(methodDataItem, dataTypeFactory);
+ this._methodSignature = this._computeSignature();
+ this._methodSelector = this._computeSelector();
+ const returnDataItem: DataItem = { type: 'tuple', name: abi.name, components: abi.outputs };
+ this._returnDataType = new TupleDataType(returnDataItem, this.getFactory());
+ }
+
+ public encode(value: any, rules?: EncodingRules): string {
+ const calldata = super.encode(value, rules, this._methodSelector);
+ return calldata;
+ }
+
+ public decode(calldata: string, rules?: DecodingRules): any[] | object {
+ const value = super.decode(calldata, rules, this._methodSelector);
+ return value;
+ }
+
+ public encodeReturnValues(value: any, rules?: EncodingRules): string {
+ const returnData = this._returnDataType.encode(value, rules);
+ return returnData;
+ }
+
+ public decodeReturnValues(returndata: string, rules?: DecodingRules): any {
+ const returnValues = this._returnDataType.decode(returndata, rules);
+ return returnValues;
+ }
+
+ public getSignature(): string {
+ return this._methodSignature;
+ }
+
+ public getSelector(): string {
+ return this._methodSelector;
+ }
+
+ private _computeSignature(): string {
+ const memberSignature = this._computeSignatureOfMembers();
+ const methodSignature = `${this.getDataItem().name}${memberSignature}`;
+ return methodSignature;
+ }
+
+ private _computeSelector(): string {
+ const signature = this._computeSignature();
+ const selector = ethUtil.bufferToHex(
+ ethUtil.toBuffer(
+ ethUtil
+ .sha3(signature)
+ .slice(constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, constants.HEX_SELECTOR_LENGTH_IN_BYTES),
+ ),
+ );
+ return selector;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts
new file mode 100644
index 000000000..389e75927
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts
@@ -0,0 +1,17 @@
+import { DataItem } from 'ethereum-types';
+
+import { DataType } from '../abstract_data_types/data_type';
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractPointerDataType } from '../abstract_data_types/types/pointer';
+
+export class PointerDataType extends AbstractPointerDataType {
+ constructor(destDataType: DataType, parentDataType: DataType, dataTypeFactory: DataTypeFactory) {
+ const destDataItem = destDataType.getDataItem();
+ const dataItem: DataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` };
+ super(dataItem, dataTypeFactory, destDataType, parentDataType);
+ }
+
+ public getSignature(): string {
+ return this._destination.getSignature();
+ }
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
new file mode 100644
index 000000000..2e371c505
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
@@ -0,0 +1,78 @@
+import { DataItem, SolidityTypes } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractBlobDataType } from '../abstract_data_types/types/blob';
+import { RawCalldata } from '../calldata/raw_calldata';
+import { constants } from '../utils/constants';
+
+export class StaticBytesDataType extends AbstractBlobDataType {
+ private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
+ private static readonly _MATCHER = RegExp(
+ '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$',
+ );
+ private static readonly _DEFAULT_WIDTH = 1;
+ private readonly _width: number;
+
+ public static matchType(type: string): boolean {
+ return StaticBytesDataType._MATCHER.test(type);
+ }
+
+ private static _decodeWidthFromType(type: string): number {
+ const matches = StaticBytesDataType._MATCHER.exec(type);
+ const width =
+ !_.isNull(matches) && matches.length === 3 && !_.isUndefined(matches[2])
+ ? parseInt(matches[2], constants.DEC_BASE)
+ : StaticBytesDataType._DEFAULT_WIDTH;
+ return width;
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ super(dataItem, dataTypeFactory, StaticBytesDataType._SIZE_KNOWN_AT_COMPILE_TIME);
+ if (!StaticBytesDataType.matchType(dataItem.type)) {
+ throw new Error(`Tried to instantiate Static Bytes with bad input: ${dataItem}`);
+ }
+ this._width = StaticBytesDataType._decodeWidthFromType(dataItem.type);
+ }
+
+ public getSignature(): string {
+ // Note that `byte` reduces to `bytes1`
+ return `${SolidityTypes.Bytes}${this._width}`;
+ }
+
+ public encodeValue(value: string | Buffer): Buffer {
+ // 1/2 Convert value into a buffer and do bounds checking
+ this._sanityCheckValue(value);
+ const valueBuf = ethUtil.toBuffer(value);
+ // 2/2 Store value as hex
+ const valuePadded = ethUtil.setLengthRight(valueBuf, constants.EVM_WORD_WIDTH_IN_BYTES);
+ return valuePadded;
+ }
+
+ public decodeValue(calldata: RawCalldata): string {
+ const valueBufPadded = calldata.popWord();
+ const valueBuf = valueBufPadded.slice(0, this._width);
+ const value = ethUtil.bufferToHex(valueBuf);
+ this._sanityCheckValue(value);
+ return value;
+ }
+
+ private _sanityCheckValue(value: string | Buffer): void {
+ if (typeof value === 'string') {
+ if (!_.startsWith(value, '0x')) {
+ throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`);
+ } else if (value.length % 2 !== 0) {
+ throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`);
+ }
+ }
+ const valueBuf = ethUtil.toBuffer(value);
+ if (valueBuf.byteLength > this._width) {
+ throw new Error(
+ `Tried to assign ${value} (${
+ valueBuf.byteLength
+ } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`,
+ );
+ }
+ }
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts
new file mode 100644
index 000000000..91a72ad3f
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts
@@ -0,0 +1,59 @@
+import { DataItem, SolidityTypes } from 'ethereum-types';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractBlobDataType } from '../abstract_data_types/types/blob';
+import { RawCalldata } from '../calldata/raw_calldata';
+import { constants } from '../utils/constants';
+
+export class StringDataType extends AbstractBlobDataType {
+ private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false;
+
+ public static matchType(type: string): boolean {
+ return type === SolidityTypes.String;
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ super(dataItem, dataTypeFactory, StringDataType._SIZE_KNOWN_AT_COMPILE_TIME);
+ if (!StringDataType.matchType(dataItem.type)) {
+ throw new Error(`Tried to instantiate String with bad input: ${dataItem}`);
+ }
+ }
+
+ // Disable prefer-function-over-method for inherited abstract methods.
+ /* tslint:disable prefer-function-over-method */
+ public encodeValue(value: string): Buffer {
+ // Encoded value is of the form: <length><value>, with each field padded to be word-aligned.
+ // 1/3 Construct the length
+ const wordsToStoreValuePadded = Math.ceil(value.length / constants.EVM_WORD_WIDTH_IN_BYTES);
+ const bytesToStoreValuePadded = wordsToStoreValuePadded * constants.EVM_WORD_WIDTH_IN_BYTES;
+ const lengthBuf = ethUtil.toBuffer(value.length);
+ const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, constants.EVM_WORD_WIDTH_IN_BYTES);
+ // 2/3 Construct the value
+ const valueBuf = new Buffer(value);
+ const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded);
+ // 3/3 Combine length and value
+ const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]);
+ return encodedValue;
+ }
+
+ public decodeValue(calldata: RawCalldata): string {
+ // Encoded value is of the form: <length><value>, with each field padded to be word-aligned.
+ // 1/2 Decode length
+ const lengthBufPadded = calldata.popWord();
+ const lengthHexPadded = ethUtil.bufferToHex(lengthBufPadded);
+ const length = parseInt(lengthHexPadded, constants.HEX_BASE);
+ // 2/2 Decode value
+ const wordsToStoreValuePadded = Math.ceil(length / constants.EVM_WORD_WIDTH_IN_BYTES);
+ const valueBufPadded = calldata.popWords(wordsToStoreValuePadded);
+ const valueBuf = valueBufPadded.slice(0, length);
+ const value = valueBuf.toString('ascii');
+ return value;
+ }
+
+ public getSignature(): string {
+ return SolidityTypes.String;
+ }
+ /* tslint:enable prefer-function-over-method */
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts
new file mode 100644
index 000000000..31593c882
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts
@@ -0,0 +1,24 @@
+import { DataItem, SolidityTypes } from 'ethereum-types';
+
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractSetDataType } from '../abstract_data_types/types/set';
+
+export class TupleDataType extends AbstractSetDataType {
+ private readonly _signature: string;
+
+ public static matchType(type: string): boolean {
+ return type === SolidityTypes.Tuple;
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ super(dataItem, dataTypeFactory);
+ if (!TupleDataType.matchType(dataItem.type)) {
+ throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`);
+ }
+ this._signature = this._computeSignatureOfMembers();
+ }
+
+ public getSignature(): string {
+ return this._signature;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts
new file mode 100644
index 000000000..5180f0cf3
--- /dev/null
+++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts
@@ -0,0 +1,58 @@
+import { DataItem, SolidityTypes } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { BigNumber } from '../../configured_bignumber';
+import { DataTypeFactory } from '../abstract_data_types/interfaces';
+import { AbstractBlobDataType } from '../abstract_data_types/types/blob';
+import { RawCalldata } from '../calldata/raw_calldata';
+import { constants } from '../utils/constants';
+import * as EncoderMath from '../utils/math';
+
+export class UIntDataType extends AbstractBlobDataType {
+ private static readonly _MATCHER = RegExp(
+ '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$',
+ );
+ private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
+ private static readonly _MAX_WIDTH: number = 256;
+ private static readonly _DEFAULT_WIDTH: number = UIntDataType._MAX_WIDTH;
+ private static readonly _MIN_VALUE = new BigNumber(0);
+ private readonly _width: number;
+ private readonly _maxValue: BigNumber;
+
+ public static matchType(type: string): boolean {
+ return UIntDataType._MATCHER.test(type);
+ }
+
+ private static _decodeWidthFromType(type: string): number {
+ const matches = UIntDataType._MATCHER.exec(type);
+ const width =
+ !_.isNull(matches) && matches.length === 2 && !_.isUndefined(matches[1])
+ ? parseInt(matches[1], constants.DEC_BASE)
+ : UIntDataType._DEFAULT_WIDTH;
+ return width;
+ }
+
+ public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) {
+ super(dataItem, dataTypeFactory, UIntDataType._SIZE_KNOWN_AT_COMPILE_TIME);
+ if (!UIntDataType.matchType(dataItem.type)) {
+ throw new Error(`Tried to instantiate UInt with bad input: ${dataItem}`);
+ }
+ this._width = UIntDataType._decodeWidthFromType(dataItem.type);
+ this._maxValue = new BigNumber(2).toPower(this._width).sub(1);
+ }
+
+ public encodeValue(value: BigNumber | string | number): Buffer {
+ const encodedValue = EncoderMath.safeEncodeNumericValue(value, UIntDataType._MIN_VALUE, this._maxValue);
+ return encodedValue;
+ }
+
+ public decodeValue(calldata: RawCalldata): BigNumber {
+ const valueBuf = calldata.popWord();
+ const value = EncoderMath.safeDecodeNumericValue(valueBuf, UIntDataType._MIN_VALUE, this._maxValue);
+ return value;
+ }
+
+ public getSignature(): string {
+ return `${SolidityTypes.Uint}${this._width}`;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/index.ts b/packages/utils/src/abi_encoder/index.ts
new file mode 100644
index 000000000..baf844ac6
--- /dev/null
+++ b/packages/utils/src/abi_encoder/index.ts
@@ -0,0 +1,14 @@
+export { EncodingRules, DecodingRules } from './utils/rules';
+export {
+ Address,
+ Array,
+ Bool,
+ DynamicBytes,
+ Int,
+ Method,
+ Pointer,
+ StaticBytes,
+ String,
+ Tuple,
+ UInt,
+} from './evm_data_type_factory';
diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts
new file mode 100644
index 000000000..2f43ba04d
--- /dev/null
+++ b/packages/utils/src/abi_encoder/utils/constants.ts
@@ -0,0 +1,17 @@
+import { DecodingRules, EncodingRules } from './rules';
+
+export const constants = {
+ EVM_WORD_WIDTH_IN_BYTES: 32,
+ EVM_WORD_WIDTH_IN_BITS: 256,
+ HEX_BASE: 16,
+ DEC_BASE: 10,
+ BIN_BASE: 2,
+ HEX_SELECTOR_LENGTH_IN_CHARS: 10,
+ HEX_SELECTOR_LENGTH_IN_BYTES: 4,
+ HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA: 0,
+ // Disable no-object-literal-type-assertion so we can enforce cast
+ /* tslint:disable no-object-literal-type-assertion */
+ DEFAULT_DECODING_RULES: { structsAsObjects: false } as DecodingRules,
+ DEFAULT_ENCODING_RULES: { optimize: true, annotate: false } as EncodingRules,
+ /* tslint:enable no-object-literal-type-assertion */
+};
diff --git a/packages/utils/src/abi_encoder/utils/math.ts b/packages/utils/src/abi_encoder/utils/math.ts
new file mode 100644
index 000000000..d84983c5b
--- /dev/null
+++ b/packages/utils/src/abi_encoder/utils/math.ts
@@ -0,0 +1,111 @@
+import BigNumber from 'bignumber.js';
+import * as ethUtil from 'ethereumjs-util';
+import * as _ from 'lodash';
+
+import { constants } from '../utils/constants';
+
+function sanityCheckBigNumberRange(
+ value_: BigNumber | string | number,
+ minValue: BigNumber,
+ maxValue: BigNumber,
+): void {
+ const value = new BigNumber(value_, 10);
+ if (value.greaterThan(maxValue)) {
+ throw new Error(`Tried to assign value of ${value}, which exceeds max value of ${maxValue}`);
+ } else if (value.lessThan(minValue)) {
+ throw new Error(`Tried to assign value of ${value}, which exceeds min value of ${minValue}`);
+ }
+}
+function bigNumberToPaddedBuffer(value: BigNumber): Buffer {
+ const valueHex = `0x${value.toString(constants.HEX_BASE)}`;
+ const valueBuf = ethUtil.toBuffer(valueHex);
+ const valueBufPadded = ethUtil.setLengthLeft(valueBuf, constants.EVM_WORD_WIDTH_IN_BYTES);
+ return valueBufPadded;
+}
+/**
+ * Takes a numeric value and returns its ABI-encoded value
+ * @param value_ The value to encode.
+ * @return ABI Encoded value
+ */
+export function encodeNumericValue(value_: BigNumber | string | number): Buffer {
+ const value = new BigNumber(value_, 10);
+ // Case 1/2: value is non-negative
+ if (value.greaterThanOrEqualTo(0)) {
+ const encodedPositiveValue = bigNumberToPaddedBuffer(value);
+ return encodedPositiveValue;
+ }
+ // Case 2/2: Value is negative
+ // Use two's-complement to encode the value
+ // Step 1/3: Convert negative value to positive binary string
+ const valueBin = value.times(-1).toString(constants.BIN_BASE);
+ // Step 2/3: Invert binary value
+ let invertedValueBin = '1'.repeat(constants.EVM_WORD_WIDTH_IN_BITS - valueBin.length);
+ _.each(valueBin, (bit: string) => {
+ invertedValueBin += bit === '1' ? '0' : '1';
+ });
+ const invertedValue = new BigNumber(invertedValueBin, constants.BIN_BASE);
+ // Step 3/3: Add 1 to inverted value
+ const negativeValue = invertedValue.plus(1);
+ const encodedValue = bigNumberToPaddedBuffer(negativeValue);
+ return encodedValue;
+}
+/**
+ * Takes a numeric value and returns its ABI-encoded value.
+ * Performs an additional sanity check, given the min/max allowed value.
+ * @param value_ The value to encode.
+ * @return ABI Encoded value
+ */
+export function safeEncodeNumericValue(
+ value: BigNumber | string | number,
+ minValue: BigNumber,
+ maxValue: BigNumber,
+): Buffer {
+ sanityCheckBigNumberRange(value, minValue, maxValue);
+ const encodedValue = encodeNumericValue(value);
+ return encodedValue;
+}
+/**
+ * Takes an ABI-encoded numeric value and returns its decoded value as a BigNumber.
+ * @param encodedValue The encoded numeric value.
+ * @param minValue The minimum possible decoded value.
+ * @return ABI Decoded value
+ */
+export function decodeNumericValue(encodedValue: Buffer, minValue: BigNumber): BigNumber {
+ const valueHex = ethUtil.bufferToHex(encodedValue);
+ // Case 1/3: value is definitely non-negative because of numeric boundaries
+ const value = new BigNumber(valueHex, constants.HEX_BASE);
+ if (!minValue.lessThan(0)) {
+ return value;
+ }
+ // Case 2/3: value is non-negative because there is no leading 1 (encoded as two's-complement)
+ const valueBin = value.toString(constants.BIN_BASE);
+ const isValueNegative = valueBin.length === constants.EVM_WORD_WIDTH_IN_BITS && _.startsWith(valueBin[0], '1');
+ if (!isValueNegative) {
+ return value;
+ }
+ // Case 3/3: value is negative
+ // Step 1/3: Invert b inary value
+ let invertedValueBin = '';
+ _.each(valueBin, (bit: string) => {
+ invertedValueBin += bit === '1' ? '0' : '1';
+ });
+ const invertedValue = new BigNumber(invertedValueBin, constants.BIN_BASE);
+ // Step 2/3: Add 1 to inverted value
+ // The result is the two's-complement representation of the input value.
+ const positiveValue = invertedValue.plus(1);
+ // Step 3/3: Invert positive value to get the negative value
+ const negativeValue = positiveValue.times(-1);
+ return negativeValue;
+}
+/**
+ * Takes an ABI-encoded numeric value and returns its decoded value as a BigNumber.
+ * Performs an additional sanity check, given the min/max allowed value.
+ * @param encodedValue The encoded numeric value.
+ * @param minValue The minimum possible decoded value.
+ * @return ABI Decoded value
+ */
+export function safeDecodeNumericValue(encodedValue: Buffer, minValue: BigNumber, maxValue: BigNumber): BigNumber {
+ const value = decodeNumericValue(encodedValue, minValue);
+ sanityCheckBigNumberRange(value, minValue, maxValue);
+ return value;
+}
diff --git a/packages/utils/src/abi_encoder/utils/queue.ts b/packages/utils/src/abi_encoder/utils/queue.ts
new file mode 100644
index 000000000..53afb7e11
--- /dev/null
+++ b/packages/utils/src/abi_encoder/utils/queue.ts
@@ -0,0 +1,39 @@
+export class Queue<T> {
+ private _store: T[] = [];
+
+ public pushBack(val: T): void {
+ this._store.push(val);
+ }
+
+ public pushFront(val: T): void {
+ this._store.unshift(val);
+ }
+
+ public popFront(): T | undefined {
+ return this._store.shift();
+ }
+
+ public popBack(): T | undefined {
+ if (this._store.length === 0) {
+ return undefined;
+ }
+ const backElement = this._store.splice(-1, 1)[0];
+ return backElement;
+ }
+
+ public mergeBack(q: Queue<T>): void {
+ this._store = this._store.concat(q._store);
+ }
+
+ public mergeFront(q: Queue<T>): void {
+ this._store = q._store.concat(this._store);
+ }
+
+ public getStore(): T[] {
+ return this._store;
+ }
+
+ public peekFront(): T | undefined {
+ return this._store.length >= 0 ? this._store[0] : undefined;
+ }
+}
diff --git a/packages/utils/src/abi_encoder/utils/rules.ts b/packages/utils/src/abi_encoder/utils/rules.ts
new file mode 100644
index 000000000..31471e97a
--- /dev/null
+++ b/packages/utils/src/abi_encoder/utils/rules.ts
@@ -0,0 +1,8 @@
+export interface DecodingRules {
+ structsAsObjects: boolean;
+}
+
+export interface EncodingRules {
+ optimize?: boolean;
+ annotate?: boolean;
+}
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index 0723e5788..082aff6bb 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -10,3 +10,4 @@ export { NULL_BYTES } from './constants';
export { errorUtils } from './error_utils';
export { fetchAsync } from './fetch_async';
export { signTypedDataUtils } from './sign_typed_data_utils';
+export import AbiEncoder = require('./abi_encoder');
diff --git a/packages/utils/test/abi_encoder/abi_samples/method_abis.ts b/packages/utils/test/abi_encoder/abi_samples/method_abis.ts
new file mode 100644
index 000000000..fc552c127
--- /dev/null
+++ b/packages/utils/test/abi_encoder/abi_samples/method_abis.ts
@@ -0,0 +1,780 @@
+/* tslint:disable max-file-line-count */
+import { MethodAbi } from 'ethereum-types';
+
+export const simpleAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'greg',
+ type: 'uint256',
+ },
+ {
+ name: 'gregStr',
+ type: 'string',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const stringAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'greg',
+ type: 'string[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const GAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'a',
+ type: 'uint256',
+ },
+ {
+ name: 'b',
+ type: 'string',
+ },
+ {
+ name: 'e',
+ type: 'bytes',
+ },
+ {
+ name: 'f',
+ type: 'address',
+ },
+ ],
+
+ name: 'f',
+ type: 'tuple',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const typesWithDefaultWidthsAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'someUint',
+ type: 'uint',
+ },
+ {
+ name: 'someInt',
+ type: 'int',
+ },
+ {
+ name: 'someByte',
+ type: 'byte',
+ },
+ {
+ name: 'someUint',
+ type: 'uint[]',
+ },
+ {
+ name: 'someInt',
+ type: 'int[]',
+ },
+ {
+ name: 'someByte',
+ type: 'byte[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const multiDimensionalArraysStaticTypeAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'a',
+ type: 'uint8[][][]',
+ },
+ {
+ name: 'b',
+ type: 'uint8[][][2]',
+ },
+ {
+ name: 'c',
+ type: 'uint8[][2][]',
+ },
+ {
+ name: 'd',
+ type: 'uint8[2][][]',
+ },
+ {
+ name: 'e',
+ type: 'uint8[][2][2]',
+ },
+ {
+ name: 'f',
+ type: 'uint8[2][2][]',
+ },
+ {
+ name: 'g',
+ type: 'uint8[2][][2]',
+ },
+ {
+ name: 'h',
+ type: 'uint8[2][2][2]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const multiDimensionalArraysDynamicTypeAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'a',
+ type: 'string[][][]',
+ },
+ {
+ name: 'b',
+ type: 'string[][][2]',
+ },
+ {
+ name: 'c',
+ type: 'string[][2][]',
+ },
+ {
+ name: 'h',
+ type: 'string[2][2][2]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const dynamicTupleAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someStr',
+ type: 'string',
+ },
+ ],
+ name: 'order',
+ type: 'tuple',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const arrayOfStaticTuplesWithDefinedLengthAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someUint2',
+ type: 'uint256',
+ },
+ ],
+ name: 'order',
+ type: 'tuple[8]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const arrayOfStaticTuplesWithDynamicLengthAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someUint2',
+ type: 'uint256',
+ },
+ ],
+ name: 'order',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const arrayOfDynamicTuplesWithDefinedLengthAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someString',
+ type: 'string',
+ },
+ ],
+ name: 'order',
+ type: 'tuple[8]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const arrayOfDynamicTuplesWithUndefinedLengthAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someString',
+ type: 'string',
+ },
+ ],
+ name: 'order',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const arrayOfDynamicTuplesAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someString',
+ type: 'string',
+ },
+ ],
+ name: 'order',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const multidimensionalArrayOfDynamicTuplesAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someString',
+ type: 'string',
+ },
+ ],
+ name: 'order',
+ type: 'tuple[][2][]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const staticTupleAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'someUint1',
+ type: 'uint256',
+ },
+ {
+ name: 'someUint2',
+ type: 'uint256',
+ },
+ {
+ name: 'someUint3',
+ type: 'uint256',
+ },
+ {
+ name: 'someBool',
+ type: 'bool',
+ },
+ ],
+ name: 'order',
+ type: 'tuple',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const staticArrayAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'someStaticArray',
+ type: 'uint8[3]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const staticArrayDynamicMembersAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'someStaticArray',
+ type: 'string[3]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const dynamicArrayDynamicMembersAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'someStaticArray',
+ type: 'string[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const dynamicArrayStaticMembersAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'someStaticArray',
+ type: 'uint8[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const largeFlatAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'someUInt256',
+ type: 'uint256',
+ },
+ {
+ name: 'someInt256',
+ type: 'int256',
+ },
+ {
+ name: 'someInt32',
+ type: 'int32',
+ },
+ {
+ name: 'someByte',
+ type: 'byte',
+ },
+ {
+ name: 'someBytes32',
+ type: 'bytes32',
+ },
+ {
+ name: 'someBytes',
+ type: 'bytes',
+ },
+ {
+ name: 'someString',
+ type: 'string',
+ },
+ {
+ name: 'someAddress',
+ type: 'address',
+ },
+ {
+ name: 'someBool',
+ type: 'bool',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const largeNestedAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'someStaticArray',
+ type: 'uint8[3]',
+ },
+ {
+ name: 'someStaticArrayWithDynamicMembers',
+ type: 'string[2]',
+ },
+ {
+ name: 'someDynamicArrayWithDynamicMembers',
+ type: 'bytes[]',
+ },
+ {
+ name: 'some2DArray',
+ type: 'string[][]',
+ },
+ {
+ name: 'someTuple',
+ type: 'tuple',
+ components: [
+ {
+ name: 'someUint32',
+ type: 'uint32',
+ },
+ {
+ name: 'someStr',
+ type: 'string',
+ },
+ ],
+ },
+ {
+ name: 'someTupleWithDynamicTypes',
+ type: 'tuple',
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someStr',
+ type: 'string',
+ },
+ /*{
+ name: 'someStrArray',
+ type: 'string[]',
+ },*/
+ {
+ name: 'someBytes',
+ type: 'bytes',
+ },
+ {
+ name: 'someAddress',
+ type: 'address',
+ },
+ ],
+ },
+ {
+ name: 'someArrayOfTuplesWithDynamicTypes',
+ type: 'tuple[]',
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someStr',
+ type: 'string',
+ },
+ /*{
+ name: 'someStrArray',
+ type: 'string[]',
+ },*/
+ {
+ name: 'someBytes',
+ type: 'bytes',
+ },
+ {
+ name: 'someAddress',
+ type: 'address',
+ },
+ ],
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const nestedTuples: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'firstTuple',
+ type: 'tuple[1]',
+ components: [
+ {
+ name: 'someUint32',
+ type: 'uint32',
+ },
+ {
+ name: 'nestedTuple',
+ type: 'tuple',
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someAddress',
+ type: 'address',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: 'secondTuple',
+ type: 'tuple[]',
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someStr',
+ type: 'string',
+ },
+ {
+ name: 'nestedTuple',
+ type: 'tuple',
+ components: [
+ {
+ name: 'someUint32',
+ type: 'uint32',
+ },
+ {
+ name: 'secondNestedTuple',
+ type: 'tuple',
+ components: [
+ {
+ name: 'someUint',
+ type: 'uint256',
+ },
+ {
+ name: 'someStr',
+ type: 'string',
+ },
+ {
+ name: 'someBytes',
+ type: 'bytes',
+ },
+ {
+ name: 'someAddress',
+ type: 'address',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: 'someBytes',
+ type: 'bytes',
+ },
+ {
+ name: 'someAddress',
+ type: 'address',
+ },
+ ],
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const simpleAbi2: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'someByte',
+ type: 'byte',
+ },
+ {
+ name: 'someBytes32',
+ type: 'bytes32',
+ },
+ {
+ name: 'someBytes',
+ type: 'bytes',
+ },
+ {
+ name: 'someString',
+ type: 'string',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const fillOrderAbi: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'makerAddress',
+ type: 'address',
+ },
+ {
+ name: 'takerAddress',
+ type: 'address',
+ },
+ {
+ name: 'feeRecipientAddress',
+ type: 'address',
+ },
+ {
+ name: 'senderAddress',
+ type: 'address',
+ },
+ {
+ name: 'makerAssetAmount',
+ type: 'uint256',
+ },
+ {
+ name: 'takerAssetAmount',
+ type: 'uint256',
+ },
+ {
+ name: 'makerFee',
+ type: 'uint256',
+ },
+ {
+ name: 'takerFee',
+ type: 'uint256',
+ },
+ {
+ name: 'expirationTimeSeconds',
+ type: 'uint256',
+ },
+ {
+ name: 'salt',
+ type: 'uint256',
+ },
+ {
+ name: 'makerAssetData',
+ type: 'bytes',
+ },
+ {
+ name: 'takerAssetData',
+ type: 'bytes',
+ },
+ ],
+ name: 'order',
+ type: 'tuple',
+ },
+ {
+ name: 'takerAssetFillAmount',
+ type: 'uint256',
+ },
+ {
+ name: 'salt',
+ type: 'uint256',
+ },
+ {
+ name: 'orderSignature',
+ type: 'bytes',
+ },
+ {
+ name: 'takerSignature',
+ type: 'bytes',
+ },
+ ],
+ name: 'fillOrder',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
diff --git a/packages/utils/test/abi_encoder/abi_samples/optimizer_abis.ts b/packages/utils/test/abi_encoder/abi_samples/optimizer_abis.ts
new file mode 100644
index 000000000..7cfd7a118
--- /dev/null
+++ b/packages/utils/test/abi_encoder/abi_samples/optimizer_abis.ts
@@ -0,0 +1,340 @@
+/* tslint:disable max-file-line-count */
+import { MethodAbi } from 'ethereum-types';
+
+export const duplicateDynamicArraysWithStaticElements: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'array1',
+ type: 'uint[]',
+ },
+ {
+ name: 'array2',
+ type: 'uint[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateDynamicArraysWithDynamicElements: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'array1',
+ type: 'string[]',
+ },
+ {
+ name: 'array2',
+ type: 'string[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateStaticArraysWithStaticElements: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'array1',
+ type: 'uint[2]',
+ },
+ {
+ name: 'array2',
+ type: 'uint[2]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateStaticArraysWithDynamicElements: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'array1',
+ type: 'string[2]',
+ },
+ {
+ name: 'array2',
+ type: 'string[2]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateArrayElements: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'array',
+ type: 'string[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateTupleFields: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'field1',
+ type: 'string',
+ },
+ {
+ name: 'field2',
+ type: 'string',
+ },
+ ],
+ name: 'Tuple',
+ type: 'tuple',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateStrings: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'string1',
+ type: 'string',
+ },
+ {
+ name: 'string2',
+ type: 'string',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateBytes: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'bytes1',
+ type: 'bytes',
+ },
+ {
+ name: 'bytes2',
+ type: 'bytes',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateTuples: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'field1',
+ type: 'string',
+ },
+ {
+ name: 'field2',
+ type: 'uint',
+ },
+ ],
+ name: 'Tuple',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ name: 'field1',
+ type: 'string',
+ },
+ {
+ name: 'field2',
+ type: 'uint',
+ },
+ ],
+ name: 'Tuple',
+ type: 'tuple',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateArraysNestedInTuples: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ name: 'field',
+ type: 'uint[]',
+ },
+ ],
+ name: 'Tuple1',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ name: 'field',
+ type: 'uint[]',
+ },
+ {
+ name: 'extraField',
+ type: 'string',
+ },
+ ],
+ name: 'Tuple2',
+ type: 'tuple',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateTuplesNestedInTuples: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ components: [
+ {
+ components: [
+ {
+ name: 'nestedField',
+ type: 'string',
+ },
+ ],
+ name: 'field',
+ type: 'tuple',
+ },
+ ],
+ name: 'Tuple1',
+ type: 'tuple',
+ },
+ {
+ components: [
+ {
+ components: [
+ {
+ name: 'nestedField',
+ type: 'string',
+ },
+ ],
+ name: 'field',
+ type: 'tuple',
+ },
+ {
+ name: 'extraField',
+ type: 'string',
+ },
+ ],
+ name: 'Tuple1',
+ type: 'tuple',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const duplicateTwoDimensionalArrays: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'array1',
+ type: 'string[][]',
+ },
+ {
+ name: 'array2',
+ type: 'string[][]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const arrayElementsDuplicatedAsSeparateParameter: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'stringArray',
+ type: 'string[]',
+ },
+ {
+ name: 'string',
+ type: 'string',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const arrayElementsDuplicatedAsTupleFields: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'uint8Array',
+ type: 'uint8[]',
+ },
+ {
+ components: [
+ {
+ name: 'uint',
+ type: 'uint',
+ },
+ ],
+ name: 'uintTuple',
+ type: 'tuple[]',
+ },
+ ],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
diff --git a/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts b/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts
new file mode 100644
index 000000000..ac2124011
--- /dev/null
+++ b/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts
@@ -0,0 +1,99 @@
+/* tslint:disable max-file-line-count */
+import { MethodAbi } from 'ethereum-types';
+
+export const noReturnValues: MethodAbi = {
+ constant: false,
+ inputs: [],
+ name: 'simpleFunction',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const singleStaticReturnValue: MethodAbi = {
+ constant: false,
+ inputs: [],
+ name: 'simpleFunction',
+ outputs: [
+ {
+ name: 'Bytes4',
+ type: 'bytes4',
+ },
+ ],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const multipleStaticReturnValues: MethodAbi = {
+ constant: false,
+ inputs: [],
+ name: 'simpleFunction',
+ outputs: [
+ {
+ name: 'val1',
+ type: 'bytes4',
+ },
+ {
+ name: 'val2',
+ type: 'bytes4',
+ },
+ ],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const singleDynamicReturnValue: MethodAbi = {
+ constant: false,
+ inputs: [],
+ name: 'simpleFunction',
+ outputs: [
+ {
+ name: 'val',
+ type: 'bytes',
+ },
+ ],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const multipleDynamicReturnValues: MethodAbi = {
+ constant: false,
+ inputs: [],
+ name: 'simpleFunction',
+ outputs: [
+ {
+ name: 'val1',
+ type: 'bytes',
+ },
+ {
+ name: 'val2',
+ type: 'bytes',
+ },
+ ],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+export const mixedStaticAndDynamicReturnValues: MethodAbi = {
+ constant: false,
+ inputs: [],
+ name: 'simpleFunction',
+ outputs: [
+ {
+ name: 'val1',
+ type: 'bytes4',
+ },
+ {
+ name: 'val2',
+ type: 'bytes',
+ },
+ ],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
diff --git a/packages/utils/test/abi_encoder/evm_data_types_test.ts b/packages/utils/test/abi_encoder/evm_data_types_test.ts
new file mode 100644
index 000000000..9ef80a560
--- /dev/null
+++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts
@@ -0,0 +1,1007 @@
+/* tslint:disable max-file-line-count */
+import * as chai from 'chai';
+import * as ethUtil from 'ethereumjs-util';
+import 'mocha';
+
+import { AbiEncoder, BigNumber } from '../../src/';
+import { chaiSetup } from '../utils/chai_setup';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
+ const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately.
+ describe('Array', () => {
+ it('Fixed size; Static elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'int[2]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const args = [new BigNumber(5), new BigNumber(6)];
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Dynamic size; Static elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'int[]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const args = [new BigNumber(5), new BigNumber(6)];
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Fixed size; Dynamic elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'string[2]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const args = ['Hello', 'world'];
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Dynamic size; Dynamic elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'string[]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const args = ['Hello', 'world'];
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Dynamic Size; Multidimensional; Dynamic Elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'bytes[][]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const array1 = ['0x01020304', '0x05060708', '0x09101112'];
+ const array2 = ['0x10111213', '0x14151617'];
+ const array3 = ['0x18192021'];
+ const args = [array1, array2, array3];
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000041011121300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414151617000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Dynamic Size; Multidimensional; Static Elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'bytes4[][]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const array1 = ['0x01020304', '0x05060708', '0x09101112'];
+ const array2 = ['0x10111213', '0x14151617'];
+ const array3 = ['0x18192021'];
+ const args = [array1, array2, array3];
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000301020304000000000000000000000000000000000000000000000000000000000506070800000000000000000000000000000000000000000000000000000000091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021011121300000000000000000000000000000000000000000000000000000000141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011819202100000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Static Size; Multidimensional; Static Elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'bytes4[3][2]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const array1 = ['0x01020304', '0x05060708', '0x09101112'];
+ const array2 = ['0x10111213', '0x14151617', '0x18192021'];
+ const args = [array1, array2];
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x010203040000000000000000000000000000000000000000000000000000000005060708000000000000000000000000000000000000000000000000000000000910111200000000000000000000000000000000000000000000000000000000101112130000000000000000000000000000000000000000000000000000000014151617000000000000000000000000000000000000000000000000000000001819202100000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Static Size; Multidimensional; Dynamic Elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'bytes[3][2]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const array1 = ['0x01020304', '0x05060708', '0x09101112'];
+ const array2 = ['0x10111213', '0x14151617', '0x18192021'];
+ const args = [array1, array2];
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000401020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040910111200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000410111213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Static size; Too Few Elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'string[3]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const args = ['Hello', 'world'];
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw('Expected array of 3 elements, but got array of length 2');
+ });
+ it('Static size; Too Many Elements', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'string[1]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const args = ['Hello', 'world'];
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw('Expected array of 1 elements, but got array of length 2');
+ });
+ it('Element Type Mismatch', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'uint[]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ // Construct args to be encoded
+ const args = [new BigNumber(1), 'Bad Argument'];
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ });
+
+ describe('Tuple', () => {
+ it('Static elements only', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = { field_1: new BigNumber(-5), field_2: true };
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodedArgs = dataType.decode(encodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Dynamic elements only', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field_1', type: 'string' }, { name: 'field_2', type: 'bytes' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = { field_1: 'Hello, World!', field_2: '0xabcdef0123456789' };
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodedArgs = dataType.decode(encodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Nested Static Array', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field', type: 'uint[2]' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = { field: [new BigNumber(1), new BigNumber(2)] };
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodedArgs = dataType.decode(encodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Nested Dynamic Array', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field', type: 'uint[]' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = { field: [new BigNumber(1), new BigNumber(2)] };
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodedArgs = dataType.decode(encodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Nested Static Multidimensional Array', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field', type: 'bytes4[2][2]' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const array1 = ['0x01020304', '0x05060708'];
+ const array2 = ['0x09101112', '0x13141516'];
+ const args = { field: [array1, array2] };
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodedArgs = dataType.decode(encodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Nested Dynamic Multidimensional Array', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field', type: 'bytes[2][2]' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const array1 = ['0x01020304', '0x05060708'];
+ const array2 = ['0x09101112', '0x13141516'];
+ const args = { field: [array1, array2] };
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodedArgs = dataType.decode(encodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Static and dynamic elements mixed', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [
+ { name: 'field_1', type: 'int32' },
+ { name: 'field_2', type: 'string' },
+ { name: 'field_3', type: 'bool' },
+ { name: 'field_4', type: 'bytes' },
+ ],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = {
+ field_1: new BigNumber(-5),
+ field_2: 'Hello, World!',
+ field_3: true,
+ field_4: '0xabcdef0123456789',
+ };
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodedArgs = dataType.decode(encodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Missing Key', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = { field_1: new BigNumber(-5) };
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw('Could not assign tuple to object: missing keys field_2');
+ });
+ it('Bad Key', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = { unknown_field: new BigNumber(-5) };
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw("Could not assign tuple to object: unrecognized key 'unknown_field' in object Tuple");
+ });
+ });
+
+ describe('Address', () => {
+ it('Valid Address', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Address', type: 'address' };
+ const dataType = new AbiEncoder.Address(testDataItem);
+ // Construct args to be encoded
+ const args = '0xe41d2489571d322189246dafa5ebde1f4699f498';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Invalid Address - input is not valid hex', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Address', type: 'address' };
+ const dataType = new AbiEncoder.Address(testDataItem);
+ // Construct args to be encoded
+ const args = 'e4';
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw(`Invalid address: '${args}'`);
+ });
+ it('Invalid Address - input is not 20 bytes', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Address', type: 'address' };
+ const dataType = new AbiEncoder.Address(testDataItem);
+ // Construct args to be encoded
+ const args = '0xe4';
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw(`Invalid address: '${args}'`);
+ });
+ });
+
+ describe('Bool', () => {
+ it('True', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Boolean', type: 'bool' };
+ const dataType = new AbiEncoder.Bool(testDataItem);
+ // Construct args to be encoded
+ const args = true;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('False', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Boolean', type: 'bool' };
+ const dataType = new AbiEncoder.Bool(testDataItem);
+ // Construct args to be encoded
+ const args = false;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ });
+
+ describe('Integer', () => {
+ /* tslint:disable custom-no-magic-numbers */
+ const max256BitInteger = new BigNumber(2).pow(255).minus(1);
+ const min256BitInteger = new BigNumber(2).pow(255).times(-1);
+ const max32BitInteger = new BigNumber(2).pow(31).minus(1);
+ const min32BitInteger = new BigNumber(2).pow(31).times(-1);
+ /* tslint:enable custom-no-magic-numbers */
+
+ it('Int256 - Positive Base Case', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (256)', type: 'int' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = new BigNumber(1);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Int256 - Negative Base Case', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (256)', type: 'int' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = new BigNumber(-1);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Int256 - Positive Value', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (256)', type: 'int' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = max256BitInteger;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Int256 - Negative Value', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (256)', type: 'int' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = min256BitInteger;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = `0x8000000000000000000000000000000000000000000000000000000000000000`;
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Int256 - Value too large', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (256)', type: 'int' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = max256BitInteger.plus(1);
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ it('Int256 - Value too small', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (256)', type: 'int' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = min256BitInteger.minus(1);
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ it('Int32 - Positive Base Case', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (32)', type: 'int32' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = new BigNumber(1);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Int32 - Negative Base Case', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (32)', type: 'int32' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = new BigNumber(-1);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Int32 - Positive Value', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (32)', type: 'int32' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = max32BitInteger;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000007fffffff';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Int32 - Negative Value', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (32)', type: 'int32' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = min32BitInteger;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000`;
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Int32 - Value too large', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (32)', type: 'int32' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = max32BitInteger.plus(1);
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ it('Int32 - Value too small', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (32)', type: 'int32' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ // Construct args to be encoded
+ const args = min32BitInteger.minus(1);
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ });
+
+ describe('Unsigned Integer', () => {
+ /* tslint:disable custom-no-magic-numbers */
+ const max256BitUnsignedInteger = new BigNumber(2).pow(256).minus(1);
+ const min256BitUnsignedInteger = new BigNumber(0);
+ const max32BitUnsignedInteger = new BigNumber(2).pow(32).minus(1);
+ const min32BitUnsignedInteger = new BigNumber(0);
+ /* tslint:enable custom-no-magic-numbers */
+
+ it('UInt256 - Positive Base Case', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = new BigNumber(1);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('UInt256 - Positive Value', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = max256BitUnsignedInteger;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('UInt256 - Zero Value', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = min256BitUnsignedInteger;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`;
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('UInt256 - Value too large', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = max256BitUnsignedInteger.plus(1);
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ it('UInt256 - Value too small', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = min256BitUnsignedInteger.minus(1);
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ it('UInt32 - Positive Base Case', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = new BigNumber(1);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('UInt32 - Positive Value', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = max32BitUnsignedInteger;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000ffffffff';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('UInt32 - Zero Value', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = min32BitUnsignedInteger;
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`;
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('UInt32 - Value too large', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = max32BitUnsignedInteger.plus(1);
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ it('UInt32 - Value too small', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ // Construct args to be encoded
+ const args = min32BitUnsignedInteger.minus(1);
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw();
+ });
+ });
+
+ describe('Static Bytes', () => {
+ it('Single Byte (byte)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Byte', type: 'byte' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0x05';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Single Byte (bytes1)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes1', type: 'bytes1' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0x05';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('4 Bytes (bytes4)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes4', type: 'bytes4' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0x00010203';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0001020300000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('4 Bytes (bytes4); Encoder must pad input', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes4', type: 'bytes4' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes.
+ const args = '0x1a18';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x1a18000000000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ const paddedArgs = '0x1a180000';
+ expect(decodedArgs).to.be.deep.equal(paddedArgs);
+ });
+ it('32 Bytes (bytes32)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes32', type: 'bytes32' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0x0001020304050607080911121314151617181920212223242526272829303132';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x0001020304050607080911121314151617181920212223242526272829303132';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('32 Bytes (bytes32); Encoder must pad input', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes32', type: 'bytes32' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes.
+ const args = '0x1a18bf61';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ const paddedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000';
+ expect(decodedArgs).to.be.deep.equal(paddedArgs);
+ });
+ it('Should throw when pass in too many bytes (bytes4)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes4', type: 'bytes4' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0x0102030405';
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw(
+ 'Tried to assign 0x0102030405 (5 bytes), which exceeds max bytes that can be stored in a bytes4',
+ );
+ });
+ it('Should throw when pass in too many bytes (bytes32)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes32', type: 'bytes32' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0x010203040506070809101112131415161718192021222324252627282930313233';
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw(
+ 'Tried to assign 0x010203040506070809101112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32',
+ );
+ });
+ it('Should throw when pass in bad hex (no 0x prefix)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes32', type: 'bytes32' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0102030405060708091011121314151617181920212223242526272829303132';
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix.");
+ });
+ it('Should throw when pass in bad hex (include a half-byte)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes32', type: 'bytes32' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0x010';
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.');
+ });
+ });
+
+ describe('Dynamic Bytes', () => {
+ it('Fits into one EVM word', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' };
+ const dataType = new AbiEncoder.DynamicBytes(testDataItem);
+ // Construct args to be encoded
+ // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes.
+ const args = '0x1a18bf61';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Spans multiple EVM words', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' };
+ const dataType = new AbiEncoder.DynamicBytes(testDataItem);
+ // Construct args to be encoded
+ // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes.
+ const bytesLength = 40;
+ const args = '0x' + '61'.repeat(bytesLength);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Input as Buffer', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' };
+ const dataType = new AbiEncoder.DynamicBytes(testDataItem);
+ // Construct args to be encoded
+ // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes.
+ const args = '0x1a18bf61';
+ const argsAsBuffer = ethUtil.toBuffer(args);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(argsAsBuffer);
+ const expectedEncodedArgs =
+ '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Should throw when pass in bad hex (no 0x prefix)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes', type: 'bytes' };
+ const dataType = new AbiEncoder.DynamicBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '01';
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix.");
+ });
+ it('Should throw when pass in bad hex (include a half-byte)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes', type: 'bytes' };
+ const dataType = new AbiEncoder.DynamicBytes(testDataItem);
+ // Construct args to be encoded
+ const args = '0x010';
+ // Encode Args and validate result
+ expect(() => {
+ dataType.encode(args, encodingRules);
+ }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.');
+ });
+ });
+
+ describe('String', () => {
+ it('Fits into one EVM word', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'String', type: 'string' };
+ const dataType = new AbiEncoder.String(testDataItem);
+ // Construct args to be encoded
+ // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes.
+ const args = 'five';
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Spans multiple EVM words', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'String', type: 'string' };
+ const dataType = new AbiEncoder.String(testDataItem);
+ // Construct args to be encoded
+ // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes.
+ const bytesLength = 40;
+ const args = 'a'.repeat(bytesLength);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('String that begins with 0x prefix', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'String', type: 'string' };
+ const dataType = new AbiEncoder.String(testDataItem);
+ // Construct args to be encoded
+ // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes.
+ const strLength = 40;
+ const args = '0x' + 'a'.repeat(strLength);
+ // Encode Args and validate result
+ const encodedArgs = dataType.encode(args, encodingRules);
+ const expectedEncodedArgs =
+ '0x000000000000000000000000000000000000000000000000000000000000002a30786161616161616161616161616161616161616161616161616161616161616161616161616161616100000000000000000000000000000000000000000000';
+ expect(encodedArgs).to.be.equal(expectedEncodedArgs);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(encodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ });
+});
diff --git a/packages/utils/test/abi_encoder/methods_test.ts b/packages/utils/test/abi_encoder/methods_test.ts
new file mode 100644
index 000000000..837020883
--- /dev/null
+++ b/packages/utils/test/abi_encoder/methods_test.ts
@@ -0,0 +1,366 @@
+import * as chai from 'chai';
+import 'mocha';
+
+import { AbiEncoder, BigNumber } from '../../src/';
+import { chaiSetup } from '../utils/chai_setup';
+
+import * as AbiSamples from './abi_samples/method_abis';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+describe('ABI Encoder: Method Encoding / Decoding', () => {
+ const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately.
+ it('Types with default widths', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi);
+ const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Array of Static Tuples (Array has defined length)', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi);
+ let value = 0;
+ const arrayOfTuples = [];
+ const arrayOfTuplesLength = 8;
+ for (let i = 0; i < arrayOfTuplesLength; ++i) {
+ arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]);
+ }
+ const args = [arrayOfTuples];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Array of Static Tuples (Array has dynamic length)', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi);
+ let value = 0;
+ const arrayOfTuples = [];
+ const arrayOfTuplesLength = 8;
+ for (let i = 0; i < arrayOfTuplesLength; ++i) {
+ arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]);
+ }
+ const args = [arrayOfTuples];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Array of Dynamic Tuples (Array has defined length)', async () => {
+ // Generate Calldata
+ const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi);
+ let value = 0;
+ const arrayOfTuples = [];
+ const arrayOfTuplesLength = 8;
+ for (let i = 0; i < arrayOfTuplesLength; ++i) {
+ arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]);
+ }
+ const args = [arrayOfTuples];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Array of Dynamic Tuples (Array has dynamic length)', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi);
+ let value = 0;
+ const arrayOfTuples = [];
+ const arrayOfTuplesLength = 8;
+ for (let i = 0; i < arrayOfTuplesLength; ++i) {
+ arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]);
+ }
+ const args = [arrayOfTuples];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Multidimensional Arrays / Static Members', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi);
+ // Eight 3-dimensional arrays of uint8[2][2][2]
+ let value = 0;
+ const args = [];
+ const argsLength = 8;
+ for (let i = 0; i < argsLength; ++i) {
+ args.push([
+ [[new BigNumber(++value), new BigNumber(++value)], [new BigNumber(++value), new BigNumber(++value)]],
+ [[new BigNumber(++value), new BigNumber(++value)], [new BigNumber(++value), new BigNumber(++value)]],
+ ]);
+ }
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038';
+ expect(calldata).to.be.equal(expectedCalldata);
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Multidimensional Arrays / Dynamic Members', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi);
+ // Eight 3-dimensional arrays of string[2][2][2]
+ let value = 0;
+ const args = [];
+ const argsLength = 4;
+ for (let i = 0; i < argsLength; ++i) {
+ args.push([
+ [
+ [new BigNumber(++value).toString(), new BigNumber(++value).toString()],
+ [new BigNumber(++value).toString(), new BigNumber(++value).toString()],
+ ],
+ [
+ [new BigNumber(++value).toString(), new BigNumber(++value).toString()],
+ [new BigNumber(++value).toString(), new BigNumber(++value).toString()],
+ ],
+ ]);
+ }
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Fixed Length Array / Dynamic Members', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi);
+ const args = [['Brave', 'New', 'World']];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Fixed Length Array / Dynamic Members', async () => {
+ // Generaet calldata
+ const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi);
+ const args = [['Brave', 'New', 'World']];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Unfixed Length Array / Dynamic Members ABI', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi);
+ const args = [['Brave', 'New', 'World']];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Unfixed Length Array / Static Members ABI', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi);
+ const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Fixed Length Array / Static Members ABI', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi);
+ const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Array ABI', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(AbiSamples.stringAbi);
+ const args = [['five', 'six', 'seven']];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Static Tuple', async () => {
+ // Generate calldata
+ // This is dynamic because it has dynamic members
+ const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi);
+ const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Dynamic Tuple (Array input)', async () => {
+ // Generate calldata
+ // This is dynamic because it has dynamic members
+ const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi);
+ const args = [[new BigNumber(5), 'five']];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Dynamic Tuple (Object input)', async () => {
+ // Generate Calldata
+ // This is dynamic because it has dynamic members
+ const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi);
+ const args = [[new BigNumber(5), 'five']];
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Large, Flat ABI', async () => {
+ // Construct calldata
+ const method = new AbiEncoder.Method(AbiSamples.largeFlatAbi);
+ const args = [
+ new BigNumber(256745454),
+ new BigNumber(-256745454),
+ new BigNumber(434244),
+ '0x43',
+ '0x0001020304050607080911121314151617181920212223242526272829303132',
+ '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132',
+ 'Little peter piper piped a piping pepper pot',
+ '0xe41d2489571d322189246dafa5ebde1f4699f498',
+ true,
+ ];
+ // Validate calldata
+ const calldata = method.encode(args, encodingRules);
+ const expectedCalldata =
+ '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata);
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+ it('Large, Nested ABI', async () => {
+ // Construct Calldata
+ const method = new AbiEncoder.Method(AbiSamples.largeNestedAbi);
+ const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)];
+ const someStaticArrayWithDynamicMembers = [
+ 'the little piping piper piped a piping pipper papper',
+ 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.',
+ ];
+ const someDynamicArrayWithDynamicMembers = [
+ '0x38745637834987324827439287423897238947239847',
+ '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398',
+ '0x283473298473248923749238742398742398472894729843278942374982374892374892743982',
+ ];
+ const some2DArray = [
+ [
+ 'some string',
+ 'some another string',
+ 'there are just too many stringsup in',
+ 'here',
+ 'yall ghonna make me lose my mind',
+ ],
+ [
+ 'the little piping piper piped a piping pipper papper',
+ 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.',
+ ],
+ [],
+ ];
+ const someTuple = {
+ someUint32: new BigNumber(4037824789),
+ someStr:
+ 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.',
+ };
+ const someTupleWithDynamicTypes = {
+ someUint: new BigNumber(4024789),
+ someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk',
+ someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea',
+ someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498',
+ };
+ const someTupleWithDynamicTypes2 = {
+ someUint: new BigNumber(9024789),
+ someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj',
+ someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1',
+ someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895',
+ };
+ const someTupleWithDynamicTypes3 = {
+ someUint: new BigNumber(1024789),
+ someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk',
+ someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef',
+ someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa',
+ };
+ const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3];
+ const args = {
+ someStaticArray,
+ someStaticArrayWithDynamicMembers,
+ someDynamicArrayWithDynamicMembers,
+ some2DArray,
+ someTuple,
+ someTupleWithDynamicTypes,
+ someArrayOfTuplesWithDynamicTypes,
+ };
+ const calldata = method.encode(args, encodingRules);
+ // Validate calldata
+ const expectedCalldata =
+ '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000';
+ expect(calldata).to.be.equal(expectedCalldata);
+ // Validate decoding
+ const decodedValue = method.decode(calldata, { structsAsObjects: true });
+ expect(decodedValue).to.be.deep.equal(args);
+ });
+});
diff --git a/packages/utils/test/abi_encoder/optimizer_test.ts b/packages/utils/test/abi_encoder/optimizer_test.ts
new file mode 100644
index 000000000..18aa6549a
--- /dev/null
+++ b/packages/utils/test/abi_encoder/optimizer_test.ts
@@ -0,0 +1,262 @@
+import * as chai from 'chai';
+import 'mocha';
+
+import { AbiEncoder, BigNumber } from '../../src/';
+import { chaiSetup } from '../utils/chai_setup';
+
+import * as OptimizedAbis from './abi_samples/optimizer_abis';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+describe('ABI Encoder: Optimized Method Encoding/Decoding', () => {
+ const encodingRules: AbiEncoder.EncodingRules = { optimize: true };
+ it('Duplicate Dynamic Arrays with Static Elements', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithStaticElements);
+ const array1 = [new BigNumber(100), new BigNumber(150)];
+ const array2 = array1;
+ const args = [array1, array2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x7221063300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Dynamic Arrays with Dynamic Elements', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithDynamicElements);
+ const array1 = ['Hello', 'World'];
+ const array2 = array1;
+ const args = [array1, array2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0xbb4f12e300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Static Arrays with Static Elements (should not optimize)', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithStaticElements);
+ const array1 = [new BigNumber(100), new BigNumber(150)];
+ const array2 = array1;
+ const args = [array1, array2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x7f8130430000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ const unoptimizedCalldata = method.encode(args);
+ expect(optimizedCalldata).to.be.equal(unoptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Static Arrays with Dynamic Elements', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithDynamicElements);
+ const array1 = ['Hello', 'World'];
+ const array2 = array1;
+ const args = [array1, array2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x9fe31f8e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Array Elements (should optimize)', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateArrayElements);
+ const strings = ['Hello', 'World', 'Hello', 'World'];
+ const args = [strings];
+ // Validate calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Tuple Fields', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateTupleFields);
+ const tuple = ['Hello', 'Hello'];
+ const args = [tuple];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x16780a5e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Strings', async () => {
+ // Description:
+ // Two dynamic arrays with the same values.
+ // In the optimized calldata, only one set of elements should be included.
+ // Both arrays should point to this set.
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateStrings);
+ const args = ['Hello', 'Hello'];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x07370bfa00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Bytes', async () => {
+ // Description:
+ // Two dynamic arrays with the same values.
+ // In the optimized calldata, only one set of elements should be included.
+ // Both arrays should point to this set.
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateBytes);
+ const value = '0x01020304050607080910111213141516171819202122232425262728293031323334353637383940';
+ const args = [value, value];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x6045e42900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002801020304050607080910111213141516171819202122232425262728293031323334353637383940000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Tuples', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuples);
+ const tuple1 = ['Hello, World!', new BigNumber(424234)];
+ const tuple2 = tuple1;
+ const args = [tuple1, tuple2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000006792a000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Fields Across Two Tuples', async () => {
+ // Description:
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuples);
+ const tuple1 = ['Hello, World!', new BigNumber(1)];
+ const tuple2 = [tuple1[0], new BigNumber(2)];
+ const args = [tuple1, tuple2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Arrays, Nested in Separate Tuples', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateArraysNestedInTuples);
+ const array = [new BigNumber(100), new BigNumber(150), new BigNumber(200)];
+ const tuple1 = [array];
+ const tuple2 = [array, 'extra argument to prevent exactly matching the tuples'];
+ const args = [tuple1, tuple2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x18970a9e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Tuples, Nested in Separate Tuples', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuplesNestedInTuples);
+ const nestedTuple = ['Hello, World!'];
+ const tuple1 = [nestedTuple];
+ const tuple2 = [nestedTuple, 'extra argument to prevent exactly matching the tuples'];
+ const args = [tuple1, tuple2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x0b4d2e6a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Two-Dimensional Arrays', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateTwoDimensionalArrays);
+ const twoDimArray1 = [['Hello', 'World'], ['Foo', 'Bar', 'Zaa']];
+ const twoDimArray2 = twoDimArray1;
+ const args = [twoDimArray1, twoDimArray2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, { optimize: false });
+ const expectedOptimizedCalldata =
+ '0x0d28c4f9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Duplicate Array, Nested within Separate Two-Dimensional Arrays', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.duplicateTwoDimensionalArrays);
+ const twoDimArray1 = [['Hello', 'World'], ['Foo']];
+ const twoDimArray2 = [['Hello', 'World'], ['Bar']];
+ const args = [twoDimArray1, twoDimArray2];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x0d28c4f900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003466f6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034261720000000000000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Array Elements Duplicated as Tuple Fields', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsTupleFields);
+ const array = [new BigNumber(100), new BigNumber(150), new BigNumber(200), new BigNumber(225)];
+ const tuple = [[array[0]], [array[1]], [array[2]], [array[3]]];
+ const args = [array, tuple];
+ // Validata calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0x5b5c78fd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000e1';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Array Elements Duplicated as Separate Parameter', async () => {
+ // Generate calldata
+ const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsSeparateParameter);
+ const array = ['Hello', 'Hello', 'Hello', 'World'];
+ const str = 'Hello';
+ const args = [array, str];
+ // Validate calldata
+ const optimizedCalldata = method.encode(args, encodingRules);
+ const expectedOptimizedCalldata =
+ '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000';
+ expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
+ // Validate decoding
+ const decodedArgs = method.decode(optimizedCalldata);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+});
diff --git a/packages/utils/test/abi_encoder/return_values_test.ts b/packages/utils/test/abi_encoder/return_values_test.ts
new file mode 100644
index 000000000..a8cdd6ca3
--- /dev/null
+++ b/packages/utils/test/abi_encoder/return_values_test.ts
@@ -0,0 +1,67 @@
+import * as chai from 'chai';
+import 'mocha';
+
+import { AbiEncoder } from '../../src/';
+import { chaiSetup } from '../utils/chai_setup';
+
+import * as ReturnValueAbis from './abi_samples/return_value_abis';
+
+chaiSetup.configure();
+const expect = chai.expect;
+
+describe('ABI Encoder: Return Value Encoding/Decoding', () => {
+ const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately.
+ it('No Return Value', async () => {
+ // Decode return value
+ const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues);
+ const returnValue = '0x';
+ const decodedReturnValue = method.decodeReturnValues(returnValue);
+ const expectedDecodedReturnValue: any[] = [];
+ expect(decodedReturnValue).to.be.deep.equal(expectedDecodedReturnValue);
+ });
+ it('Single static return value', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue);
+ const returnValue = ['0x01020304'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Multiple static return values', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues);
+ const returnValue = ['0x01020304', '0x05060708'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Single dynamic return value', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue);
+ const returnValue = ['0x01020304'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Multiple dynamic return values', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues);
+ const returnValue = ['0x01020304', '0x05060708'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Mixed static/dynamic return values', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.mixedStaticAndDynamicReturnValues);
+ const returnValue = ['0x01020304', '0x05060708'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+});
diff --git a/packages/utils/test/utils/chai_setup.ts b/packages/utils/test/utils/chai_setup.ts
new file mode 100644
index 000000000..1a8733093
--- /dev/null
+++ b/packages/utils/test/utils/chai_setup.ts
@@ -0,0 +1,13 @@
+import * as chai from 'chai';
+import chaiAsPromised = require('chai-as-promised');
+import ChaiBigNumber = require('chai-bignumber');
+import * as dirtyChai from 'dirty-chai';
+
+export const chaiSetup = {
+ configure(): void {
+ chai.config.includeStack = true;
+ chai.use(ChaiBigNumber());
+ chai.use(dirtyChai);
+ chai.use(chaiAsPromised);
+ },
+};
diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json
index ad6902e33..9f5194e0d 100644
--- a/packages/web3-wrapper/CHANGELOG.json
+++ b/packages/web3-wrapper/CHANGELOG.json
@@ -6,7 +6,8 @@
"note": "Unmarshall mined transaction receipts",
"pr": 1308
}
- ]
+ ],
+ "timestamp": 1543401373
},
{
"version": "3.1.5",
diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md
index fa88eee2c..fffaf1d0a 100644
--- a/packages/web3-wrapper/CHANGELOG.md
+++ b/packages/web3-wrapper/CHANGELOG.md
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v3.1.6 - _November 28, 2018_
+
+ * Unmarshall mined transaction receipts (#1308)
+
## v3.1.5 - _November 21, 2018_
* Add unmarshalling of transaction receipts (#1291)
diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json
index 2eba35da7..218d85bfc 100644
--- a/packages/web3-wrapper/package.json
+++ b/packages/web3-wrapper/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/web3-wrapper",
- "version": "3.1.5",
+ "version": "3.1.6",
"engines": {
"node": ">=6.12"
},
diff --git a/packages/website/md/docs/smart_contracts/1/introduction.md b/packages/website/md/docs/smart_contracts/1/introduction.md
index 79a8f00fd..81715a3d1 100644
--- a/packages/website/md/docs/smart_contracts/1/introduction.md
+++ b/packages/website/md/docs/smart_contracts/1/introduction.md
@@ -1 +1 @@
-Welcome to the [0x smart contracts](https://github.com/0xProject/0x-monorepo/tree/development/packages/contracts) documentation! This documentation is intended for dApp developers who want to integrate 0x exchange functionality directly into their own smart contracts.
+Welcome to the [0x smart contracts](https://github.com/0xProject/0x-monorepo/tree/development/contracts/core) documentation! This documentation is intended for dApp developers who want to integrate 0x exchange functionality directly into their own smart contracts.
diff --git a/packages/website/md/docs/smart_contracts/2/introduction.md b/packages/website/md/docs/smart_contracts/2/introduction.md
index 4aa31db3d..0b4e2aac6 100644
--- a/packages/website/md/docs/smart_contracts/2/introduction.md
+++ b/packages/website/md/docs/smart_contracts/2/introduction.md
@@ -1,4 +1,4 @@
-Welcome to the [0x smart contracts](https://github.com/0xProject/0x-monorepo/tree/development/packages/contracts) documentation! This documentation is intended for dApp developers who want to integrate 0x exchange functionality directly into their own smart contracts.
+Welcome to the [0x smart contracts](https://github.com/0xProject/0x-monorepo/tree/development/contracts/core) documentation! This documentation is intended for dApp developers who want to integrate 0x exchange functionality directly into their own smart contracts.
### Helpful wiki articles:
diff --git a/packages/website/package.json b/packages/website/package.json
index 75e147168..52d5c8f96 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -1,6 +1,6 @@
{
"name": "@0x/website",
- "version": "0.0.60",
+ "version": "0.0.61",
"engines": {
"node": ">=6.12"
},
@@ -20,17 +20,17 @@
"author": "Fabio Berger",
"license": "Apache-2.0",
"dependencies": {
- "@0x/contract-wrappers": "^4.1.0",
+ "@0x/contract-wrappers": "^4.1.1",
"@0x/json-schemas": "^2.1.2",
- "@0x/order-utils": "^3.0.3",
- "@0x/react-docs": "^1.0.19",
- "@0x/react-shared": "^1.0.22",
- "@0x/subproviders": "^2.1.5",
+ "@0x/order-utils": "^3.0.4",
+ "@0x/react-docs": "^1.0.20",
+ "@0x/react-shared": "^1.0.23",
+ "@0x/subproviders": "^2.1.6",
"@0x/types": "^1.3.0",
"@0x/typescript-typings": "^3.0.4",
"@0x/utils": "^2.0.6",
- "@0x/web3-wrapper": "^3.1.5",
"@types/styled-components": "^4.1.1",
+ "@0x/web3-wrapper": "^3.1.6",
"accounting": "^0.4.1",
"basscss": "^8.0.3",
"blockies": "^0.0.2",
diff --git a/packages/website/public/images/instant/dai_screenshot.png b/packages/website/public/images/instant/dai_screenshot.png
new file mode 100644
index 000000000..02aefc909
--- /dev/null
+++ b/packages/website/public/images/instant/dai_screenshot.png
Binary files differ
diff --git a/packages/website/public/images/instant/feature_1.svg b/packages/website/public/images/instant/feature_1.svg
new file mode 100644
index 000000000..cca58d9b9
--- /dev/null
+++ b/packages/website/public/images/instant/feature_1.svg
@@ -0,0 +1,33 @@
+<svg width="343" height="127" viewBox="0 0 343 127" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="22" cy="101" r="22" fill="#0057FF"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9276 102.3C16.7747 102.3 17.5795 102.47 18.3419 102.81C19.0873 103.133 19.7396 103.576 20.2986 104.137C20.8577 104.698 21.2982 105.353 21.6201 106.101C21.959 106.867 22.1284 107.675 22.1284 108.525C22.1284 109.392 21.9675 110.2 21.6455 110.949C21.3067 111.697 20.8577 112.356 20.2986 112.926C19.7395 113.496 19.0873 113.942 18.3419 114.265C17.5795 114.588 16.7747 114.75 15.9276 114.75C15.0635 114.75 14.2589 114.588 13.5133 114.265C12.7679 113.942 12.1114 113.496 11.5438 112.926C10.9763 112.356 10.5315 111.697 10.2096 110.949C9.88772 110.2 9.72677 109.392 9.72677 108.525C9.72677 107.675 9.88772 106.867 10.2096 106.101C10.5315 105.353 10.9763 104.698 11.5438 104.137C12.1114 103.576 12.7679 103.125 13.5133 102.785C14.2588 102.462 15.0635 102.3 15.9276 102.3ZM15.9276 112.582C17.0289 112.582 17.9734 112.186 18.7612 111.395C19.549 110.604 19.9429 109.648 19.9429 108.525C19.9429 107.42 19.549 106.471 18.7612 105.68C17.9734 104.89 17.0288 104.494 15.9276 104.494C14.8094 104.494 13.8564 104.89 13.0686 105.68C12.2807 106.471 11.8868 107.42 11.8868 108.525C11.8868 109.648 12.2807 110.604 13.0686 111.395C13.8564 112.186 14.8093 112.582 15.9276 112.582ZM29.5492 88.625C30.4132 88.625 31.2179 88.7866 31.9634 89.1097C32.7089 89.4329 33.3654 89.8794 33.9329 90.4491C34.5005 91.0189 34.9452 91.678 35.2671 92.4263C35.5891 93.1747 35.75 93.9826 35.75 94.85C35.75 95.7004 35.5891 96.5084 35.2671 97.2737C34.9452 98.0221 34.5005 98.6769 33.9329 99.2381C33.3654 99.7994 32.7089 100.25 31.9634 100.59C31.218 100.913 30.4132 101.075 29.5492 101.075C28.7021 101.075 27.8973 100.913 27.1349 100.59C26.3894 100.25 25.7372 99.7994 25.1781 99.2381C24.6191 98.6768 24.1701 98.022 23.8312 97.2737C23.5093 96.5083 23.3484 95.7004 23.3484 94.85C23.3484 93.9826 23.5093 93.1748 23.8312 92.4263C24.1701 91.678 24.619 91.0189 25.1781 90.4491C25.7372 89.8794 26.3895 89.4329 27.1349 89.1097C27.8973 88.7866 28.7021 88.625 29.5492 88.625ZM29.5492 98.881C30.6674 98.881 31.6204 98.4856 32.4082 97.6947C33.1961 96.9038 33.59 95.9555 33.59 94.85C33.59 93.7275 33.1961 92.7707 32.4082 91.9798C31.6204 91.189 30.6675 90.7935 29.5492 90.7935C28.4479 90.7935 27.5034 91.1889 26.7156 91.9798C25.9278 92.7707 25.5339 93.7274 25.5339 94.85C25.5339 95.9556 25.9278 96.9038 26.7156 97.6947C27.5034 98.4856 28.448 98.881 29.5492 98.881ZM15.1398 88.625H22.2046V90.7936H20.629C21.0864 91.3379 21.4465 91.9502 21.7091 92.6305C21.9717 93.3108 22.103 94.0337 22.103 94.7991V94.8502C22.103 95.7176 21.9336 96.5254 21.5947 97.2738C21.2728 98.0392 20.8281 98.7068 20.2605 99.2766C19.6929 99.8464 19.0364 100.293 18.291 100.616C17.5286 100.939 16.7154 101.101 15.8513 101.101C14.9872 101.101 14.1825 100.939 13.437 100.616C12.6746 100.293 12.0139 99.8464 11.4548 99.2766C10.8957 98.7068 10.4467 98.0392 10.1079 97.2738C9.78595 96.5255 9.625 95.7176 9.625 94.8502C9.625 94.0337 9.78595 93.2684 10.1079 92.554C10.4298 91.8396 10.8491 91.2061 11.3658 90.6534C11.8826 90.1006 12.4713 89.6456 13.132 89.2885C13.7928 88.9313 14.462 88.7187 15.1397 88.6506L15.1398 88.625ZM19.9429 94.8501C19.9429 93.7276 19.549 92.7708 18.7611 91.9799C17.9733 91.189 17.0287 90.7936 15.9275 90.7936C14.8093 90.7936 13.8563 91.189 13.0685 91.9799C12.2807 92.7708 11.8868 93.7274 11.8868 94.8501C11.8868 95.9557 12.2807 96.9038 13.0685 97.6947C13.8563 98.4856 14.8092 98.8811 15.9275 98.8811C17.0288 98.8811 17.9733 98.4856 18.7611 97.6947C19.5489 96.9039 19.9429 95.9556 19.9429 94.8501Z" fill="white"/>
+<circle cx="75.5" cy="25.5" r="22.5" fill="#F08839"/>
+<path d="M56.8066 31.7309C61.6528 31.5578 71.2413 31.7309 72.9028 33.8078C74.9797 36.404 62.5182 39.5193 72.9028 40.5578C80.4452 41.312 83.4605 43.327 83.8066 44.1924" stroke="white" stroke-width="1.73077"/>
+<circle cx="75.4997" cy="25.5002" r="19.9038" stroke="white" stroke-width="1.73077"/>
+<circle cx="72.039" cy="13.3847" r="4.32692" stroke="white" stroke-width="1.73077"/>
+<path d="M56.2891 26.0186C56.6352 26.3647 57.4314 26.7455 57.8468 25.4993C58.2621 24.2532 60.0968 21.5186 60.9621 20.307H64.0775L66.1544 26.0186C67.1929 25.8455 69.3737 25.4993 69.7891 25.4993C70.3083 25.4993 71.3468 26.0186 71.866 26.0186M71.866 26.0186C72.3852 26.0186 73.9429 23.9416 74.9814 23.9416C76.0198 23.9416 79.1352 26.0186 79.1352 25.4993C79.1352 24.9801 81.7314 22.3839 82.2506 21.3455C82.666 20.5147 83.8083 19.9609 84.3275 19.7878C84.8468 19.9609 85.8852 20.2032 85.8852 19.7878C85.8852 19.2686 86.4044 18.7493 87.4429 20.307C88.4814 21.8647 90.0391 23.9416 90.5583 23.9416C91.0775 23.9416 91.5968 23.9416 92.116 24.4609C92.5314 24.8762 94.0198 26.3647 94.7121 27.057L95.5 28M71.866 26.0186L70.3083 29.1339" stroke="white" stroke-width="1.73077"/>
+<path d="M85.885 33.9376C85.885 35.2995 84.9901 35.7159 83.7215 35.8374C83.3207 35.8758 82.8825 35.8847 82.4235 35.8847C81.9644 35.8847 81.5262 35.8758 81.1254 35.8374C79.8568 35.7159 78.9619 35.2995 78.9619 33.9376C78.9619 32.1453 80.5117 30.6924 82.4235 30.6924C84.3352 30.6924 85.885 32.1453 85.885 33.9376Z" stroke="white" stroke-width="1.73077"/>
+<path d="M91.0771 36.3173C91.0771 37.2252 90.4059 37.5028 89.4545 37.5838C89.1538 37.6094 88.8252 37.6153 88.4809 37.6153C88.1366 37.6153 87.808 37.6094 87.5074 37.5838C86.5559 37.5028 85.8848 37.2252 85.8848 36.3173C85.8848 35.1224 87.0471 34.1538 88.4809 34.1538C89.9147 34.1538 91.0771 35.1224 91.0771 36.3173Z" stroke="white" stroke-width="1.73077"/>
+<circle cx="322.5" cy="98.5" r="20.5" fill="#F2B350"/>
+<rect x="322.499" y="88.1144" width="14.6871" height="14.6871" transform="rotate(45 322.499 88.1144)" stroke="white" stroke-width="3.15385"/>
+<rect x="322.499" y="94.2949" width="5.94697" height="5.94697" transform="rotate(45 322.499 94.2949)" fill="white"/>
+<path d="M335.115 98.4998L322.499 111.115L309.884 98.4998H335.115Z" fill="white"/>
+<circle cx="274" cy="26" r="21" fill="#9E19A0"/>
+<path d="M268.973 27.3501C268.829 25.8594 270.406 23.8901 272.06 23.1905C273.715 22.4909 274.124 20.8111 274.124 20.8111L273.999 12.269" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/>
+<path d="M274.125 40.027V36.6055C274.125 36.6055 273.637 34.5659 275.292 33.8663C276.253 33.4599 277.23 33.8663 278.547 31.6665" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/>
+<path d="M262.567 32.8566L265.863 30.707C265.863 30.707 268.146 29.9997 269.476 31.197C270.368 32.0011 270.625 32.9673 272.061 33.1987" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/>
+<path d="M285.929 32.3543C285.929 32.3543 282.656 31.2176 281.559 30.5071C280.461 29.7966 279.769 29.6631 279.169 27.1944C278.751 25.4729 277.422 24.0452 275.938 23.9409" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M275.494 12.3748C274.88 11.2696 273.293 11.2649 272.673 12.3664L261.859 31.589C261.426 32.3596 261.692 33.3356 262.457 33.7788L273.307 40.0648C273.812 40.3574 274.436 40.3548 274.939 40.0579L285.57 33.7788C286.323 33.3338 286.585 32.3696 286.161 31.6043L275.494 12.3748Z" stroke="white" stroke-width="1.61538" stroke-miterlimit="10" stroke-linecap="round"/>
+<circle cx="119.5" cy="102.5" r="19.5" fill="#68CCBB"/>
+<path d="M131.499 109.423V96.4901C131.499 95.8739 130.797 95.5206 130.302 95.8878L122.469 101.698C122.278 101.84 122.166 102.063 122.166 102.301V110" stroke="white" stroke-width="2.25" stroke-linecap="round"/>
+<path d="M107.501 109.423V96.4901C107.501 95.8739 108.203 95.5206 108.698 95.8878L116.531 101.698C116.722 101.84 116.834 102.063 116.834 102.301V110" stroke="white" stroke-width="2.25" stroke-linecap="round"/>
+<path d="M222.5 125.557C224.047 126.45 225.953 126.45 227.5 125.557L245.883 114.943C247.43 114.05 248.383 112.4 248.383 110.613V89.3867C248.383 87.6004 247.43 85.9498 245.883 85.0566L227.5 74.4434C225.953 73.5502 224.047 73.5502 222.5 74.4434L204.117 85.0566C202.57 85.9498 201.617 87.6004 201.617 89.3867V110.613C201.617 112.4 202.57 114.05 204.117 114.943L222.5 125.557Z" fill="#FF5D3A"/>
+<path d="M213.397 96.7922L213.829 88L219.337 91.1811C223.063 89.3156 227.01 89.296 230.751 91.132L236.195 88V96.4241C236.985 97.9655 237.397 99.669 237.397 101.402C237.378 107.828 231.781 113.037 224.874 113.037C217.967 113.037 212.371 107.813 212.371 101.402C212.371 99.8064 212.719 98.2355 213.397 96.7922Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M232.003 99.3155C231.551 98.1177 229.632 98.2993 229.151 99.3155" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M221.738 99.3155C221.287 98.1177 219.367 98.2993 218.886 99.3155" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M226.951 103.307H223.942" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M225.443 103.307V104.809" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M226.734 108.471C226.327 108.746 225.34 109.203 223.941 108.52" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M174.169 1.04473C172.798 0.384526 171.202 0.384527 169.831 1.04473L153.06 9.12104C151.689 9.78124 150.693 11.0298 150.355 12.5133L146.213 30.6606C145.874 32.144 146.23 33.701 147.178 34.8906L158.784 49.4436C159.733 50.6333 161.171 51.3262 162.693 51.3262L181.307 51.3262C182.829 51.3262 184.267 50.6333 185.216 49.4436L196.822 34.8906C197.77 33.701 198.126 32.144 197.787 30.6606L193.645 12.5133C193.307 11.0298 192.311 9.78125 190.94 9.12104L174.169 1.04473Z" fill="#3AC4FF"/>
+<path d="M161 22.12V37H169.186M161 22.12H183M161 22.12L171.744 13L183 22.12M183 22.12V37H175.326M175.326 37V29.32H169.186V37M175.326 37H169.186" stroke="white" stroke-width="2"/>
+</svg>
diff --git a/packages/website/public/images/instant/feature_2.svg b/packages/website/public/images/instant/feature_2.svg
new file mode 100644
index 000000000..a313872b2
--- /dev/null
+++ b/packages/website/public/images/instant/feature_2.svg
@@ -0,0 +1,15 @@
+<svg width="209" height="112" viewBox="0 0 209 112" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="2" y="32" width="178.035" height="77.5613" rx="3.29952" fill="#8397DE" stroke="#5265AA" stroke-width="4"/>
+<path d="M121.491 69.9311C121.491 88.7841 107.718 103.915 90.9058 103.915C74.0938 103.915 60.3203 88.7841 60.3203 69.9311C60.3203 51.0782 74.0938 35.9473 90.9058 35.9473C107.718 35.9473 121.491 51.0782 121.491 69.9311Z" fill="#6E80BF" stroke="#6E80BF" stroke-width="1.69919"/>
+<path d="M172.468 50.39C172.468 55.6225 168.596 59.7356 163.972 59.7356C159.347 59.7356 155.476 55.6225 155.476 50.39C155.476 45.1575 159.347 41.0444 163.972 41.0444C168.596 41.0444 172.468 45.1575 172.468 50.39Z" fill="#6E80C0" stroke="#8397DE" stroke-width="1.69919"/>
+<path d="M172.468 93.1903C172.468 98.4228 168.596 102.536 163.972 102.536C159.347 102.536 155.476 98.4228 155.476 93.1903C155.476 87.9578 159.347 83.8447 163.972 83.8447C168.596 83.8447 172.468 87.9578 172.468 93.1903Z" fill="#6E80C0" stroke="#8397DE" stroke-width="1.69919"/>
+<path d="M26.3171 49.4691C26.3171 54.7016 22.4453 58.8147 17.8212 58.8147C13.197 58.8147 9.32518 54.7016 9.32518 49.4691C9.32518 44.2366 13.197 40.1235 17.8212 40.1235C22.4453 40.1235 26.3171 44.2366 26.3171 49.4691Z" fill="#6E80C0" stroke="#8397DE" stroke-width="1.69919"/>
+<path d="M26.3367 92.87C26.3367 98.1025 22.4648 102.216 17.8407 102.216C13.2166 102.216 9.34471 98.1025 9.34471 92.87C9.34471 87.6375 13.2166 83.5244 17.8407 83.5244C22.4648 83.5244 26.3367 87.6375 26.3367 92.87Z" fill="#6E80C0" stroke="#8397DE" stroke-width="1.69919"/>
+<path d="M105.329 70.7255L105.329 70.7256L93.111 77.9472L93.1106 77.9474C92.199 78.4868 91.0649 78.4871 90.1516 77.9472L90.1515 77.9471L77.9287 70.7256C76.537 69.903 76.0828 68.1043 76.9171 66.721L76.9172 66.7208L89.1402 46.4409L89.1402 46.4408C90.2706 44.5646 92.9907 44.5646 94.1211 46.4408L106.339 66.7207L106.339 66.7208C107.174 68.1058 106.719 69.9044 105.329 70.7255Z" fill="#5265AA" stroke="#5265AA" stroke-width="1.69919"/>
+<path d="M93.4911 93.863L93.4911 93.863C92.5828 95.1423 90.6834 95.1423 89.7751 93.863L77.2355 76.1985L90.7948 84.2098C90.7952 84.21 90.7955 84.2102 90.7959 84.2104C91.3117 84.5159 91.9534 84.5158 92.4693 84.2103C92.4696 84.2101 92.4699 84.2099 92.4702 84.2098L106.035 76.1981L93.4911 93.863Z" fill="#5265AA" stroke="#5265AA" stroke-width="1.69919"/>
+<circle cx="177" cy="32" r="32" fill="#9AA7D7"/>
+<g opacity="0.7">
+<line x1="195.064" y1="32.5152" x2="158.935" y2="32.5152" stroke="white" stroke-width="5.16129" stroke-linecap="round"/>
+<line x1="177.516" y1="50.0644" x2="177.516" y2="13.9353" stroke="white" stroke-width="5.16129" stroke-linecap="round"/>
+</g>
+</svg>
diff --git a/packages/website/public/images/instant/feature_3.svg b/packages/website/public/images/instant/feature_3.svg
new file mode 100644
index 000000000..83aa259c6
--- /dev/null
+++ b/packages/website/public/images/instant/feature_3.svg
@@ -0,0 +1,195 @@
+<svg width="312" height="238" viewBox="0 0 312 238" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0)">
+<path d="M4.54049 64.6748L115.492 0.617544C117.998 -0.826333 120.032 0.345114 120.032 3.24195V162.804C120.032 165.701 117.998 169.215 115.492 170.668L4.54049 234.716C2.03414 236.16 0 234.989 0 232.092V72.539C0 69.6421 2.03414 66.1278 4.54049 64.6748Z" fill="#363636"/>
+<path d="M4.54049 64.6748L115.492 0.617544C117.998 -0.826333 120.032 0.345114 120.032 3.24195V43.6342L0 112.931V72.539C0 69.6421 2.03414 66.1278 4.54049 64.6748Z" fill="#363636"/>
+<path d="M14.8197 76.8164L13.5393 77.552C13.4667 77.7064 12.9854 79.577 11.4053 80.4851V81.8745C12.4768 81.257 13.2124 80.3943 13.3668 79.9675L13.4394 79.9221V88.6126L14.8106 87.8226V76.8164H14.8197ZM24.0551 78.9414L22.7111 79.7133V72.2578L21.2037 73.1295L17.0627 83.3366V84.4354L21.4034 81.929V84.0176L22.7111 83.2639V81.1753L24.0551 80.4034V78.9414ZM18.7245 82.0198L21.2037 75.763L21.4034 75.1001V80.467L18.7245 82.0198ZM31.7104 71.84C31.7104 68.6526 30.5026 67.6174 28.4775 68.7797C26.4343 69.9603 25.2175 72.4031 25.2175 75.5905V76.8891C25.2175 80.1401 26.4525 81.2661 28.4957 80.0856C30.5208 78.9141 31.7194 76.3896 31.7194 73.1386V71.84H31.7104ZM30.2483 74.2465C30.2483 76.2352 29.6399 77.9606 28.4866 78.6326C27.3061 79.3137 26.6704 78.3057 26.6704 76.317V74.4826C26.6704 72.5393 27.297 70.9138 28.4775 70.2327C29.6399 69.5607 30.2574 70.4597 30.2574 72.4121V74.2465H30.2483ZM33.8353 76.9526C34.3529 76.653 34.7797 75.9265 34.7797 75.3181C34.7797 74.7278 34.3529 74.4917 33.8353 74.7914C33.3268 75.082 32.8909 75.7993 32.8909 76.4078C32.9 77.0162 33.3177 77.2614 33.8353 76.9526ZM42.3079 65.7195C42.3079 62.532 41.1001 61.4968 39.075 62.6592C37.0318 63.8397 35.815 66.2825 35.815 69.4699V70.7685C35.815 74.0195 37.05 75.1455 39.0932 73.965C41.1183 72.7935 42.317 70.269 42.317 67.018V65.7195H42.3079ZM40.8549 68.1259C40.8549 70.1146 40.2465 71.84 39.0932 72.512C37.9127 73.1931 37.277 72.1851 37.277 70.1964V68.362C37.277 66.4187 37.9036 64.7932 39.0841 64.1121C40.2465 63.4401 40.864 64.3391 40.864 66.2916V68.1259H40.8549ZM50.1811 61.1699C50.1811 57.9825 48.9733 56.9472 46.9483 58.1096C44.905 59.2901 43.6882 61.7329 43.6882 64.9203V66.2189C43.6882 69.4699 44.9232 70.5959 46.9664 69.4154C48.9915 68.244 50.1902 65.7195 50.1902 62.4685V61.1699H50.1811ZM48.7281 63.5763C48.7281 65.5651 48.1197 67.2905 46.9664 67.9625C45.7859 68.6435 45.1502 67.6355 45.1502 65.6468V63.8124C45.1502 61.8691 45.7768 60.2436 46.9573 59.5625C48.1197 58.8905 48.7372 59.7896 48.7372 61.742V63.5763H48.7281ZM62.0772 59.0812L57.0826 61.969L62.0409 50.6813V49.5553L55.2574 53.4692V54.9312L60.2519 52.0526L55.2937 63.3402V64.4663L62.0772 60.5524V59.0812ZM69.1059 56.4841L70.7768 55.5215L68.7517 52.1434C69.7869 51.0082 70.4226 49.637 70.4226 48.1205C70.4226 46.1862 69.4146 45.3054 67.1535 46.6131L63.8116 48.5382V59.5535L65.2919 58.6998V54.4863L67.2806 53.3421L69.1059 56.4841ZM65.2828 49.1466L67.1444 48.0751C68.5428 47.2669 68.9606 47.9752 68.9606 48.9832C68.9606 50.1274 68.443 51.1808 67.0536 51.9799L65.2828 53.0061V49.1466ZM73.2105 43.1078L71.5033 44.0885L74.4818 47.8753L71.4851 55.1129L73.2014 54.123L75.4353 48.62L75.508 48.5745L77.7419 51.4986L79.4582 50.5088L76.5069 46.7039L79.44 39.5117L77.7328 40.4925L75.508 46.041L75.4353 46.0864L73.2105 43.1078Z" fill="#363636"/>
+<path d="M10.724 199.291L109.716 142.145C110.833 141.5 111.732 142.027 111.732 143.307V160.443C111.732 161.732 110.833 163.294 109.716 163.939L10.724 221.086C9.60703 221.73 8.70801 221.204 8.70801 219.923V202.788C8.70801 201.498 9.61611 199.936 10.724 199.291Z" fill="#363636"/>
+<path opacity="0.2" d="M10.5881 121.159L109.435 64.0936C109.989 63.7758 110.443 64.0301 110.443 64.6748V78.6505C110.443 79.2952 109.989 80.0762 109.435 80.394L10.5881 137.468C10.0341 137.786 9.58008 137.532 9.58008 136.887V122.911C9.58008 122.266 10.0341 121.485 10.5881 121.159Z" fill="#363636"/>
+<path opacity="0.2" d="M9.7168 183.272L107.583 126.771" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+<path opacity="0.2" d="M9.7168 167.835L107.583 111.333" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+<path opacity="0.2" d="M9.7168 153.305L107.583 96.8032" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+<path d="M6.56588 66.4912L117.517 2.43395C120.024 0.990073 122.058 2.16152 122.058 5.05836V164.62C122.058 167.517 120.024 171.032 117.517 172.485L6.56588 236.533C4.05953 237.977 2.02539 236.805 2.02539 233.908V74.3554C2.02539 71.4585 4.05953 67.9442 6.56588 66.4912Z" fill="#424242"/>
+<path d="M85.7878 33.0822C85.7878 35.1708 85.1159 36.7237 83.9081 37.4229C82.7003 38.1222 82.0283 37.3503 82.0283 35.2526C82.0283 33.1549 82.7003 31.602 83.9081 30.9028C85.1159 30.2035 85.7878 30.9845 85.7878 33.0822ZM84.8888 32.2377C84.7254 31.5929 84.3894 31.4022 83.9081 31.6837C83.1816 32.1015 82.7911 33.1912 82.7911 34.8167C82.7911 35.0074 82.7911 35.1799 82.8093 35.3434L84.8888 32.2377ZM82.9183 36.1153C83.0908 36.7509 83.4177 36.9416 83.9081 36.6601C84.6346 36.2424 85.016 35.1527 85.016 33.5272C85.016 33.3365 85.016 33.1549 84.9978 32.9914L82.9183 36.1153Z" fill="#FF602E"/>
+<path d="M87.9219 34.1629C87.9219 33.6998 88.2397 33.1277 88.6393 32.9006C89.0388 32.6736 89.3567 32.8643 89.3567 33.3365C89.3567 33.7997 89.0388 34.3718 88.6393 34.5988C88.2397 34.8258 87.9219 34.626 87.9219 34.1629Z" fill="#FF602E"/>
+<path d="M93.4165 31.9291C92.3086 32.5738 91.5186 32.1289 91.5186 30.8485C91.5186 30.1129 91.7365 29.3138 92.2632 28.1423C92.3449 27.9425 93.3802 25.6905 93.4528 25.5452L94.3427 25.0366L92.9079 28.1332C92.7445 28.4965 92.7445 28.5056 92.7082 28.6054L92.7717 28.5964C92.9443 28.2513 93.3166 27.8608 93.6617 27.661C94.6243 27.1071 95.3326 27.543 95.3326 28.6781C95.3144 29.9131 94.5153 31.2934 93.4165 31.9291ZM94.5516 29.1594C94.5516 28.3966 94.0612 28.106 93.4165 28.4783C92.7717 28.8506 92.2814 29.7133 92.2814 30.4761C92.2814 31.2389 92.7717 31.5295 93.4165 31.1572C94.0612 30.7758 94.5516 29.9131 94.5516 29.1594Z" fill="#FF602E"/>
+<path d="M100.017 24.8641C100.017 26.9527 99.3453 28.5056 98.1376 29.2048C96.9298 29.904 96.2578 29.1321 96.2578 27.0344C96.2578 24.9367 96.9298 23.3839 98.1376 22.6846C99.3453 21.9945 100.017 22.7664 100.017 24.8641ZM99.1183 24.0195C98.9549 23.3748 98.6189 23.1841 98.1285 23.4656C97.402 23.8833 97.0115 24.973 97.0115 26.5985C97.0115 26.7892 97.0115 26.9618 97.0297 27.1252L99.1183 24.0195ZM97.1478 27.8971C97.3203 28.5328 97.6472 28.7235 98.1376 28.442C98.8641 28.0243 99.2455 26.9345 99.2455 25.309C99.2455 25.1183 99.2455 24.9367 99.2273 24.7733L97.1478 27.8971Z" fill="#FF602E"/>
+<path d="M85.7878 44.3337C85.7878 46.4223 85.1159 47.9751 83.9081 48.6744C82.7003 49.3736 82.0283 48.6017 82.0283 46.504C82.0283 44.4063 82.7003 42.8535 83.9081 42.1542C85.1159 41.455 85.7878 42.236 85.7878 44.3337ZM84.8888 43.4801C84.7254 42.8353 84.3894 42.6446 83.9081 42.9261C83.1816 43.3438 82.7911 44.4336 82.7911 46.0591C82.7911 46.2498 82.7911 46.4223 82.8093 46.5858L84.8888 43.4801ZM82.9183 47.3576C83.0908 47.9933 83.4177 48.184 83.9081 47.9025C84.6346 47.4848 85.016 46.3951 85.016 44.7696C85.016 44.5789 85.016 44.3972 84.9978 44.2338L82.9183 47.3576Z" fill="#FF602E"/>
+<path d="M87.9219 45.4051C87.9219 44.942 88.2397 44.3699 88.6393 44.1428C89.0388 43.9158 89.3567 44.1065 89.3567 44.5787C89.3567 45.0419 89.0388 45.614 88.6393 45.841C88.2397 46.068 87.9219 45.8682 87.9219 45.4051Z" fill="#FF602E"/>
+<path d="M91.5273 42.4452L92.272 42.0093C92.3356 42.5814 92.7805 42.7448 93.3799 42.3997C94.0428 42.0183 94.4877 41.2283 94.4877 40.4201C94.4877 39.6119 94.0428 39.3213 93.3799 39.7027C92.9258 39.966 92.5535 40.4201 92.3446 40.965L91.6 41.3918L91.8633 37.6958L94.9145 35.9341V36.715L92.4808 38.1226L92.3356 40.1386L92.3991 40.1023C92.6534 39.5756 93.0439 39.1578 93.5252 38.8763C94.5241 38.2951 95.2233 38.7492 95.2233 39.9751C95.2233 41.2555 94.4605 42.5632 93.3345 43.217C92.3083 43.8073 91.5727 43.4985 91.5273 42.4452Z" fill="#FF602E"/>
+<path d="M98.1284 33.9542C99.2363 33.3094 100.026 33.7544 100.026 35.0348C100.026 35.7522 99.8084 36.5514 99.2726 37.75C99.1818 37.9589 98.1466 40.211 98.083 40.3472L97.1931 40.8557L98.6278 37.7591C98.755 37.4776 98.7913 37.3868 98.8276 37.296L98.7641 37.3051C98.5915 37.6502 98.2283 38.0316 97.8923 38.2313C96.9297 38.7853 96.2305 38.3585 96.2305 37.2052C96.2305 35.9611 97.0296 34.5899 98.1284 33.9542ZM96.9933 36.7239C96.9933 37.4867 97.4836 37.7773 98.1284 37.405C98.7731 37.0326 99.2635 36.17 99.2635 35.4071C99.2635 34.6443 98.7731 34.3538 98.1284 34.7261C97.4836 35.0984 96.9933 35.9611 96.9933 36.7239Z" fill="#FF602E"/>
+<path d="M85.7878 56.5112C85.7878 58.5998 85.1159 60.1527 83.9081 60.8519C82.7003 61.5511 82.0283 60.7793 82.0283 58.6816C82.0283 56.5838 82.7003 55.031 83.9081 54.3318C85.1159 53.6325 85.7878 54.4226 85.7878 56.5112ZM84.8888 55.6667C84.7254 55.0219 84.3894 54.8312 83.9081 55.1127C83.1816 55.5304 82.7911 56.6202 82.7911 58.2457C82.7911 58.4364 82.7911 58.6089 82.8093 58.7724L84.8888 55.6667ZM82.9183 59.5442C83.0908 60.1799 83.4177 60.3706 83.9081 60.0891C84.6346 59.6714 85.016 58.5817 85.016 56.9562C85.016 56.7655 85.016 56.5838 84.9978 56.4204L82.9183 59.5442Z" fill="#FF602E"/>
+<path d="M87.9219 57.5916C87.9219 57.1285 88.2397 56.5564 88.6393 56.3294C89.0388 56.1023 89.3567 56.293 89.3567 56.7652C89.3567 57.2284 89.0388 57.8005 88.6393 58.0275C88.2397 58.2545 87.9219 58.0547 87.9219 57.5916Z" fill="#FF602E"/>
+<path d="M91.5273 54.6317L92.272 54.1958C92.3356 54.7679 92.7805 54.9313 93.3799 54.5863C94.0428 54.2049 94.4877 53.4148 94.4877 52.6066C94.4877 51.7984 94.0428 51.5078 93.3799 51.8892C92.9258 52.1526 92.5535 52.6066 92.3446 53.1515L91.6091 53.5783L91.8724 49.8823L94.9236 48.1206V48.9016L92.4899 50.3091L92.3446 52.3251L92.4082 52.2888C92.6625 51.7621 93.0529 51.3444 93.5342 51.0628C94.5331 50.4817 95.2324 50.9357 95.2324 52.1616C95.2324 53.4421 94.4696 54.7497 93.3435 55.4036C92.3083 55.9938 91.5727 55.6851 91.5273 54.6317Z" fill="#FF602E"/>
+<path d="M96.1768 51.9617C96.1768 51.1898 96.6126 50.3089 97.2937 49.7277V49.6551C96.7398 49.8004 96.3947 49.4644 96.3947 48.7924C96.3947 47.8116 97.1212 46.7038 98.1473 46.1135C99.1735 45.5232 99.9 45.7957 99.9 46.7673C99.9 47.4393 99.5549 48.1658 98.9919 48.6653V48.7379C99.6639 48.5109 100.118 48.9014 100.118 49.6732C100.118 50.7448 99.3097 51.9526 98.1473 52.6246C96.985 53.3056 96.1768 53.0332 96.1768 51.9617ZM99.3279 50.0728C99.3279 49.3917 98.8466 49.201 98.1564 49.6097C97.4663 50.0183 96.985 50.7539 96.985 51.435C96.985 52.116 97.4663 52.3067 98.1564 51.8981C98.8466 51.4894 99.3279 50.7448 99.3279 50.0728ZM99.1372 47.294C99.1372 46.6856 98.7376 46.5131 98.1473 46.8491C97.548 47.1941 97.1575 47.8298 97.1575 48.4291C97.1575 49.0376 97.548 49.2192 98.1473 48.8741C98.7376 48.5381 99.1372 47.9025 99.1372 47.294Z" fill="#FF602E"/>
+<path d="M85.7878 68.2712C85.7878 70.3598 85.1159 71.9126 83.9081 72.6119C82.7003 73.3111 82.0283 72.5392 82.0283 70.4415C82.0283 68.3438 82.7003 66.791 83.9081 66.0917C85.1159 65.3925 85.7878 66.1735 85.7878 68.2712ZM84.8888 67.4266C84.7254 66.7819 84.3894 66.5912 83.9081 66.8727C83.1816 67.2904 82.7911 68.3801 82.7911 70.0056C82.7911 70.1963 82.7911 70.3689 82.8093 70.5323L84.8888 67.4266ZM82.9183 71.2951C83.0908 71.9308 83.4177 72.1215 83.9081 71.84C84.6346 71.4223 85.016 70.3326 85.016 68.7071C85.016 68.5164 85.016 68.3347 84.9978 68.1713L82.9183 71.2951Z" fill="#FF602E"/>
+<path d="M87.9219 69.3519C87.9219 68.8887 88.2397 68.3166 88.6393 68.0896C89.0388 67.8626 89.3567 68.0533 89.3567 68.5255C89.3567 68.9886 89.0388 69.5607 88.6393 69.7878C88.2397 70.0148 87.9219 69.815 87.9219 69.3519Z" fill="#FF602E"/>
+<path d="M91.5273 66.3822L92.272 65.9463C92.3356 66.5184 92.7805 66.6818 93.3799 66.3368C94.0428 65.9554 94.4877 65.1653 94.4877 64.3571C94.4877 63.5489 94.0428 63.2583 93.3799 63.6397C92.9258 63.9031 92.5535 64.3571 92.3446 64.902L91.6 65.3288L91.8633 61.6328L94.9145 59.8711V60.6521L92.4808 62.0596L92.3356 64.0756L92.3991 64.0393C92.6534 63.5126 93.0439 63.0948 93.5252 62.8133C94.5241 62.2321 95.2233 62.6862 95.2233 63.9121C95.2233 65.1926 94.4605 66.5002 93.3345 67.154C92.3083 67.7443 91.5727 67.4356 91.5273 66.3822Z" fill="#FF602E"/>
+<path d="M99.2097 58.2366V58.173L96.3311 59.8348V59.0629L100.009 56.938V57.728L97.6206 64.5569L96.7942 65.0291L99.2097 58.2366Z" fill="#FF602E"/>
+<path d="M85.7878 79.5226C85.7878 81.6113 85.1159 83.1641 83.9081 83.8633C82.7003 84.5626 82.0283 83.7907 82.0283 81.693C82.0283 79.5953 82.7003 78.0424 83.9081 77.3432C85.1159 76.644 85.7878 77.4249 85.7878 79.5226ZM84.8888 78.669C84.7254 78.0243 84.3894 77.8336 83.9081 78.1151C83.1816 78.5328 82.7911 79.6225 82.7911 81.248C82.7911 81.4387 82.7911 81.6113 82.8093 81.7747L84.8888 78.669ZM82.9183 82.5466C83.0908 83.1823 83.4177 83.373 83.9081 83.0915C84.6346 82.6737 85.016 81.584 85.016 79.9585C85.016 79.7678 85.016 79.5862 84.9978 79.4227L82.9183 82.5466Z" fill="#FF602E"/>
+<path d="M87.9219 80.5936C87.9219 80.1304 88.2397 79.5583 88.6393 79.3313C89.0388 79.1043 89.3567 79.295 89.3567 79.7672C89.3567 80.2303 89.0388 80.8024 88.6393 81.0295C88.2397 81.2565 87.9219 81.0567 87.9219 80.5936Z" fill="#FF602E"/>
+<path d="M91.5273 77.6336L92.272 77.1977C92.3356 77.7698 92.7805 77.9333 93.3799 77.5882C94.0428 77.2068 94.4877 76.4168 94.4877 75.6086C94.4877 74.8004 94.0428 74.5098 93.3799 74.8912C92.9258 75.1545 92.5535 75.6086 92.3446 76.1534L91.6 76.5802L91.8633 72.8843L94.9145 71.1226V71.9035L92.4808 73.3111L92.3356 75.3271L92.3991 75.2907C92.6534 74.764 93.0439 74.3463 93.5252 74.0648C94.5241 73.4836 95.2233 73.9377 95.2233 75.1636C95.2233 76.444 94.4605 77.7517 93.3345 78.4055C92.3083 78.9958 91.5727 78.687 91.5273 77.6336Z" fill="#FF602E"/>
+<path d="M98.1645 75.6176C97.0566 76.2623 96.2666 75.8174 96.2666 74.5369C96.2666 73.8014 96.4845 73.0022 97.0112 71.8308C97.1021 71.631 98.1282 69.3789 98.2008 69.2336L99.0908 68.7251L97.656 71.8217C97.4925 72.185 97.4925 72.194 97.4562 72.2939L97.5198 72.2848C97.6923 71.9398 98.0646 71.5493 98.4097 71.3495C99.3723 70.7956 100.081 71.2315 100.081 72.3666C100.053 73.6107 99.2542 74.991 98.1645 75.6176ZM99.2906 72.8479C99.2906 72.0851 98.8002 71.7945 98.1554 72.1668C97.5107 72.5391 97.0203 73.4018 97.0203 74.1646C97.0203 74.9274 97.5107 75.218 98.1554 74.8457C98.8002 74.4734 99.2906 73.6107 99.2906 72.8479Z" fill="#FF602E"/>
+<path d="M85.7878 91.6999C85.7878 93.7885 85.1159 95.3414 83.9081 96.0406C82.7003 96.7398 82.0283 95.9679 82.0283 93.8702C82.0283 91.7725 82.7003 90.2197 83.9081 89.5204C85.1159 88.8212 85.7878 89.6022 85.7878 91.6999ZM84.8888 90.8553C84.7254 90.2106 84.3894 90.0199 83.9081 90.3014C83.1816 90.7191 82.7911 91.8088 82.7911 93.4343C82.7911 93.625 82.7911 93.7976 82.8093 93.961L84.8888 90.8553ZM82.9183 94.7329C83.0908 95.3686 83.4177 95.5593 83.9081 95.2778C84.6346 94.8601 85.016 93.7703 85.016 92.1448C85.016 91.9541 85.016 91.7725 84.9978 91.6091L82.9183 94.7329Z" fill="#FF602E"/>
+<path d="M87.9219 92.7806C87.9219 92.3174 88.2397 91.7453 88.6393 91.5183C89.0388 91.2913 89.3567 91.482 89.3567 91.9542C89.3567 92.4173 89.0388 92.9894 88.6393 93.2165C88.2397 93.4435 87.9219 93.2437 87.9219 92.7806Z" fill="#FF602E"/>
+<path d="M91.5273 89.8202L92.272 89.3843C92.3356 89.9564 92.7805 90.1198 93.3799 89.7747C94.0428 89.3933 94.4877 88.6033 94.4877 87.7951C94.4877 86.9869 94.0428 86.6963 93.3799 87.0777C92.9258 87.341 92.5535 87.7951 92.3446 88.34L91.6 88.7668L91.8633 85.0708L94.9145 83.3091V84.09L92.4808 85.4976L92.3356 87.5136L92.3991 87.4773C92.6534 86.9506 93.0439 86.5328 93.5252 86.2513C94.5241 85.6701 95.2233 86.1242 95.2233 87.3501C95.2233 88.6305 94.4605 89.9382 93.3345 90.592C92.3083 91.1823 91.5727 90.8736 91.5273 89.8202Z" fill="#FF602E"/>
+<path d="M96.2666 87.0775L97.0112 86.6416C97.0748 87.2137 97.5198 87.3771 98.1191 87.0321C98.782 86.6507 99.227 85.8606 99.227 85.0524C99.227 84.2442 98.782 83.9536 98.1191 84.335C97.6651 84.5984 97.2928 85.0524 97.0839 85.5973L96.3392 86.0241L96.6026 82.3281L99.6538 80.5664V81.3474L97.2201 82.7549L97.0748 84.7709L97.1384 84.7346C97.3926 84.2079 97.7831 83.7902 98.2644 83.5086C99.2633 82.9275 99.9626 83.3815 99.9626 84.6074C99.9626 85.8879 99.1998 87.1955 98.0737 87.8494C97.0476 88.4396 96.312 88.1309 96.2666 87.0775Z" fill="#FF602E"/>
+<path d="M85.7878 114.238C85.7878 116.327 85.1159 117.88 83.9081 118.579C82.7003 119.278 82.0283 118.507 82.0283 116.409C82.0283 114.311 82.7003 112.758 83.9081 112.059C85.1159 111.36 85.7878 112.141 85.7878 114.238ZM84.8888 113.394C84.7254 112.749 84.3894 112.558 83.9081 112.84C83.1816 113.258 82.7911 114.347 82.7911 115.973C82.7911 116.164 82.7911 116.336 82.8093 116.5L84.8888 113.394ZM82.9183 117.271C83.0908 117.907 83.4177 118.098 83.9081 117.816C84.6346 117.399 85.016 116.309 85.016 114.683C85.016 114.493 85.016 114.311 84.9978 114.148L82.9183 117.271Z" fill="#23C49E"/>
+<path d="M87.9219 115.319C87.9219 114.856 88.2397 114.284 88.6393 114.057C89.0388 113.83 89.3567 114.021 89.3567 114.493C89.3567 114.956 89.0388 115.528 88.6393 115.755C88.2397 115.982 87.9219 115.782 87.9219 115.319Z" fill="#23C49E"/>
+<path d="M91.5273 112.349L92.272 111.914C92.3356 112.486 92.7805 112.649 93.3799 112.304C94.0428 111.923 94.4877 111.133 94.4877 110.324C94.4877 109.516 94.0428 109.226 93.3799 109.607C92.9258 109.87 92.5535 110.324 92.3446 110.869L91.6 111.296L91.8633 107.6L94.9145 105.838V106.619L92.4808 108.027L92.3356 110.043L92.3991 110.007C92.6534 109.48 93.0439 109.062 93.5252 108.781C94.5241 108.199 95.2233 108.653 95.2233 109.879C95.2233 111.16 94.4605 112.467 93.3345 113.121C92.3083 113.721 91.5727 113.412 91.5273 112.349Z" fill="#23C49E"/>
+<path d="M98.7006 108.617L96.2578 110.025V109.307L98.0377 104.04L98.8277 103.586L97.0842 108.735V108.799L98.7278 107.854V106.129L99.4271 105.72V107.446L100.226 106.983V107.736L99.4362 108.19V109.471L98.7188 109.889V108.617H98.7006Z" fill="#23C49E"/>
+<path d="M85.7878 125.49C85.7878 127.579 85.1159 129.132 83.9081 129.831C82.7003 130.53 82.0283 129.758 82.0283 127.661C82.0283 125.563 82.7003 124.01 83.9081 123.311C85.1159 122.612 85.7878 123.393 85.7878 125.49ZM84.8888 124.646C84.7254 124.001 84.3894 123.81 83.9081 124.092C83.1816 124.51 82.7911 125.599 82.7911 127.225C82.7911 127.416 82.7911 127.588 82.8093 127.752L84.8888 124.646ZM82.9183 128.514C83.0908 129.15 83.4177 129.341 83.9081 129.059C84.6346 128.642 85.016 127.552 85.016 125.926C85.016 125.736 85.016 125.554 84.9978 125.391L82.9183 128.514Z" fill="#23C49E"/>
+<path d="M87.9219 126.571C87.9219 126.108 88.2397 125.536 88.6393 125.309C89.0388 125.082 89.3567 125.273 89.3567 125.745C89.3567 126.208 89.0388 126.78 88.6393 127.007C88.2397 127.234 87.9219 127.025 87.9219 126.571Z" fill="#23C49E"/>
+<path d="M91.5273 123.601L92.272 123.166C92.3356 123.738 92.7805 123.901 93.3799 123.556C94.0428 123.175 94.4877 122.385 94.4877 121.576C94.4877 120.768 94.0428 120.478 93.3799 120.859C92.9258 121.122 92.5535 121.576 92.3446 122.121L91.6 122.548L91.8633 118.852L94.9145 117.09V117.871L92.4808 119.279L92.3356 121.295L92.3991 121.259C92.6534 120.732 93.0439 120.314 93.5252 120.033C94.5241 119.451 95.2233 119.905 95.2233 121.131C95.2233 122.412 94.4605 123.719 93.3345 124.373C92.3083 124.964 91.5727 124.655 91.5273 123.601Z" fill="#23C49E"/>
+<path d="M97.4751 118.289L98.0745 117.944C98.6557 117.608 99.0643 116.945 99.0643 116.327C99.0643 115.719 98.6738 115.546 98.0836 115.892C97.4933 116.228 97.1119 116.845 97.0574 117.517L96.3309 117.935C96.3854 116.8 97.0665 115.719 98.1017 115.12C99.1097 114.538 99.809 114.802 99.809 115.764C99.809 116.509 99.4639 117.254 98.8736 117.744V117.817C99.6001 117.508 100.027 117.835 100.027 118.679C100.027 119.76 99.2369 120.968 98.1199 121.613C97.0211 122.248 96.2674 121.967 96.2129 120.886L96.9394 120.468C96.9939 121.077 97.4479 121.222 98.0927 120.841C98.801 120.432 99.2369 119.76 99.2369 119.097C99.2369 118.416 98.7828 118.244 98.0745 118.661L97.4479 119.024V118.289H97.4751Z" fill="#23C49E"/>
+<path d="M85.7878 137.677C85.7878 139.766 85.1159 141.319 83.9081 142.018C82.7003 142.717 82.0283 141.945 82.0283 139.848C82.0283 137.75 82.7003 136.197 83.9081 135.498C85.1159 134.799 85.7878 135.58 85.7878 137.677ZM84.8888 136.824C84.7254 136.179 84.3894 135.988 83.9081 136.27C83.1816 136.688 82.7911 137.777 82.7911 139.403C82.7911 139.593 82.7911 139.766 82.8093 139.929L84.8888 136.824ZM82.9183 140.701C83.0908 141.337 83.4177 141.528 83.9081 141.246C84.6346 140.829 85.016 139.739 85.016 138.113C85.016 137.923 85.016 137.741 84.9978 137.578L82.9183 140.701Z" fill="#23C49E"/>
+<path d="M87.9219 138.748C87.9219 138.285 88.2397 137.713 88.6393 137.486C89.0388 137.259 89.3567 137.45 89.3567 137.922C89.3567 138.385 89.0388 138.957 88.6393 139.184C88.2397 139.411 87.9219 139.211 87.9219 138.748Z" fill="#23C49E"/>
+<path d="M91.5273 135.788L92.272 135.353C92.3356 135.925 92.7805 136.088 93.3799 135.743C94.0428 135.362 94.4877 134.572 94.4877 133.763C94.4877 132.955 94.0428 132.665 93.3799 133.046C92.9258 133.309 92.5535 133.763 92.3446 134.308L91.6 134.735L91.8633 131.039L94.9145 129.277V130.058L92.4808 131.466L92.3356 133.482L92.3991 133.446C92.6534 132.919 93.0439 132.501 93.5252 132.22C94.5241 131.638 95.2233 132.092 95.2233 133.318C95.2233 134.599 94.4605 135.906 93.3345 136.56C92.3083 137.151 91.5727 136.842 91.5273 135.788Z" fill="#23C49E"/>
+<path d="M98.1016 127.307C99.1277 126.716 99.8269 127.025 99.8269 128.078C99.8269 128.732 99.5545 129.432 98.6737 131.003L97.4659 133.182V133.255L99.9087 131.847V132.637L96.3762 134.68V134.063L98.1833 130.757C98.8825 129.477 99.0551 129.05 99.0551 128.578C99.0551 127.933 98.6646 127.752 98.0743 128.088C97.475 128.433 97.0754 129.123 97.0754 129.822V129.849L96.3398 130.276V130.249C96.3489 129.123 97.0754 127.906 98.1016 127.307Z" fill="#23C49E"/>
+<path d="M85.7878 149.428C85.7878 151.517 85.1159 153.069 83.9081 153.769C82.7003 154.468 82.0283 153.696 82.0283 151.598C82.0283 149.501 82.7003 147.948 83.9081 147.248C85.1159 146.549 85.7878 147.33 85.7878 149.428ZM84.8888 148.583C84.7254 147.939 84.3894 147.748 83.9081 148.029C83.1816 148.447 82.7911 149.537 82.7911 151.162C82.7911 151.353 82.7911 151.526 82.8093 151.689L84.8888 148.583ZM82.9183 152.461C83.0908 153.097 83.4177 153.287 83.9081 153.006C84.6346 152.588 85.016 151.498 85.016 149.873C85.016 149.682 85.016 149.501 84.9978 149.337L82.9183 152.461Z" fill="#23C49E"/>
+<path d="M87.9219 150.509C87.9219 150.045 88.2397 149.473 88.6393 149.246C89.0388 149.019 89.3567 149.21 89.3567 149.682C89.3567 150.145 89.0388 150.717 88.6393 150.944C88.2397 151.172 87.9219 150.972 87.9219 150.509Z" fill="#23C49E"/>
+<path d="M91.5273 147.539L92.272 147.103C92.3356 147.675 92.7805 147.839 93.3799 147.493C94.0428 147.112 94.4877 146.322 94.4877 145.514C94.4877 144.706 94.0428 144.415 93.3799 144.796C92.9258 145.06 92.5535 145.514 92.3446 146.059L91.6 146.486L91.8633 142.79L94.9145 141.028V141.809L92.4808 143.216L92.3356 145.232L92.3991 145.196C92.6534 144.669 93.0439 144.252 93.5252 143.97C94.5241 143.389 95.2233 143.843 95.2233 145.069C95.2233 146.349 94.4605 147.657 93.3345 148.311C92.3083 148.91 91.5727 148.601 91.5273 147.539Z" fill="#23C49E"/>
+<path d="M100.19 143.462V144.224L96.3938 146.413V145.65L97.9467 144.751V140.247L97.8831 140.283L96.3848 142.281V141.373L97.9467 139.284L98.7095 138.839V144.315L100.19 143.462Z" fill="#23C49E"/>
+<path d="M85.7878 160.678C85.7878 162.767 85.1159 164.32 83.9081 165.019C82.7003 165.718 82.0283 164.946 82.0283 162.849C82.0283 160.751 82.7003 159.198 83.9081 158.499C85.1159 157.8 85.7878 158.581 85.7878 160.678ZM84.8888 159.825C84.7254 159.18 84.3894 158.989 83.9081 159.271C83.1816 159.689 82.7911 160.778 82.7911 162.404C82.7911 162.594 82.7911 162.767 82.8093 162.93L84.8888 159.825ZM82.9183 163.702C83.0908 164.338 83.4177 164.529 83.9081 164.247C84.6346 163.829 85.016 162.74 85.016 161.114C85.016 160.924 85.016 160.742 84.9978 160.579L82.9183 163.702Z" fill="#23C49E"/>
+<path d="M87.9219 161.75C87.9219 161.287 88.2397 160.715 88.6393 160.488C89.0388 160.261 89.3567 160.451 89.3567 160.923C89.3567 161.387 89.0388 161.959 88.6393 162.186C88.2397 162.413 87.9219 162.213 87.9219 161.75Z" fill="#23C49E"/>
+<path d="M91.5273 158.79L92.272 158.354C92.3356 158.926 92.7805 159.09 93.3799 158.744C94.0428 158.363 94.4877 157.573 94.4877 156.765C94.4877 155.957 94.0428 155.666 93.3799 156.047C92.9258 156.311 92.5535 156.765 92.3446 157.31L91.6 157.736L91.8633 154.041L94.9145 152.279V153.06L92.4808 154.467L92.3356 156.483L92.3991 156.447C92.6534 155.92 93.0439 155.503 93.5252 155.221C94.5241 154.64 95.2233 155.094 95.2233 156.32C95.2233 157.6 94.4605 158.908 93.3345 159.562C92.3083 160.152 91.5727 159.843 91.5273 158.79Z" fill="#23C49E"/>
+<path d="M100.017 152.46C100.017 154.549 99.3453 156.102 98.1376 156.801C96.9298 157.5 96.2578 156.728 96.2578 154.631C96.2578 152.533 96.9298 150.98 98.1376 150.281C99.3453 149.591 100.017 150.363 100.017 152.46ZM99.1183 151.607C98.9549 150.962 98.6189 150.771 98.1285 151.053C97.402 151.47 97.0115 152.56 97.0115 154.186C97.0115 154.376 97.0115 154.549 97.0297 154.712L99.1183 151.607ZM97.1478 155.484C97.3203 156.12 97.6472 156.311 98.1376 156.029C98.8641 155.611 99.2455 154.522 99.2455 152.896C99.2455 152.705 99.2455 152.524 99.2273 152.36L97.1478 155.484Z" fill="#23C49E"/>
+<path d="M85.7878 172.856C85.7878 174.945 85.1159 176.498 83.9081 177.197C82.7003 177.896 82.0283 177.124 82.0283 175.027C82.0283 172.929 82.7003 171.376 83.9081 170.677C85.1159 169.978 85.7878 170.768 85.7878 172.856ZM84.8888 172.012C84.7254 171.367 84.3894 171.176 83.9081 171.458C83.1816 171.876 82.7911 172.965 82.7911 174.591C82.7911 174.782 82.7911 174.954 82.8093 175.118L84.8888 172.012ZM82.9183 175.889C83.0908 176.525 83.4177 176.716 83.9081 176.434C84.6346 176.017 85.016 174.927 85.016 173.301C85.016 173.111 85.016 172.929 84.9978 172.766L82.9183 175.889Z" fill="#23C49E"/>
+<path d="M87.9219 173.937C87.9219 173.474 88.2397 172.902 88.6393 172.675C89.0388 172.448 89.3567 172.638 89.3567 173.11C89.3567 173.574 89.0388 174.146 88.6393 174.373C88.2397 174.6 87.9219 174.4 87.9219 173.937Z" fill="#23C49E"/>
+<path d="M93.9613 169.978L91.5186 171.385V170.668L93.2984 165.401L94.0885 164.947L92.3449 170.096V170.159L93.9886 169.215V167.489L94.6878 167.081V168.806L95.4869 168.343V169.097L94.6969 169.551V170.831L93.9704 171.249V169.978H93.9613Z" fill="#23C49E"/>
+<path d="M98.1284 162.486C99.2363 161.841 100.026 162.286 100.026 163.567C100.026 164.284 99.8084 165.083 99.2726 166.282C99.1818 166.491 98.1466 168.743 98.083 168.879L97.1931 169.387L98.6278 166.291C98.755 166.009 98.7913 165.919 98.8276 165.828L98.7641 165.837C98.5915 166.182 98.2283 166.563 97.8923 166.763C96.9297 167.317 96.2305 166.89 96.2305 165.737C96.2305 164.493 97.0296 163.113 98.1284 162.486ZM96.9933 165.256C96.9933 166.018 97.4836 166.309 98.1284 165.937C98.7731 165.564 99.2635 164.702 99.2635 163.939C99.2635 163.176 98.7731 162.885 98.1284 163.258C97.4836 163.63 96.9933 164.493 96.9933 165.256Z" fill="#23C49E"/>
+<g opacity="0.6">
+<g opacity="0.6">
+<path opacity="0.6" d="M25.6448 70.1598V70.9226L21.8489 73.1111V72.3483L23.4018 71.4493V66.9452L23.3382 66.9815L21.8398 68.9793V68.0712L23.4018 65.9826L24.1646 65.5376V71.0134L25.6448 70.1598Z" fill="white"/>
+<path opacity="0.6" d="M28.9047 67.7628L26.4619 69.1704V68.453L28.2418 63.186L29.0318 62.7319L27.2883 67.8809V67.9444L28.9229 67V65.2746L29.6221 64.866V66.5914L30.4212 66.1282V66.8819L29.6312 67.336V68.6164L28.9047 69.0341V67.7628Z" fill="white"/>
+<path opacity="0.6" d="M34.9617 62.4232C34.9617 64.5118 34.2897 66.0646 33.0819 66.7639C31.8741 67.4631 31.2021 66.6912 31.2021 64.5935C31.2021 62.4958 31.8741 60.943 33.0819 60.2437C34.2897 59.5536 34.9617 60.3255 34.9617 62.4232ZM34.0627 61.5786C33.8992 60.9339 33.5632 60.7432 33.0819 61.0247C32.3554 61.4424 31.965 62.5321 31.965 64.1576C31.965 64.3483 31.965 64.5209 31.9831 64.6843L34.0627 61.5786ZM32.0921 65.4562C32.2555 66.0919 32.5915 66.2826 33.0819 66.0011C33.8084 65.5833 34.1898 64.4936 34.1898 62.8681C34.1898 62.6774 34.1898 62.4958 34.1716 62.3324L32.0921 65.4562Z" fill="white"/>
+<path opacity="0.6" d="M37.1045 63.5037C37.1045 63.0406 37.4223 62.4685 37.8219 62.2415C38.2215 62.0144 38.5393 62.2051 38.5393 62.6774C38.5393 63.1405 38.2215 63.7126 37.8219 63.9396C37.4223 64.1666 37.1045 63.9669 37.1045 63.5037Z" fill="white"/>
+<path opacity="0.6" d="M44.4509 56.9476C44.4509 59.0362 43.7789 60.589 42.5712 61.2883C41.3634 61.9875 40.6914 61.2156 40.6914 59.1179C40.6914 57.0202 41.3634 55.4674 42.5712 54.7681C43.7789 54.078 44.4509 54.8499 44.4509 56.9476ZM43.5519 56.103C43.3885 55.4583 43.0525 55.2676 42.5712 55.5491C41.8447 55.9668 41.4542 57.0565 41.4542 58.682C41.4542 58.8727 41.4542 59.0453 41.4724 59.2087L43.5519 56.103ZM41.5813 59.9806C41.7539 60.6163 42.0808 60.807 42.5712 60.5255C43.2976 60.1078 43.679 59.018 43.679 57.3925C43.679 57.2018 43.67 57.0202 43.6609 56.8568L41.5813 59.9806Z" fill="white"/>
+<path opacity="0.6" d="M49.2009 54.2049C49.2009 56.2935 48.5289 57.8464 47.3212 58.5456C46.1134 59.2448 45.4414 58.473 45.4414 56.3753C45.4414 54.2775 46.1134 52.7247 47.3212 52.0255C48.5199 51.3353 49.2009 52.1072 49.2009 54.2049ZM48.3019 53.3604C48.1385 52.7156 47.8025 52.5249 47.3212 52.8064C46.5947 53.2241 46.2042 54.3139 46.2042 55.9394C46.2042 56.1301 46.2042 56.3026 46.2224 56.4661L48.3019 53.3604ZM46.3223 57.2379C46.4948 57.8736 46.8217 58.0643 47.3121 57.7828C48.0386 57.3651 48.42 56.2754 48.42 54.6499C48.42 54.4592 48.4109 54.2775 48.4018 54.1141L46.3223 57.2379Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M30.1213 78.8234V79.5862L26.3255 81.7747V81.0119L27.8783 80.1129V75.6087L27.8148 75.6451L26.3164 77.6429V76.7348L27.8783 74.6461L28.6411 74.2012V79.677L30.1213 78.8234Z" fill="white"/>
+<path opacity="0.6" d="M32.7815 71.686C33.8077 71.0958 34.5069 71.4045 34.5069 72.4579C34.5069 73.1117 34.2345 73.811 33.3536 75.382L32.1459 77.5614V77.6341L34.5887 76.2265V77.0166L31.0561 79.0598V78.4423L32.8633 75.1368C33.5625 73.8564 33.735 73.4296 33.735 72.9573C33.735 72.3126 33.3446 72.131 32.7452 72.467C32.1459 72.8121 31.7463 73.5022 31.7463 74.2014V74.2287L31.0107 74.6555V74.6283C31.0198 73.4931 31.7463 72.2763 32.7815 71.686Z" fill="white"/>
+<path opacity="0.6" d="M36.832 74.9095C36.832 74.4464 37.1499 73.8743 37.5494 73.6472C37.949 73.4202 38.2668 73.6109 38.2668 74.0831C38.2668 74.5462 37.9399 75.1183 37.5494 75.3454C37.1589 75.5724 36.832 75.3726 36.832 74.9095Z" fill="white"/>
+<path opacity="0.6" d="M44.1882 68.3527C44.1882 70.4413 43.5162 71.9942 42.3085 72.6934C41.1007 73.3927 40.4287 72.6208 40.4287 70.5231C40.4287 68.4254 41.1007 66.8725 42.3085 66.1733C43.5162 65.474 44.1882 66.255 44.1882 68.3527ZM43.2892 67.4991C43.1258 66.8543 42.7898 66.6636 42.3085 66.9452C41.582 67.3629 41.1915 68.4526 41.1915 70.0781C41.1915 70.2688 41.1915 70.4413 41.2097 70.6048L43.2892 67.4991ZM41.3186 71.3767C41.4912 72.0123 41.8181 72.203 42.3085 71.9215C43.035 71.5038 43.4164 70.4141 43.4164 68.7886C43.4164 68.5979 43.4073 68.4163 43.3982 68.2528L41.3186 71.3767Z" fill="white"/>
+<path opacity="0.6" d="M48.9275 65.6107C48.9275 67.6993 48.2555 69.2521 47.0477 69.9514C45.84 70.6506 45.168 69.8787 45.168 67.781C45.168 65.6833 45.84 64.1305 47.0477 63.4312C48.2555 62.7411 48.9275 63.513 48.9275 65.6107ZM48.0285 64.7661C47.865 64.1214 47.529 63.9307 47.0477 64.2122C46.3213 64.6299 45.9308 65.7196 45.9308 67.3451C45.9308 67.5358 45.9308 67.7084 45.9489 67.8718L48.0285 64.7661ZM46.0579 68.6437C46.2304 69.2794 46.5574 69.4701 47.0477 69.1886C47.7742 68.7708 48.1556 67.6811 48.1556 66.0556C48.1556 65.8649 48.1465 65.6833 48.1374 65.5199L46.0579 68.6437Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M25.6448 93.5895V94.3523L21.8489 96.5408V95.778L23.4018 94.879V90.3748L23.3382 90.4112L21.8398 92.409V91.5009L23.4018 89.4123L24.1646 88.9673V94.4431L25.6448 93.5895Z" fill="white"/>
+<path opacity="0.6" d="M27.6705 89.6209L28.2698 89.2759C28.851 88.9399 29.2596 88.277 29.2596 87.6594C29.2596 87.051 28.8692 86.8785 28.2789 87.2236C27.6886 87.5686 27.3072 88.1771 27.2527 88.8491L26.5263 89.2668C26.5807 88.1317 27.2618 87.051 28.297 86.4517C29.305 85.8705 30.0043 86.1338 30.0043 87.0964C30.0043 87.8411 29.6592 88.5857 29.0689 89.0761V89.1487C29.7954 88.84 30.2222 89.1669 30.2222 90.0114C30.2222 91.0921 29.4322 92.2998 28.3152 92.9446C27.2164 93.5802 26.4627 93.2987 26.4082 92.2181L27.1347 91.8004C27.1892 92.4088 27.6432 92.5541 28.288 92.1727C28.9872 91.7641 29.4322 91.0921 29.4322 90.4291C29.4322 89.7481 28.9781 89.5755 28.2698 89.9933L27.6432 90.3565V89.6209H27.6705Z" fill="white"/>
+<path opacity="0.6" d="M33.0452 83.7187C34.0714 83.1285 34.7706 83.4372 34.7706 84.4906C34.7706 85.1444 34.4982 85.8437 33.6173 87.4147L32.4095 89.5941V89.6668L34.8523 88.2592V89.0493L31.3198 91.0925V90.475L33.1269 87.1695C33.8262 85.8891 33.9987 85.4623 33.9987 84.9901C33.9987 84.3453 33.6082 84.1637 33.0089 84.4997C32.4095 84.8448 32.01 85.5349 32.01 86.2342V86.2614L31.2744 86.6882V86.661C31.2926 85.5258 32.0191 84.309 33.0452 83.7187Z" fill="white"/>
+<path opacity="0.6" d="M37.1045 86.9329C37.1045 86.4698 37.4223 85.8977 37.8219 85.6707C38.2215 85.4436 38.5393 85.6343 38.5393 86.1066C38.5393 86.5697 38.2215 87.1418 37.8219 87.3688C37.4223 87.5958 37.1045 87.3961 37.1045 86.9329Z" fill="white"/>
+<path opacity="0.6" d="M44.4509 80.3766C44.4509 82.4652 43.7789 84.018 42.5712 84.7173C41.3634 85.4165 40.6914 84.6446 40.6914 82.5469C40.6914 80.4492 41.3634 78.8964 42.5712 78.1971C43.7789 77.507 44.4509 78.2879 44.4509 80.3766ZM43.5519 79.532C43.3885 78.8873 43.0525 78.6966 42.5712 78.9781C41.8447 79.3958 41.4542 80.4855 41.4542 82.111C41.4542 82.3017 41.4542 82.4743 41.4724 82.6377L43.5519 79.532ZM41.5813 83.4096C41.7539 84.0453 42.0808 84.236 42.5712 83.9545C43.2976 83.5367 43.679 82.447 43.679 80.8215C43.679 80.6308 43.67 80.4492 43.6609 80.2858L41.5813 83.4096Z" fill="white"/>
+<path opacity="0.6" d="M49.2009 77.6434C49.2009 79.732 48.5289 81.2848 47.3212 81.9841C46.1134 82.6833 45.4414 81.9114 45.4414 79.8137C45.4414 77.716 46.1134 76.1632 47.3212 75.4639C48.5199 74.7738 49.2009 75.5457 49.2009 77.6434ZM48.3019 76.7898C48.1385 76.145 47.8025 75.9543 47.3212 76.2358C46.5947 76.6535 46.2042 77.7433 46.2042 79.3688C46.2042 79.5595 46.2042 79.732 46.2224 79.8955L48.3019 76.7898ZM46.3223 80.6673C46.4948 81.303 46.8217 81.4937 47.3121 81.2122C48.0386 80.7945 48.42 79.7048 48.42 78.0793C48.42 77.8886 48.4109 77.7069 48.4018 77.5435L46.3223 80.6673Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M25.6448 105.349V106.112L21.8489 108.3V107.537L23.4018 106.638V102.134L23.3382 102.17L21.8398 104.168V103.26L23.4018 101.172L24.1646 100.727V106.202L25.6448 105.349Z" fill="white"/>
+<path opacity="0.6" d="M28.9047 102.952L26.4619 104.359V103.642L28.2418 98.3749L29.0318 97.9209L27.2883 103.07V103.133L28.9229 102.189V100.464L29.6221 100.055V101.78L30.4212 101.317V102.071L29.6312 102.525V103.805L28.9047 104.223V102.952Z" fill="white"/>
+<path opacity="0.6" d="M34.9617 97.6121C34.9617 99.7008 34.2897 101.254 33.0819 101.953C31.8741 102.652 31.2021 101.88 31.2021 99.7825C31.2021 97.6848 31.8741 96.1319 33.0819 95.4327C34.2897 94.7425 34.9617 95.5144 34.9617 97.6121ZM34.0627 96.7676C33.8992 96.1228 33.5632 95.9321 33.0819 96.2137C32.3554 96.6314 31.965 97.7211 31.965 99.3466C31.965 99.5373 31.965 99.7098 31.9831 99.8733L34.0627 96.7676ZM32.0921 100.645C32.2555 101.281 32.5915 101.472 33.0819 101.19C33.8084 100.772 34.1898 99.6826 34.1898 98.0571C34.1898 97.8664 34.1898 97.6848 34.1716 97.5213L32.0921 100.645Z" fill="white"/>
+<path opacity="0.6" d="M37.1045 98.6927C37.1045 98.2296 37.4223 97.6575 37.8219 97.4304C38.2215 97.2034 38.5393 97.3941 38.5393 97.8663C38.5393 98.3294 38.2215 98.9016 37.8219 99.1286C37.4223 99.3556 37.1045 99.1558 37.1045 98.6927Z" fill="white"/>
+<path opacity="0.6" d="M44.4509 92.1365C44.4509 94.2252 43.7789 95.778 42.5712 96.4772C41.3634 97.1765 40.6914 96.4046 40.6914 94.3069C40.6914 92.2092 41.3634 90.6563 42.5712 89.9571C43.7789 89.2669 44.4509 90.0388 44.4509 92.1365ZM43.5519 91.292C43.3885 90.6473 43.0525 90.4566 42.5712 90.7381C41.8447 91.1558 41.4542 92.2455 41.4542 93.871C41.4542 94.0617 41.4542 94.2342 41.4724 94.3977L43.5519 91.292ZM41.5813 95.1605C41.7539 95.7962 42.0808 95.9869 42.5712 95.7054C43.2976 95.2876 43.679 94.1979 43.679 92.5724C43.679 92.3817 43.67 92.2001 43.6609 92.0366L41.5813 95.1605Z" fill="white"/>
+<path opacity="0.6" d="M49.2009 89.3939C49.2009 91.4825 48.5289 93.0353 47.3212 93.7346C46.1134 94.4338 45.4414 93.6619 45.4414 91.5642C45.4414 89.4665 46.1134 87.9137 47.3212 87.2144C48.5199 86.5243 49.2009 87.2962 49.2009 89.3939ZM48.3019 88.5493C48.1385 87.9046 47.8025 87.7139 47.3212 87.9954C46.5947 88.4131 46.2042 89.5028 46.2042 91.1283C46.2042 91.319 46.2042 91.4916 46.2224 91.655L48.3019 88.5493ZM46.3223 92.4269C46.4948 93.0626 46.8217 93.2533 47.3121 92.9718C48.0386 92.554 48.42 91.4643 48.42 89.8388C48.42 89.6481 48.4109 89.4665 48.4018 89.303L46.3223 92.4269Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M30.1213 114.012V114.775L26.3255 116.964V116.201L27.8783 115.302V110.798L27.8148 110.834L26.3164 112.832V111.924L27.8783 109.835L28.6411 109.39V114.866L30.1213 114.012Z" fill="white"/>
+<path opacity="0.6" d="M32.7815 106.875C33.8077 106.285 34.5069 106.593 34.5069 107.647C34.5069 108.301 34.2345 109 33.3536 110.571L32.1459 112.75V112.823L34.5887 111.415V112.206L31.0561 114.249V113.631L32.8633 110.326C33.5625 109.045 33.735 108.619 33.735 108.146C33.735 107.502 33.3446 107.32 32.7452 107.656C32.1459 108.001 31.7463 108.691 31.7463 109.39V109.418L31.0107 109.844V109.817C31.0198 108.682 31.7463 107.465 32.7815 106.875Z" fill="white"/>
+<path opacity="0.6" d="M36.832 110.089C36.832 109.626 37.1499 109.054 37.5494 108.827C37.949 108.6 38.2668 108.791 38.2668 109.263C38.2668 109.726 37.9399 110.298 37.5494 110.525C37.1589 110.752 36.832 110.552 36.832 110.089Z" fill="white"/>
+<path opacity="0.6" d="M44.1882 103.533C44.1882 105.621 43.5162 107.174 42.3085 107.874C41.1007 108.573 40.4287 107.801 40.4287 105.703C40.4287 103.605 41.1007 102.053 42.3085 101.353C43.5162 100.663 44.1882 101.444 44.1882 103.533ZM43.2892 102.688C43.1258 102.044 42.7898 101.853 42.3085 102.134C41.582 102.552 41.1915 103.642 41.1915 105.267C41.1915 105.458 41.1915 105.631 41.2097 105.794L43.2892 102.688ZM41.3186 106.566C41.4912 107.202 41.8181 107.392 42.3085 107.111C43.035 106.693 43.4164 105.603 43.4164 103.978C43.4164 103.787 43.4073 103.605 43.3982 103.442L41.3186 106.566Z" fill="white"/>
+<path opacity="0.6" d="M48.9275 100.8C48.9275 102.888 48.2555 104.441 47.0477 105.14C45.84 105.84 45.168 105.068 45.168 102.97C45.168 100.872 45.84 99.3194 47.0477 98.6202C48.2555 97.93 48.9275 98.7019 48.9275 100.8ZM48.0285 99.9551C47.865 99.3103 47.529 99.1196 47.0477 99.4012C46.3213 99.8189 45.9308 100.909 45.9308 102.534C45.9308 102.725 45.9308 102.897 45.9489 103.061L48.0285 99.9551ZM46.0579 103.824C46.2304 104.459 46.5574 104.65 47.0477 104.368C47.7742 103.951 48.1556 102.861 48.1556 101.236C48.1556 101.045 48.1465 100.863 48.1374 100.7L46.0579 103.824Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M25.6448 128.777V129.54L21.8489 131.729V130.966L23.4018 130.067V125.563L23.3382 125.599L21.8398 127.597V126.689L23.4018 124.6L24.1646 124.155V129.631L25.6448 128.777Z" fill="white"/>
+<path opacity="0.6" d="M27.6705 124.809L28.2698 124.464C28.851 124.128 29.2596 123.465 29.2596 122.848C29.2596 122.239 28.8692 122.067 28.2789 122.412C27.6886 122.757 27.3072 123.366 27.2527 124.038L26.5263 124.455C26.5807 123.32 27.2618 122.239 28.297 121.64C29.305 121.059 30.0043 121.322 30.0043 122.285C30.0043 123.03 29.6592 123.774 29.0689 124.265V124.337C29.7954 124.028 30.2222 124.355 30.2222 125.2C30.2222 126.281 29.4322 127.488 28.3152 128.133C27.2164 128.769 26.4627 128.487 26.4082 127.407L27.1347 126.989C27.1892 127.597 27.6432 127.743 28.288 127.361C28.9872 126.953 29.4322 126.281 29.4322 125.618C29.4322 124.937 28.9781 124.764 28.2698 125.182L27.6432 125.545V124.809H27.6705Z" fill="white"/>
+<path opacity="0.6" d="M33.0452 118.898C34.0714 118.308 34.7706 118.616 34.7706 119.67C34.7706 120.324 34.4982 121.023 33.6173 122.594L32.4095 124.773V124.846L34.8523 123.438V124.228L31.3198 126.272V125.654L33.1269 122.349C33.8262 121.068 33.9987 120.641 33.9987 120.169C33.9987 119.525 33.6082 119.343 33.0089 119.679C32.4095 120.024 32.01 120.714 32.01 121.413V121.441L31.2744 121.867V121.84C31.2926 120.714 32.0191 119.497 33.0452 118.898Z" fill="white"/>
+<path opacity="0.6" d="M37.1045 122.121C37.1045 121.658 37.4223 121.086 37.8219 120.859C38.2215 120.632 38.5393 120.823 38.5393 121.295C38.5393 121.758 38.2215 122.33 37.8219 122.557C37.4223 122.784 37.1045 122.585 37.1045 122.121Z" fill="white"/>
+<path opacity="0.6" d="M44.4509 115.565C44.4509 117.654 43.7789 119.207 42.5712 119.906C41.3634 120.605 40.6914 119.833 40.6914 117.736C40.6914 115.638 41.3634 114.085 42.5712 113.386C43.7789 112.696 44.4509 113.468 44.4509 115.565ZM43.5519 114.721C43.3885 114.076 43.0525 113.885 42.5712 114.167C41.8447 114.585 41.4542 115.674 41.4542 117.3C41.4542 117.49 41.4542 117.663 41.4724 117.826L43.5519 114.721ZM41.5813 118.598C41.7539 119.234 42.0808 119.425 42.5712 119.143C43.2976 118.725 43.679 117.636 43.679 116.01C43.679 115.82 43.67 115.638 43.6609 115.474L41.5813 118.598Z" fill="white"/>
+<path opacity="0.6" d="M49.2009 112.832C49.2009 114.92 48.5289 116.473 47.3212 117.172C46.1134 117.872 45.4414 117.1 45.4414 115.002C45.4414 112.904 46.1134 111.352 47.3212 110.652C48.5199 109.953 49.2009 110.734 49.2009 112.832ZM48.3019 111.978C48.1385 111.333 47.8025 111.143 47.3212 111.424C46.5947 111.842 46.2042 112.932 46.2042 114.557C46.2042 114.748 46.2042 114.92 46.2224 115.084L48.3019 111.978ZM46.3223 115.856C46.4948 116.491 46.8217 116.682 47.3121 116.401C48.0386 115.983 48.42 114.893 48.42 113.268C48.42 113.077 48.4109 112.895 48.4018 112.732L46.3223 115.856Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M25.6448 151.318V152.08L21.8489 154.269V153.506L23.4018 152.607V148.103L23.3382 148.139L21.8398 150.137V149.229L23.4018 147.14L24.1646 146.695V152.171L25.6448 151.318Z" fill="white"/>
+<path opacity="0.6" d="M28.9047 148.92L26.4619 150.327V149.61L28.2418 144.343L29.0318 143.889L27.2883 149.038V149.101L28.9229 148.157V146.431L29.6221 146.023V147.748L30.4212 147.285V148.039L29.6312 148.493V149.773L28.9047 150.191V148.92Z" fill="white"/>
+<path opacity="0.6" d="M34.9617 143.58C34.9617 145.669 34.2897 147.222 33.0819 147.921C31.8741 148.62 31.2021 147.848 31.2021 145.751C31.2021 143.653 31.8741 142.1 33.0819 141.401C34.2897 140.711 34.9617 141.492 34.9617 143.58ZM34.0627 142.736C33.8992 142.091 33.5632 141.9 33.0819 142.182C32.3554 142.599 31.965 143.689 31.965 145.315C31.965 145.505 31.965 145.678 31.9831 145.841L34.0627 142.736ZM32.0921 146.613C32.2555 147.249 32.5915 147.44 33.0819 147.158C33.8084 146.74 34.1898 145.651 34.1898 144.025C34.1898 143.834 34.1898 143.653 34.1716 143.489L32.0921 146.613Z" fill="white"/>
+<path opacity="0.6" d="M37.1045 144.661C37.1045 144.198 37.4223 143.626 37.8219 143.399C38.2215 143.172 38.5393 143.362 38.5393 143.835C38.5393 144.298 38.2215 144.87 37.8219 145.097C37.4223 145.324 37.1045 145.124 37.1045 144.661Z" fill="white"/>
+<path opacity="0.6" d="M44.4509 138.104C44.4509 140.193 43.7789 141.746 42.5712 142.445C41.3634 143.144 40.6914 142.372 40.6914 140.275C40.6914 138.177 41.3634 136.624 42.5712 135.925C43.7789 135.235 44.4509 136.007 44.4509 138.104ZM43.5519 137.26C43.3885 136.615 43.0525 136.424 42.5712 136.706C41.8447 137.124 41.4542 138.213 41.4542 139.839C41.4542 140.029 41.4542 140.202 41.4724 140.365L43.5519 137.26ZM41.5813 141.137C41.7539 141.773 42.0808 141.964 42.5712 141.682C43.2976 141.265 43.679 140.175 43.679 138.549C43.679 138.359 43.67 138.177 43.6609 138.014L41.5813 141.137Z" fill="white"/>
+<path opacity="0.6" d="M49.2009 135.362C49.2009 137.451 48.5289 139.003 47.3212 139.703C46.1134 140.402 45.4414 139.63 45.4414 137.532C45.4414 135.435 46.1134 133.882 47.3212 133.182C48.5199 132.492 49.2009 133.273 49.2009 135.362ZM48.3019 134.517C48.1385 133.873 47.8025 133.682 47.3212 133.963C46.5947 134.381 46.2042 135.471 46.2042 137.096C46.2042 137.287 46.2042 137.46 46.2224 137.623L48.3019 134.517ZM46.3223 138.395C46.4948 139.031 46.8217 139.221 47.3121 138.94C48.0386 138.522 48.42 137.432 48.42 135.807C48.42 135.616 48.4109 135.435 48.4018 135.271L46.3223 138.395Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M30.1213 159.98V160.743L26.3255 162.931V162.169L27.8783 161.27V156.765L27.8148 156.802L26.3164 158.8V157.891L27.8783 155.803L28.6411 155.358V160.834L30.1213 159.98Z" fill="white"/>
+<path opacity="0.6" d="M32.7815 152.843C33.8077 152.252 34.5069 152.561 34.5069 153.615C34.5069 154.268 34.2345 154.968 33.3536 156.539L32.1459 158.718V158.791L34.5887 157.383V158.173L31.0561 160.217V159.599L32.8633 156.294C33.5625 155.013 33.735 154.586 33.735 154.114C33.735 153.469 33.3446 153.288 32.7452 153.624C32.1459 153.969 31.7463 154.659 31.7463 155.358V155.385L31.0107 155.812V155.785C31.0198 154.65 31.7463 153.433 32.7815 152.843Z" fill="white"/>
+<path opacity="0.6" d="M36.832 156.066C36.832 155.603 37.1499 155.031 37.5494 154.804C37.949 154.577 38.2668 154.768 38.2668 155.24C38.2668 155.703 37.9399 156.275 37.5494 156.502C37.1589 156.729 36.832 156.529 36.832 156.066Z" fill="white"/>
+<path opacity="0.6" d="M44.1882 149.51C44.1882 151.599 43.5162 153.152 42.3085 153.851C41.1007 154.55 40.4287 153.778 40.4287 151.68C40.4287 149.583 41.1007 148.03 42.3085 147.331C43.5162 146.64 44.1882 147.412 44.1882 149.51ZM43.2892 148.656C43.1258 148.012 42.7898 147.821 42.3085 148.103C41.582 148.52 41.1915 149.61 41.1915 151.235C41.1915 151.426 41.1915 151.599 41.2097 151.762L43.2892 148.656ZM41.3186 152.534C41.4912 153.17 41.8181 153.36 42.3085 153.079C43.035 152.661 43.4164 151.571 43.4164 149.946C43.4164 149.755 43.4073 149.574 43.3982 149.41L41.3186 152.534Z" fill="white"/>
+<path opacity="0.6" d="M48.9275 146.767C48.9275 148.856 48.2555 150.409 47.0477 151.108C45.84 151.807 45.168 151.035 45.168 148.938C45.168 146.84 45.84 145.287 47.0477 144.588C48.2555 143.898 48.9275 144.67 48.9275 146.767ZM48.0285 145.923C47.865 145.278 47.529 145.087 47.0477 145.369C46.3213 145.787 45.9308 146.876 45.9308 148.502C45.9308 148.693 45.9308 148.865 45.9489 149.029L48.0285 145.923ZM46.0579 149.8C46.2304 150.436 46.5574 150.627 47.0477 150.345C47.7742 149.928 48.1556 148.838 48.1556 147.212C48.1556 147.022 48.1465 146.84 48.1374 146.677L46.0579 149.8Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M25.6448 174.746V175.509L21.8489 177.697V176.934L23.4018 176.035V171.531L23.3382 171.567L21.8398 173.565V172.657L23.4018 170.569L24.1646 170.124V175.599L25.6448 174.746Z" fill="white"/>
+<path opacity="0.6" d="M27.6705 170.777L28.2698 170.432C28.851 170.096 29.2596 169.433 29.2596 168.816C29.2596 168.207 28.8692 168.035 28.2789 168.38C27.6886 168.725 27.3072 169.333 27.2527 170.005L26.5263 170.423C26.5807 169.288 27.2618 168.207 28.297 167.608C29.305 167.027 30.0043 167.29 30.0043 168.253C30.0043 168.997 29.6592 169.742 29.0689 170.232V170.305C29.7954 169.996 30.2222 170.323 30.2222 171.168C30.2222 172.248 29.4322 173.456 28.3152 174.101C27.2164 174.736 26.4627 174.455 26.4082 173.374L27.1347 172.957C27.1892 173.565 27.6432 173.71 28.288 173.329C28.9872 172.92 29.4322 172.248 29.4322 171.585C29.4322 170.904 28.9781 170.732 28.2698 171.15L27.6432 171.513V170.777H27.6705Z" fill="white"/>
+<path opacity="0.6" d="M33.0452 164.875C34.0714 164.285 34.7706 164.593 34.7706 165.647C34.7706 166.301 34.4982 167 33.6173 168.571L32.4095 170.75V170.823L34.8523 169.415V170.206L31.3198 172.249V171.631L33.1269 168.326C33.8262 167.045 33.9987 166.619 33.9987 166.146C33.9987 165.502 33.6082 165.32 33.0089 165.656C32.4095 166.001 32.01 166.691 32.01 167.39V167.418L31.2744 167.844V167.817C31.2926 166.682 32.0191 165.465 33.0452 164.875Z" fill="white"/>
+<path opacity="0.6" d="M37.1045 168.098C37.1045 167.635 37.4223 167.063 37.8219 166.836C38.2215 166.609 38.5393 166.8 38.5393 167.272C38.5393 167.735 38.2215 168.307 37.8219 168.534C37.4223 168.752 37.1045 168.562 37.1045 168.098Z" fill="white"/>
+<path opacity="0.6" d="M44.4509 161.542C44.4509 163.63 43.7789 165.183 42.5712 165.882C41.3634 166.582 40.6914 165.81 40.6914 163.712C40.6914 161.614 41.3634 160.061 42.5712 159.362C43.7789 158.663 44.4509 159.444 44.4509 161.542ZM43.5519 160.688C43.3885 160.043 43.0525 159.853 42.5712 160.134C41.8447 160.552 41.4542 161.642 41.4542 163.267C41.4542 163.458 41.4542 163.63 41.4724 163.794L43.5519 160.688ZM41.5813 164.566C41.7539 165.201 42.0808 165.392 42.5712 165.111C43.2976 164.693 43.679 163.603 43.679 161.978C43.679 161.787 43.67 161.605 43.6609 161.442L41.5813 164.566Z" fill="white"/>
+<path opacity="0.6" d="M49.2009 158.8C49.2009 160.888 48.5289 162.441 47.3212 163.14C46.1134 163.84 45.4414 163.068 45.4414 160.97C45.4414 158.872 46.1134 157.319 47.3212 156.62C48.5199 155.93 49.2009 156.702 49.2009 158.8ZM48.3019 157.955C48.1385 157.31 47.8025 157.12 47.3212 157.401C46.5947 157.819 46.2042 158.909 46.2042 160.534C46.2042 160.725 46.2042 160.897 46.2224 161.061L48.3019 157.955ZM46.3223 161.824C46.4948 162.459 46.8217 162.65 47.3121 162.368C48.0386 161.951 48.42 160.861 48.42 159.236C48.42 159.045 48.4109 158.863 48.4018 158.7L46.3223 161.824Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M25.6448 186.506V187.268L21.8489 189.457V188.694L23.4018 187.795V183.291L23.3382 183.327L21.8398 185.325V184.417L23.4018 182.328L24.1646 181.883V187.359L25.6448 186.506Z" fill="white"/>
+<path opacity="0.6" d="M28.9047 184.109L26.4619 185.516V184.799L28.2418 179.532L29.0318 179.078L27.2883 184.227V184.29L28.9229 183.346V181.62L29.6221 181.212V182.937L30.4212 182.474V183.228L29.6312 183.682V184.962L28.9047 185.38V184.109Z" fill="white"/>
+<path opacity="0.6" d="M34.9617 178.769C34.9617 180.857 34.2897 182.41 33.0819 183.11C31.8741 183.809 31.2021 183.037 31.2021 180.939C31.2021 178.842 31.8741 177.289 33.0819 176.589C34.2897 175.899 34.9617 176.671 34.9617 178.769ZM34.0627 177.924C33.8992 177.28 33.5632 177.089 33.0819 177.37C32.3554 177.788 31.965 178.878 31.965 180.503C31.965 180.694 31.965 180.867 31.9831 181.03L34.0627 177.924ZM32.0921 181.802C32.2555 182.438 32.5915 182.628 33.0819 182.347C33.8084 181.929 34.1898 180.839 34.1898 179.214C34.1898 179.023 34.1898 178.841 34.1716 178.678L32.0921 181.802Z" fill="white"/>
+<path opacity="0.6" d="M37.1045 179.849C37.1045 179.386 37.4223 178.814 37.8219 178.587C38.2215 178.36 38.5393 178.551 38.5393 179.023C38.5393 179.486 38.2215 180.058 37.8219 180.285C37.4223 180.512 37.1045 180.313 37.1045 179.849Z" fill="white"/>
+<path opacity="0.6" d="M44.4509 173.293C44.4509 175.382 43.7789 176.935 42.5712 177.634C41.3634 178.333 40.6914 177.561 40.6914 175.464C40.6914 173.366 41.3634 171.813 42.5712 171.114C43.7789 170.424 44.4509 171.196 44.4509 173.293ZM43.5519 172.449C43.3885 171.804 43.0525 171.613 42.5712 171.895C41.8447 172.313 41.4542 173.402 41.4542 175.028C41.4542 175.218 41.4542 175.391 41.4724 175.554L43.5519 172.449ZM41.5813 176.326C41.7539 176.962 42.0808 177.153 42.5712 176.871C43.2976 176.453 43.679 175.364 43.679 173.738C43.679 173.548 43.67 173.366 43.6609 173.202L41.5813 176.326Z" fill="white"/>
+<path opacity="0.6" d="M49.2009 170.551C49.2009 172.639 48.5289 174.192 47.3212 174.891C46.1134 175.591 45.4414 174.819 45.4414 172.721C45.4414 170.623 46.1134 169.07 47.3212 168.371C48.5199 167.681 49.2009 168.453 49.2009 170.551ZM48.3019 169.706C48.1385 169.061 47.8025 168.871 47.3212 169.152C46.5947 169.57 46.2042 170.66 46.2042 172.285C46.2042 172.476 46.2042 172.648 46.2224 172.812L48.3019 169.706ZM46.3223 173.584C46.4948 174.219 46.8217 174.41 47.3121 174.129C48.0386 173.711 48.42 172.621 48.42 170.996C48.42 170.805 48.4109 170.623 48.4018 170.46L46.3223 173.584Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M30.1213 195.169V195.932L26.3255 198.12V197.358L27.8783 196.459V191.954L27.8148 191.991L26.3164 193.989V193.08L27.8783 190.992L28.6411 190.547V196.023L30.1213 195.169Z" fill="white"/>
+<path opacity="0.6" d="M32.7815 188.032C33.8077 187.441 34.5069 187.75 34.5069 188.804C34.5069 189.457 34.2345 190.157 33.3536 191.728L32.1459 193.907V193.98L34.5887 192.572V193.362L31.0561 195.405V194.788L32.8633 191.482C33.5625 190.202 33.735 189.775 33.735 189.303C33.735 188.658 33.3446 188.477 32.7452 188.813C32.1459 189.158 31.7463 189.848 31.7463 190.547V190.574L31.0107 191.001V190.974C31.0198 189.839 31.7463 188.622 32.7815 188.032Z" fill="white"/>
+<path opacity="0.6" d="M36.832 191.255C36.832 190.792 37.1499 190.22 37.5494 189.993C37.949 189.766 38.2668 189.957 38.2668 190.429C38.2668 190.892 37.9399 191.464 37.5494 191.691C37.1589 191.918 36.832 191.718 36.832 191.255Z" fill="white"/>
+<path opacity="0.6" d="M44.1882 184.699C44.1882 186.788 43.5162 188.341 42.3085 189.04C41.1007 189.739 40.4287 188.967 40.4287 186.869C40.4287 184.772 41.1007 183.219 42.3085 182.52C43.5162 181.829 44.1882 182.601 44.1882 184.699ZM43.2892 183.845C43.1258 183.201 42.7898 183.01 42.3085 183.291C41.582 183.709 41.1915 184.799 41.1915 186.424C41.1915 186.615 41.1915 186.788 41.2097 186.951L43.2892 183.845ZM41.3186 187.723C41.4912 188.359 41.8181 188.549 42.3085 188.268C43.035 187.85 43.4164 186.76 43.4164 185.135C43.4164 184.944 43.4073 184.763 43.3982 184.599L41.3186 187.723Z" fill="white"/>
+<path opacity="0.6" d="M48.9275 181.956C48.9275 184.045 48.2555 185.598 47.0477 186.297C45.84 186.996 45.168 186.224 45.168 184.127C45.168 182.029 45.84 180.476 47.0477 179.777C48.2555 179.087 48.9275 179.859 48.9275 181.956ZM48.0285 181.112C47.865 180.467 47.529 180.276 47.0477 180.558C46.3213 180.976 45.9308 182.065 45.9308 183.691C45.9308 183.882 45.9308 184.054 45.9489 184.218L48.0285 181.112ZM46.0579 184.989C46.2304 185.625 46.5574 185.816 47.0477 185.534C47.7742 185.117 48.1556 184.027 48.1556 182.401C48.1556 182.211 48.1465 182.029 48.1374 181.866L46.0579 184.989Z" fill="white"/>
+</g>
+<g opacity="0.6">
+<path opacity="0.6" d="M25.6448 209.935V210.698L21.8489 212.887V212.124L23.4018 211.225V206.721L23.3382 206.757L21.8398 208.755V207.847L23.4018 205.758L24.1646 205.313V210.789L25.6448 209.935Z" fill="white"/>
+<path opacity="0.6" d="M27.6705 205.967L28.2698 205.622C28.851 205.286 29.2596 204.623 29.2596 204.005C29.2596 203.397 28.8692 203.224 28.2789 203.569C27.6886 203.914 27.3072 204.523 27.2527 205.195L26.5263 205.612C26.5807 204.477 27.2618 203.397 28.297 202.797C29.305 202.216 30.0043 202.48 30.0043 203.442C30.0043 204.187 29.6592 204.931 29.0689 205.422V205.494C29.7954 205.186 30.2222 205.513 30.2222 206.357C30.2222 207.438 29.4322 208.646 28.3152 209.29C27.2164 209.926 26.4627 209.644 26.4082 208.564L27.1347 208.146C27.1892 208.755 27.6432 208.9 28.288 208.518C28.9872 208.11 29.4322 207.438 29.4322 206.775C29.4322 206.094 28.9781 205.921 28.2698 206.339L27.6432 206.702V205.967H27.6705Z" fill="white"/>
+<path opacity="0.6" d="M33.0452 200.064C34.0714 199.474 34.7706 199.783 34.7706 200.836C34.7706 201.49 34.4982 202.189 33.6173 203.76L32.4095 205.94V206.012L34.8523 204.605V205.395L31.3198 207.438V206.821L33.1269 203.515C33.8262 202.235 33.9987 201.808 33.9987 201.336C33.9987 200.691 33.6082 200.509 33.0089 200.845C32.4095 201.19 32.01 201.881 32.01 202.58V202.607L31.2744 203.034V203.007C31.2926 201.872 32.0191 200.655 33.0452 200.064Z" fill="white"/>
+<path opacity="0.6" d="M37.1045 203.279C37.1045 202.815 37.4223 202.243 37.8219 202.016C38.2215 201.789 38.5393 201.98 38.5393 202.452C38.5393 202.915 38.2215 203.487 37.8219 203.715C37.4223 203.942 37.1045 203.742 37.1045 203.279Z" fill="white"/>
+<path opacity="0.6" d="M44.4509 196.722C44.4509 198.811 43.7789 200.364 42.5712 201.063C41.3634 201.762 40.6914 200.99 40.6914 198.893C40.6914 196.795 41.3634 195.242 42.5712 194.543C43.7789 193.853 44.4509 194.634 44.4509 196.722ZM43.5519 195.878C43.3885 195.233 43.0525 195.042 42.5712 195.324C41.8447 195.742 41.4542 196.831 41.4542 198.457C41.4542 198.647 41.4542 198.82 41.4724 198.983L43.5519 195.878ZM41.5813 199.755C41.7539 200.391 42.0808 200.582 42.5712 200.3C43.2976 199.882 43.679 198.793 43.679 197.167C43.679 196.977 43.67 196.795 43.6609 196.631L41.5813 199.755Z" fill="white"/>
+<path opacity="0.6" d="M49.2009 193.989C49.2009 196.078 48.5289 197.631 47.3212 198.33C46.1134 199.029 45.4414 198.257 45.4414 196.159C45.4414 194.062 46.1134 192.509 47.3212 191.81C48.5199 191.119 49.2009 191.891 49.2009 193.989ZM48.3019 193.135C48.1385 192.491 47.8025 192.3 47.3212 192.582C46.5947 192.999 46.2042 194.089 46.2042 195.714C46.2042 195.905 46.2042 196.078 46.2224 196.241L48.3019 193.135ZM46.3223 197.013C46.4948 197.649 46.8217 197.839 47.3121 197.558C48.0386 197.14 48.42 196.05 48.42 194.425C48.42 194.234 48.4109 194.053 48.4018 193.889L46.3223 197.013Z" fill="white"/>
+</g>
+</g>
+<path d="M98.4106 64.6748L209.362 0.617544C211.868 -0.826333 213.903 0.345114 213.903 3.24195V162.804C213.903 165.701 211.868 169.215 209.362 170.668L98.4106 234.725C95.9043 236.169 93.8701 234.998 93.8701 232.101V72.539C93.8701 69.6421 95.9043 66.1278 98.4106 64.6748Z" fill="#363636"/>
+<path d="M104.604 199.291L203.596 142.145C204.713 141.5 205.612 142.027 205.612 143.307V160.443C205.612 161.732 204.713 163.294 203.596 163.939L104.604 221.086C103.487 221.73 102.588 221.204 102.588 219.923V202.788C102.588 201.498 103.487 199.936 104.604 199.291Z" fill="#424242"/>
+<path d="M100.444 66.4912L211.387 2.43395C213.893 0.990073 215.927 2.16152 215.927 5.05836V164.62C215.927 167.517 213.893 171.032 211.387 172.485L100.435 236.542C97.9287 237.986 95.8945 236.814 95.8945 233.917V74.3554C95.9036 71.4585 97.9378 67.9442 100.444 66.4912Z" fill="#424242"/>
+<path d="M100.444 66.4912L211.386 2.43395C213.893 0.990073 215.927 2.16152 215.927 5.05836V45.4506L95.9033 114.748V74.3554C95.9033 71.4585 97.9375 67.9442 100.444 66.4912Z" fill="#424242"/>
+<path d="M193.679 64.6748L304.631 0.617544C307.137 -0.826333 309.171 0.345114 309.171 3.24195V162.804C309.171 165.701 307.137 169.215 304.631 170.668L193.679 234.725C191.173 236.169 189.139 234.998 189.139 232.101V72.539C189.139 69.6421 191.173 66.1278 193.679 64.6748Z" fill="#424242"/>
+<path d="M193.679 64.6748L304.63 0.617544C307.137 -0.826333 309.171 0.345114 309.171 3.24195V43.6342L189.147 112.931V72.539C189.138 69.6421 191.173 66.1278 193.679 64.6748Z" fill="#424242"/>
+<path d="M203.968 76.8164L202.688 77.552C202.615 77.7064 202.134 79.577 200.554 80.4851V81.8745C201.625 81.257 202.361 80.3943 202.515 79.9675L202.588 79.9221V88.6126L203.959 87.8226V76.8164H203.968ZM213.194 78.9414L211.85 79.7133V72.2578L210.343 73.1295L206.202 83.3366V84.4354L210.543 81.929V84.0176L211.85 83.2639V81.1753L213.194 80.4034V78.9414ZM207.864 82.0198L210.343 75.763L210.543 75.1001V80.467L207.864 82.0198ZM220.85 71.84C220.85 68.6526 219.642 67.6174 217.617 68.7797C215.574 69.9603 214.357 72.4031 214.357 75.5905V76.8891C214.357 80.1401 215.592 81.2661 217.635 80.0856C219.66 78.9141 220.859 76.3896 220.859 73.1386V71.84H220.85ZM219.388 74.2465C219.388 76.2352 218.779 77.9606 217.626 78.6326C216.445 79.3137 215.81 78.3057 215.81 76.317V74.4826C215.81 72.5393 216.436 70.9138 217.617 70.2327C218.779 69.5607 219.397 70.4597 219.397 72.4121V74.2465H219.388ZM222.984 76.9526C223.501 76.653 223.928 75.9265 223.928 75.3181C223.928 74.7278 223.501 74.4917 222.984 74.7914C222.475 75.082 222.039 75.7993 222.039 76.4078C222.039 77.0162 222.457 77.2614 222.984 76.9526ZM231.447 65.7195C231.447 62.532 230.239 61.4968 228.214 62.6592C226.171 63.8397 224.954 66.2825 224.954 69.4699V70.7685C224.954 74.0195 226.189 75.1455 228.233 73.965C230.258 72.7935 231.456 70.269 231.456 67.018V65.7195H231.447ZM229.994 68.1259C229.994 70.1146 229.386 71.84 228.233 72.512C227.052 73.1931 226.416 72.1851 226.416 70.1964V68.362C226.416 66.4187 227.043 64.7932 228.223 64.1121C229.386 63.4401 230.003 64.3391 230.003 66.2916V68.1259H229.994ZM239.33 61.1699C239.33 57.9825 238.122 56.9472 236.097 58.1096C234.053 59.2901 232.837 61.7329 232.837 64.9203V66.2189C232.837 69.4699 234.072 70.5959 236.115 69.4154C238.14 68.244 239.339 65.7195 239.339 62.4685V61.1699H239.33ZM237.867 63.5763C237.867 65.5651 237.259 67.2905 236.106 67.9625C234.925 68.6435 234.29 67.6355 234.29 65.6468V63.8124C234.29 61.8691 234.916 60.2436 236.097 59.5625C237.259 58.8905 237.877 59.7896 237.877 61.742V63.5763H237.867ZM251.226 59.0812L246.231 61.969L251.189 50.6813V49.5553L244.406 53.4692V54.9312L249.4 52.0526L244.442 63.3402V64.4663L251.226 60.5524V59.0812ZM258.254 56.4841L259.925 55.5215L257.9 52.1434C258.935 51.0082 259.571 49.637 259.571 48.1205C259.571 46.1862 258.563 45.3054 256.302 46.6131L252.96 48.5382V59.5535L254.44 58.6998V54.4863L256.429 53.3421L258.254 56.4841ZM254.431 49.1466L256.293 48.0751C257.691 47.2669 258.109 47.9752 258.109 48.9832C258.109 50.1274 257.591 51.1808 256.202 51.9799L254.431 53.0061V49.1466ZM262.35 43.1078L260.643 44.0885L263.621 47.8753L260.624 55.1129L262.341 54.123L264.575 48.62L264.647 48.5745L266.881 51.4986L268.598 50.5088L265.646 46.7039L268.579 39.5117L266.872 40.4925L264.647 46.041L264.575 46.0864L262.35 43.1078Z" fill="#424242"/>
+<path d="M199.872 199.291L298.864 142.145C299.981 141.5 300.88 142.027 300.88 143.307V160.443C300.88 161.732 299.981 163.294 298.864 163.939L199.872 221.086C198.755 221.73 197.856 221.204 197.856 219.923V202.788C197.856 201.498 198.755 199.936 199.872 199.291Z" fill="#424242"/>
+<path opacity="0.2" d="M199.736 121.159L298.582 64.0936C299.136 63.7758 299.59 64.0301 299.59 64.6748V78.6505C299.59 79.2952 299.136 80.0762 298.582 80.394L199.736 137.459C199.182 137.777 198.728 137.522 198.728 136.878V122.902C198.728 122.266 199.173 121.485 199.736 121.159Z" fill="#424242"/>
+<path opacity="0.2" d="M198.864 183.272L296.721 126.771" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+<path opacity="0.2" d="M198.864 167.835L296.721 111.333" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+<path opacity="0.2" d="M198.864 153.305L296.721 96.8032" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+<path d="M115.601 82.0923L112.804 83.7087L112.123 86.7781L110.897 87.4864L113.549 76.2351L114.902 75.4541L117.554 83.6452L116.264 84.3898L115.601 82.0923ZM115.32 81.1206L114.23 77.4065L114.194 77.4338L113.095 82.4101L115.32 81.1206Z" fill="white"/>
+<path d="M121.64 73.8013C123.03 73.0022 123.938 73.2383 124.047 74.4551L122.93 75.0999C122.821 74.5641 122.33 74.4733 121.631 74.8819C120.887 75.3087 120.396 75.9989 120.396 76.6073C120.396 77.134 120.705 77.243 121.45 77.0069L122.376 76.7072C123.629 76.3076 124.192 76.5982 124.192 77.6789C124.192 79.0773 123.193 80.5394 121.64 81.4384C120.142 82.3011 119.17 82.0922 119.07 80.8572L120.251 80.1761C120.378 80.7301 120.905 80.7846 121.677 80.3396C122.476 79.8765 122.993 79.1682 122.993 78.5325C122.993 77.9967 122.712 77.8786 121.94 78.1238L120.986 78.4235C119.824 78.7867 119.252 78.4508 119.252 77.3792C119.225 76.0534 120.178 74.6458 121.64 73.8013Z" fill="white"/>
+<path d="M129.023 69.5332C130.413 68.7341 131.321 68.9702 131.43 70.187L130.304 70.8318C130.195 70.296 129.704 70.2052 129.005 70.6139C128.261 71.0407 127.77 71.7308 127.77 72.3392C127.77 72.8659 128.079 72.9749 128.824 72.7388L129.75 72.4391C131.003 72.0396 131.566 72.3302 131.566 73.4108C131.566 74.8093 130.567 76.2713 129.014 77.1703C127.516 78.033 126.544 77.8242 126.444 76.5891L127.625 75.9081C127.752 76.462 128.279 76.5165 129.051 76.0715C129.85 75.6084 130.367 74.9001 130.367 74.2644C130.367 73.7286 130.086 73.6106 129.314 73.8558L128.36 74.1554C127.198 74.5187 126.626 74.1827 126.626 73.1111C126.617 71.7853 127.561 70.3777 129.023 69.5332Z" fill="white"/>
+<path d="M139.13 69.2515C138.958 70.5864 137.914 72.0394 136.542 72.8294C134.753 73.8646 133.736 73.2381 133.736 71.1494V70.0325C133.736 68.0982 134.826 66.2003 136.479 65.2468C138.141 64.2842 139.149 64.9744 139.149 67.063V67.8621L134.881 70.3231V70.65C134.881 71.8941 135.525 72.3118 136.533 71.7306C137.287 71.2947 137.85 70.5955 138.013 69.9053L139.13 69.2515ZM134.881 69.3605L137.995 67.5624C137.995 66.2548 137.423 65.8007 136.461 66.3547C135.48 66.9177 134.881 68.0437 134.881 69.3605Z" fill="white"/>
+<path d="M143.78 59.1538V61.2061L146.295 59.7532V60.861L143.798 62.3049V66.2733C143.798 66.9998 144.234 67.1451 145.042 66.6819C145.342 66.5094 146.114 66.0463 146.259 65.9464V67.0543C146.132 67.1542 145.206 67.7172 144.96 67.8534C143.335 68.7887 142.617 68.5345 142.617 66.9544V62.986L140.938 63.9577V62.8407L142.617 61.869V59.8167L143.78 59.1538Z" fill="white"/>
+<path d="M157.174 59.6169L157.065 59.6805V60.8429L155.885 61.5239V51.3078L157.083 50.6177V54.6496L157.202 54.5861C157.456 53.6144 158.128 52.7699 159.036 52.2341C160.498 51.3896 161.433 52.0525 161.433 53.9776V55.3125C161.433 57.2286 160.498 58.9813 159.036 59.8258C158.128 60.3434 157.474 60.2708 157.174 59.6169ZM160.253 55.8665V54.7677C160.253 53.5054 159.635 53.0605 158.654 53.6326C157.683 54.1956 157.056 55.3489 157.056 56.6202V57.719C157.056 58.9813 157.683 59.4171 158.654 58.8541C159.635 58.2729 160.253 57.1197 160.253 55.8665Z" fill="white"/>
+<path d="M168.607 54.1683L167.472 54.8221V53.5508L167.391 53.5962C167.055 54.695 166.337 55.6213 165.393 56.1661C164.013 56.9652 163.259 56.4385 163.259 54.6768V49.9093L164.43 49.2282V53.6598C164.43 54.9129 164.866 55.2489 165.811 54.7132C166.782 54.1501 167.436 52.9878 167.436 51.7982V47.4938L168.598 46.8218V54.1683H168.607Z" fill="white"/>
+<path d="M171.032 55.3849V54.2498C171.095 54.2226 171.286 54.1227 171.386 54.0682C171.994 53.714 172.33 53.2237 172.566 52.3156L172.666 51.8978L170.351 45.8408L171.631 45.0962L173.302 50.1361L173.384 50.0907L175.045 43.1256L176.29 42.4082L173.956 51.3257C173.402 53.4688 172.802 54.3951 171.513 55.1397C171.331 55.2306 171.104 55.3395 171.032 55.3849Z" fill="white"/>
+<path d="M183.446 43.6617C183.273 44.9966 182.229 46.4495 180.858 47.2396C179.069 48.2748 178.052 47.6482 178.052 45.5596V44.4426C178.052 42.5084 179.141 40.6105 180.794 39.657C182.456 38.6944 183.464 39.3845 183.464 41.4731V42.2723L179.196 44.7332V45.0601C179.196 46.3042 179.841 46.722 180.849 46.1408C181.602 45.7049 182.165 45.0057 182.329 44.3155L183.446 43.6617ZM179.196 43.7706L182.311 41.9726C182.311 40.6649 181.739 40.2109 180.776 40.7648C179.795 41.3369 179.196 42.463 179.196 43.7706Z" fill="white"/>
+<path d="M188.04 37.4773C188.294 35.9517 189.03 34.8438 190.256 34.1355C190.619 33.9266 190.964 33.7813 191.173 33.7632V35.2434C190.891 35.2888 190.474 35.4432 190.119 35.6429C188.685 36.4693 187.886 37.9677 187.886 39.7657V41.9996L189.82 40.8827V41.9179L185.488 44.4152V43.38L186.75 42.6535V37.3865L185.37 38.1856V37.1504L187.886 35.6974V37.5681L188.04 37.4773Z" fill="white"/>
+<path d="M161.914 168.47L164.348 164.248C165.419 162.386 165.565 160.352 164.693 159.426L162.323 156.892C163.24 154.531 163.948 152.161 164.42 149.836L168.48 146.667C169.987 145.514 171.086 143.371 171.095 141.609V137.586C171.086 135.824 169.987 134.962 168.48 135.543L164.42 137.059C163.948 135.288 163.24 133.736 162.323 132.428L164.693 127.206C165.565 125.272 165.419 123.401 164.348 122.784L161.914 121.376C160.843 120.759 159.153 121.567 157.909 123.283L154.531 127.969C152.942 127.824 151.244 127.996 149.473 128.469L148.756 124.191C148.501 122.602 147.203 122.076 145.686 122.947L142.199 124.954C140.674 125.844 139.375 127.86 139.13 129.749L138.412 134.843C136.642 136.415 134.944 138.213 133.354 140.183L130.022 139.375C128.778 139.084 127.088 140.229 126.017 142.09L123.583 146.313C122.521 148.174 122.385 150.199 123.265 151.126L125.636 153.659C124.718 156.02 124.01 158.39 123.538 160.715L119.479 163.884C117.971 165.038 116.872 167.181 116.863 168.943V172.965C116.872 174.727 117.971 175.59 119.479 175.009L123.538 173.492C124.01 175.263 124.718 176.816 125.636 178.123L123.265 183.345C122.394 185.279 122.539 187.15 123.61 187.767L126.044 189.175C127.116 189.793 128.805 188.984 130.049 187.268L133.427 182.582C135.016 182.728 136.714 182.555 138.485 182.083L139.203 186.36C139.457 187.949 140.755 188.476 142.272 187.604L145.759 185.597C147.285 184.707 148.583 182.691 148.828 180.802L149.546 175.708C151.317 174.137 153.015 172.339 154.604 170.368L157.909 171.158C159.144 171.458 160.833 170.332 161.914 168.47ZM198.946 76.0623C198.547 74.6002 197.166 74.2824 195.659 75.3085L191.963 77.8149C190.674 76.3165 189.012 75.3539 187.068 74.9907L187.386 70.541C187.522 68.7157 186.551 67.6714 185.08 68.0619L181.756 68.9791C180.285 69.3877 178.741 71.1222 178.115 73.0928L176.544 77.8149C174.219 79.3133 171.949 81.3292 169.869 83.7357L166.827 83.0274C165.583 82.7368 163.894 83.881 162.822 85.7426L160.388 89.9652C159.317 91.8268 159.172 93.861 160.043 94.7872L162.205 97.0484C161.16 100.054 160.552 103.024 160.416 105.793L157.019 109.607C155.63 111.133 154.894 113.331 155.276 114.82L156.148 118.162C156.538 119.633 157.928 119.951 159.435 118.925L163.131 116.418C164.42 117.917 166.082 118.879 168.026 119.242L167.708 123.692C167.599 125.499 168.58 126.516 170.042 126.108L173.365 125.19C174.836 124.782 176.38 123.047 177.007 121.077L178.578 116.282C180.911 114.793 183.2 112.786 185.289 110.388L188.331 111.124C189.575 111.415 191.264 110.27 192.335 108.409L194.769 104.186C195.841 102.324 195.986 100.29 195.114 99.3641L192.98 97.0847C194.024 94.0789 194.633 91.1094 194.769 88.3397L198.138 84.5802C199.528 83.0546 200.263 80.857 199.882 79.3677L198.946 76.0623ZM177.642 94.2151L171.894 100.554L177.642 101.153L183.391 93.9064L177.642 94.2151ZM171.894 100.554L177.642 101.153V86.2148L171.894 100.554ZM177.642 86.2148V101.162L183.391 93.9155L177.642 86.2148ZM171.894 101.816L177.642 107.855V102.424L171.894 101.816ZM177.642 102.415V107.846L183.4 95.1687L177.642 102.415Z" stroke="white" stroke-width="1.18552" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M137.731 171.949C139.275 172.094 141.037 171.731 142.935 170.777C145.559 169.461 148.038 167.218 150.1 164.511C151.126 163.167 152.043 161.696 152.824 160.171C152.179 159.508 151.489 158.827 150.763 158.155C150.563 157.973 150.363 157.791 150.163 157.61C149.382 159.49 148.256 161.279 146.93 162.759L144.896 161.633L137.731 171.949Z" stroke="white" stroke-width="1.42299" stroke-miterlimit="10"/>
+<path d="M133.381 155.757C132.501 158.028 131.947 160.361 131.829 162.595C131.656 165.719 132.355 168.189 133.681 169.806C134.335 170.605 135.143 171.195 136.069 171.567C136.968 170.296 137.904 168.961 138.848 167.572C139.102 167.199 139.357 166.818 139.62 166.437C138.385 166.246 137.404 165.519 136.787 164.348L138.794 160.825L133.381 155.757Z" stroke="white" stroke-width="1.42299" stroke-miterlimit="10"/>
+<path d="M149.681 141.3C148.138 141.155 146.376 141.518 144.469 142.472C141.845 143.789 139.365 146.032 137.304 148.747C136.287 150.091 135.37 151.544 134.589 153.069C135.234 153.732 135.924 154.413 136.65 155.085C136.85 155.267 137.05 155.449 137.25 155.63C138.031 153.76 139.157 151.971 140.482 150.49L142.399 151.544L149.681 141.3Z" stroke="white" stroke-width="1.42299" stroke-miterlimit="10"/>
+<path d="M154.05 157.419C154.913 155.176 155.458 152.87 155.576 150.654C155.748 147.53 155.049 145.069 153.732 143.453C153.078 142.644 152.27 142.054 151.335 141.691C150.436 142.962 149.5 144.297 148.556 145.677C148.302 146.05 148.048 146.431 147.784 146.813C149.019 147.003 150.009 147.739 150.627 148.91L150.636 148.938L148.71 152.361L154.05 157.419Z" stroke="white" stroke-width="1.42299" stroke-miterlimit="10"/>
+<path d="M195.713 66.4912L306.655 2.43395C309.161 0.990073 311.196 2.16152 311.196 5.05836V164.62C311.196 167.517 309.161 171.032 306.655 172.485L195.704 236.542C193.197 237.986 191.163 236.814 191.163 233.917V74.3554C191.172 71.4585 193.206 67.9442 195.713 66.4912Z" fill="#B0B0B0"/>
+<path d="M195.712 66.4912L306.655 2.43395C309.161 0.990073 311.195 2.16152 311.195 5.05836V45.4506L191.172 114.748V74.3554C191.172 71.4585 193.206 67.9442 195.712 66.4912Z" fill="#4F4F4F"/>
+<path opacity="0.3" d="M205.993 78.6323L204.712 79.3679C204.64 79.5223 204.158 81.393 202.578 82.3011V83.6905C203.65 83.0729 204.385 82.2102 204.54 81.7834L204.612 81.738V90.4285L205.983 89.6385V78.6323H205.993ZM215.219 80.7573L213.875 81.5292V74.0737L212.367 74.9455L208.227 85.1525V86.2513L212.567 83.7449V85.8336L213.875 85.0798V82.9912L215.219 82.2193V80.7573ZM209.888 83.8357L212.367 77.5789L212.567 76.916V82.2829L209.888 83.8357ZM222.874 73.656C222.874 70.4685 221.666 69.4333 219.641 70.5957C217.598 71.7762 216.381 74.219 216.381 77.4064V78.705C216.381 81.956 217.616 83.082 219.659 81.9015C221.685 80.73 222.883 78.2055 222.883 74.9545V73.656H222.874ZM221.421 76.0624C221.421 78.0512 220.813 79.7765 219.659 80.4485C218.479 81.1296 217.843 80.1216 217.843 78.1329V76.2985C217.843 74.3552 218.47 72.7297 219.65 72.0486C220.813 71.3766 221.43 72.2756 221.43 74.2281V76.0624H221.421ZM225.008 78.7686C225.526 78.4689 225.953 77.7424 225.953 77.134C225.953 76.5437 225.526 76.3076 225.008 76.6073C224.5 76.8979 224.064 77.6153 224.064 78.2237C224.073 78.8321 224.491 79.0773 225.008 78.7686ZM233.481 67.5354C233.481 64.3479 232.273 63.3127 230.248 64.4751C228.205 65.6556 226.988 68.0984 226.988 71.2858V72.5844C226.988 75.8354 228.223 76.9614 230.266 75.7809C232.291 74.6095 233.49 72.0849 233.49 68.834V67.5354H233.481ZM232.019 69.9418C232.019 71.9306 231.41 73.656 230.257 74.3279C229.076 75.009 228.441 74.001 228.441 72.0123V70.1779C228.441 68.2346 229.067 66.6091 230.248 65.928C231.41 65.256 232.028 66.1551 232.028 68.1075V69.9418H232.019ZM241.354 62.9858C241.354 59.7984 240.146 58.7631 238.121 59.9255C236.078 61.106 234.861 63.5488 234.861 66.7362V68.0348C234.861 71.2858 236.096 72.4119 238.139 71.2313C240.164 70.0599 241.363 67.5354 241.363 64.2844V62.9858H241.354ZM239.901 65.3923C239.901 67.381 239.293 69.1064 238.139 69.7784C236.959 70.4594 236.323 69.4515 236.323 67.4627V65.6284C236.323 63.685 236.95 62.0595 238.13 61.3785C239.293 60.7065 239.91 61.6055 239.91 63.5579V65.3923H239.901ZM253.25 60.8972L248.255 63.7849L253.214 52.4973V51.3712L246.43 55.2851V56.7472L251.425 53.8685L246.467 65.1562V66.2822L253.25 62.3683V60.8972ZM260.279 58.3L261.95 57.3374L259.925 53.9593C260.96 52.8242 261.595 51.4529 261.595 49.9364C261.595 48.0022 260.587 47.1213 258.326 48.429L254.985 50.3541V61.3694L256.465 60.5158V56.3022L258.453 55.158L260.279 58.3ZM256.456 50.9626L258.317 49.891C259.716 49.0828 260.133 49.7911 260.133 50.7991C260.133 51.9433 259.616 52.9967 258.226 53.7958L256.456 54.822V50.9626ZM264.383 44.9237L262.676 45.9045L265.655 49.6912L262.658 56.9288L264.374 55.9389L266.608 50.4359L266.681 50.3905L268.915 53.3145L270.631 52.3247L267.68 48.5198L270.613 41.3276L268.906 42.3084L266.681 47.8569L266.608 47.9023L264.383 44.9237Z" fill="white"/>
+<path d="M201.897 201.108L300.889 143.961C302.006 143.316 302.905 143.843 302.905 145.123V162.259C302.905 163.549 302.006 165.111 300.889 165.755L201.897 222.902C200.78 223.547 199.881 223.02 199.881 221.74V204.604C199.881 203.314 200.78 201.753 201.897 201.108Z" fill="#4F4F4F"/>
+<path opacity="0.2" d="M201.761 122.975L300.607 65.91C301.161 65.5922 301.615 65.8465 301.615 66.4912V80.4669C301.615 81.1116 301.161 81.8926 300.607 82.2104L201.761 139.275C201.207 139.593 200.753 139.339 200.753 138.694V124.719C200.753 124.083 201.207 123.302 201.761 122.975Z" fill="white"/>
+<path opacity="0.2" d="M200.889 185.089L298.745 128.587" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+<path opacity="0.2" d="M200.889 169.651L298.745 113.149" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+<path opacity="0.2" d="M200.889 155.122L298.745 98.6196" stroke="white" stroke-width="2.16763" stroke-linecap="round"/>
+</g>
+<defs>
+<clipPath id="clip0">
+<rect width="311.196" height="237.15" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/packages/website/public/images/instant/gnt_screenshot.png b/packages/website/public/images/instant/gnt_screenshot.png
new file mode 100644
index 000000000..595c92703
--- /dev/null
+++ b/packages/website/public/images/instant/gnt_screenshot.png
Binary files differ
diff --git a/packages/website/public/images/instant/gods_screenshot.png b/packages/website/public/images/instant/gods_screenshot.png
new file mode 100644
index 000000000..07dcd3b7c
--- /dev/null
+++ b/packages/website/public/images/instant/gods_screenshot.png
Binary files differ
diff --git a/packages/website/public/images/instant/kitty_screenshot.png b/packages/website/public/images/instant/kitty_screenshot.png
new file mode 100644
index 000000000..cf201f8d9
--- /dev/null
+++ b/packages/website/public/images/instant/kitty_screenshot.png
Binary files differ
diff --git a/packages/website/public/images/instant/nmr_screenshot.png b/packages/website/public/images/instant/nmr_screenshot.png
new file mode 100644
index 000000000..9b13e59d7
--- /dev/null
+++ b/packages/website/public/images/instant/nmr_screenshot.png
Binary files differ
diff --git a/packages/website/public/images/instant/rep_screenshot.png b/packages/website/public/images/instant/rep_screenshot.png
new file mode 100644
index 000000000..813787c16
--- /dev/null
+++ b/packages/website/public/images/instant/rep_screenshot.png
Binary files differ
diff --git a/packages/website/ts/components/ui/image.tsx b/packages/website/ts/components/ui/image.tsx
index c8d39352b..d698ddaa0 100644
--- a/packages/website/ts/components/ui/image.tsx
+++ b/packages/website/ts/components/ui/image.tsx
@@ -10,6 +10,7 @@ export interface ImageProps {
height?: string | number;
maxWidth?: string | number;
maxHeight?: string | number;
+ additionalStyle?: React.CSSProperties;
}
interface ImageState {
imageLoadFailed: boolean;
@@ -30,6 +31,7 @@ export class Image extends React.Component<ImageProps, ImageState> {
onError={this._onError.bind(this)}
src={src}
style={{
+ ...this.props.additionalStyle,
borderRadius: this.props.borderRadius,
maxWidth: this.props.maxWidth,
maxHeight: this.props.maxHeight,
diff --git a/packages/website/ts/components/ui/text.tsx b/packages/website/ts/components/ui/text.tsx
index 2fe2a8c79..046442ee5 100644
--- a/packages/website/ts/components/ui/text.tsx
+++ b/packages/website/ts/components/ui/text.tsx
@@ -3,7 +3,7 @@ import { darken } from 'polished';
import * as React from 'react';
import { styled } from 'ts/style/theme';
-export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i';
+export type TextTag = 'p' | 'div' | 'span' | 'label' | 'h1' | 'h2' | 'h3' | 'h4' | 'i' | 'span';
export interface TextProps {
className?: string;
diff --git a/packages/website/ts/containers/instant.ts b/packages/website/ts/containers/instant.ts
new file mode 100644
index 000000000..12ae7454e
--- /dev/null
+++ b/packages/website/ts/containers/instant.ts
@@ -0,0 +1,30 @@
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Dispatch } from 'redux';
+import { Instant as InstantComponent, InstantProps } from 'ts/pages/instant/instant';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { State } from 'ts/redux/reducer';
+import { ScreenWidths } from 'ts/types';
+import { Translate } from 'ts/utils/translate';
+
+interface ConnectedState {
+ translate: Translate;
+ screenWidth: ScreenWidths;
+}
+
+interface ConnectedDispatch {
+ dispatcher: Dispatcher;
+}
+
+const mapStateToProps = (state: State, _ownProps: InstantProps): ConnectedState => ({
+ translate: state.translate,
+ screenWidth: state.screenWidth,
+});
+
+const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
+ dispatcher: new Dispatcher(dispatch),
+});
+
+export const Instant: React.ComponentClass<InstantProps> = connect(mapStateToProps, mapDispatchToProps)(
+ InstantComponent,
+);
diff --git a/packages/website/ts/index.tsx b/packages/website/ts/index.tsx
index 2bc8c8849..8c7fef172 100644
--- a/packages/website/ts/index.tsx
+++ b/packages/website/ts/index.tsx
@@ -7,6 +7,7 @@ import { MetaTags } from 'ts/components/meta_tags';
import { About } from 'ts/containers/about';
import { DocsHome } from 'ts/containers/docs_home';
import { FAQ } from 'ts/containers/faq';
+import { Instant } from 'ts/containers/instant';
import { Jobs } from 'ts/containers/jobs';
import { Landing } from 'ts/containers/landing'; // Note(ez): When we're done we omit all old site imports
import { LaunchKit } from 'ts/containers/launch_kit';
@@ -107,6 +108,7 @@ render(
<Route exact={true} path="/" component={Landing as any} />
<Redirect from="/otc" to={`${WebsitePaths.Portal}`} />
<Route path={WebsitePaths.LaunchKit} component={LaunchKit as any} />
+ <Route path={WebsitePaths.Instant} component={Instant as any} />
<Route path={WebsitePaths.Careers} component={Jobs as any} />
<Route path={WebsitePaths.Portal} component={LazyPortal} />
<Route path={WebsitePaths.FAQ} component={FAQ as any} />
diff --git a/packages/website/ts/pages/instant/configurator.tsx b/packages/website/ts/pages/instant/configurator.tsx
new file mode 100644
index 000000000..c836739bb
--- /dev/null
+++ b/packages/website/ts/pages/instant/configurator.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react';
+
+import { Container } from 'ts/components/ui/container';
+import { colors } from 'ts/style/colors';
+
+export interface ConfiguratorProps {
+ hash: string;
+}
+
+export const Configurator = (props: ConfiguratorProps) => (
+ <Container id={props.hash} height="400px" backgroundColor={colors.instantTertiaryBackground} />
+);
diff --git a/packages/website/ts/pages/instant/features.tsx b/packages/website/ts/pages/instant/features.tsx
new file mode 100644
index 000000000..9c1668c1c
--- /dev/null
+++ b/packages/website/ts/pages/instant/features.tsx
@@ -0,0 +1,146 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { Container } from 'ts/components/ui/container';
+import { Image } from 'ts/components/ui/image';
+import { Text } from 'ts/components/ui/text';
+import { colors } from 'ts/style/colors';
+import { ScreenWidths } from 'ts/types';
+import { utils } from 'ts/utils/utils';
+
+export interface FeatureProps {
+ screenWidth: ScreenWidths;
+ onGetStartedClick: () => void;
+}
+
+export const Features = (props: FeatureProps) => {
+ const isSmallScreen = props.screenWidth === ScreenWidths.Sm;
+ const getStartedLinkInfo = {
+ displayText: 'Get started',
+ onClick: props.onGetStartedClick,
+ };
+ const exploreTheDocsLinkInfo = {
+ displayText: 'Explore the docs',
+ linkSrc: `${utils.getCurrentBaseUrl()}/wiki#Get-Started`,
+ };
+ const tokenLinkInfos = isSmallScreen ? [getStartedLinkInfo] : [getStartedLinkInfo, exploreTheDocsLinkInfo];
+ return (
+ <Container backgroundColor={colors.instantSecondaryBackground} className="py3 flex flex-column px3">
+ <FeatureItem
+ imgSrc="images/instant/feature_1.svg"
+ title="Support ERC-20 and ERC-721 tokens"
+ description="Seamlessly integrate token purchasing into your product experience by offering digital assets ranging from in-game items to stablecoins."
+ linkInfos={tokenLinkInfos}
+ screenWidth={props.screenWidth}
+ />
+ <FeatureItem
+ imgSrc="images/instant/feature_2.svg"
+ title="Generate revenue for your business"
+ description="With just a few lines of code, you can earn up to 5% in affiliate fees on every transaction from your crypto wallet or dApp."
+ linkInfos={[
+ {
+ displayText: 'Learn about affiliate fees',
+ linkSrc: `${utils.getCurrentBaseUrl()}/wiki#Learn-About-Affiliate-Fees`,
+ },
+ ]}
+ screenWidth={props.screenWidth}
+ />
+ <FeatureItem
+ imgSrc="images/instant/feature_3.svg"
+ title="Easy and Flexible Integration"
+ description="Use our out-of-the-box design or customize the user interface by integrating the AssetBuyer engine. You can also tap into 0x networked liquidity or choose your own liquidity pool."
+ linkInfos={[
+ {
+ displayText: 'Explore AssetBuyer',
+ linkSrc: `${utils.getCurrentBaseUrl()}/docs/asset-buyer`,
+ },
+ ]}
+ screenWidth={props.screenWidth}
+ />
+ </Container>
+ );
+};
+
+interface LinkInfo {
+ displayText: string;
+ linkSrc?: string;
+ onClick?: () => void;
+}
+
+interface FeatureItemProps {
+ imgSrc: string;
+ title: string;
+ description: string;
+ linkInfos: LinkInfo[];
+ screenWidth: ScreenWidths;
+}
+
+const FeatureItem = (props: FeatureItemProps) => {
+ const { imgSrc, title, description, linkInfos, screenWidth } = props;
+ const isLargeScreen = screenWidth === ScreenWidths.Lg;
+ const maxWidth = isLargeScreen ? '500px' : undefined;
+ const image = (
+ <Container className="center" minWidth="435px" maxHeight="225px">
+ <Image src={imgSrc} additionalStyle={{ filter: 'drop-shadow(0px 4px 4px rgba(0,0,0,.25))' }} />
+ </Container>
+ );
+ const info = (
+ <Container maxWidth={maxWidth}>
+ <Text fontSize="24px" lineHeight="34px" fontColor={colors.white} fontWeight={500}>
+ {title}
+ </Text>
+ <Container marginTop="28px">
+ <Text fontFamily="Roboto Mono" fontSize="14px" lineHeight="2em" fontColor={colors.grey500}>
+ {description}
+ </Text>
+ </Container>
+ <Container className="flex" marginTop="28px">
+ {_.map(linkInfos, linkInfo => {
+ const onClick = (event: React.MouseEvent<HTMLElement>) => {
+ if (!_.isUndefined(linkInfo.onClick)) {
+ linkInfo.onClick();
+ } else if (!_.isUndefined(linkInfo.linkSrc)) {
+ utils.openUrl(linkInfo.linkSrc);
+ }
+ };
+ return (
+ <Container
+ key={linkInfo.linkSrc}
+ className="flex items-center"
+ marginRight="32px"
+ onClick={onClick}
+ cursor="pointer"
+ >
+ <Container>
+ <Text fontSize="16px" fontColor={colors.white}>
+ {linkInfo.displayText}
+ </Text>
+ </Container>
+ <Container paddingTop="1px" paddingLeft="6px">
+ <i
+ className="zmdi zmdi-chevron-right bold"
+ style={{ fontSize: 16, color: colors.white }}
+ />
+ </Container>
+ </Container>
+ );
+ })}
+ </Container>
+ </Container>
+ );
+ return (
+ <Container className="flex flex-column items-center py4 px3">
+ {isLargeScreen ? (
+ <Container className="flex">
+ {image}
+ <Container marginLeft="115px">{info}</Container>
+ </Container>
+ ) : (
+ <Container className="flex flex-column items-center" width="100%">
+ {image}
+ <Container marginTop="48px">{info}</Container>
+ </Container>
+ )}
+ </Container>
+ );
+};
diff --git a/packages/website/ts/pages/instant/instant.tsx b/packages/website/ts/pages/instant/instant.tsx
new file mode 100644
index 000000000..fa6bd1c33
--- /dev/null
+++ b/packages/website/ts/pages/instant/instant.tsx
@@ -0,0 +1,87 @@
+import { utils as sharedUtils } from '@0x/react-shared';
+import * as _ from 'lodash';
+import * as React from 'react';
+import * as DocumentTitle from 'react-document-title';
+
+import { Footer } from 'ts/components/footer';
+import { MetaTags } from 'ts/components/meta_tags';
+import { TopBar } from 'ts/components/top_bar/top_bar';
+import { Container } from 'ts/components/ui/container';
+import { Configurator } from 'ts/pages/instant/configurator';
+import { Features } from 'ts/pages/instant/features';
+import { Introducing0xInstant } from 'ts/pages/instant/introducing_0x_instant';
+import { NeedMore } from 'ts/pages/instant/need_more';
+import { Screenshots } from 'ts/pages/instant/screenshots';
+import { Dispatcher } from 'ts/redux/dispatcher';
+import { colors } from 'ts/style/colors';
+import { ScreenWidths } from 'ts/types';
+import { Translate } from 'ts/utils/translate';
+import { utils } from 'ts/utils/utils';
+
+export interface InstantProps {
+ location: Location;
+ translate: Translate;
+ dispatcher: Dispatcher;
+ screenWidth: ScreenWidths;
+}
+
+export interface InstantState {}
+
+const CONFIGURATOR_HASH = 'configure';
+const THROTTLE_TIMEOUT = 100;
+const DOCUMENT_TITLE = '0x Instant';
+const DOCUMENT_DESCRIPTION = '0x Instant';
+
+export class Instant extends React.Component<InstantProps, InstantState> {
+ // TODO: consolidate this small screen scaffolding into one place (its being used in portal and docs as well)
+ private readonly _throttledScreenWidthUpdate: () => void;
+ public constructor(props: InstantProps) {
+ super(props);
+ this._throttledScreenWidthUpdate = _.throttle(this._updateScreenWidth.bind(this), THROTTLE_TIMEOUT);
+ }
+ public componentDidMount(): void {
+ window.addEventListener('resize', this._throttledScreenWidthUpdate);
+ window.scrollTo(0, 0);
+ }
+ public render(): React.ReactNode {
+ return (
+ <Container overflowX="hidden">
+ <MetaTags title={DOCUMENT_TITLE} description={DOCUMENT_DESCRIPTION} />
+ <DocumentTitle title={DOCUMENT_TITLE} />
+ <TopBar
+ blockchainIsLoaded={false}
+ location={this.props.location}
+ style={{ backgroundColor: colors.instantPrimaryBackground, position: 'relative' }}
+ translate={this.props.translate}
+ isNightVersion={true}
+ />
+ <Container backgroundColor={colors.instantPrimaryBackground} />
+ <Introducing0xInstant screenWidth={this.props.screenWidth} onCTAClick={this._onGetStartedClick} />
+ <Screenshots screenWidth={this.props.screenWidth} />
+ <Features screenWidth={this.props.screenWidth} onGetStartedClick={this._onGetStartedClick} />
+ {!this._isSmallScreen() && <Configurator hash={CONFIGURATOR_HASH} />}
+ <NeedMore screenWidth={this.props.screenWidth} />
+ <Footer translate={this.props.translate} dispatcher={this.props.dispatcher} />
+ </Container>
+ );
+ }
+ private readonly _onGetStartedClick = () => {
+ if (this._isSmallScreen()) {
+ utils.openUrl(`${utils.getCurrentBaseUrl()}/wiki#Get-Started`);
+ } else {
+ this._scrollToConfigurator();
+ }
+ };
+ private _isSmallScreen(): boolean {
+ const isSmallScreen = this.props.screenWidth === ScreenWidths.Sm;
+ return isSmallScreen;
+ }
+ private _scrollToConfigurator(): void {
+ sharedUtils.setUrlHash(CONFIGURATOR_HASH);
+ sharedUtils.scrollToHash(CONFIGURATOR_HASH, '');
+ }
+ private _updateScreenWidth(): void {
+ const newScreenWidth = utils.getScreenWidth();
+ this.props.dispatcher.updateScreenWidth(newScreenWidth);
+ }
+}
diff --git a/packages/website/ts/pages/instant/introducing_0x_instant.tsx b/packages/website/ts/pages/instant/introducing_0x_instant.tsx
new file mode 100644
index 000000000..da3f09faa
--- /dev/null
+++ b/packages/website/ts/pages/instant/introducing_0x_instant.tsx
@@ -0,0 +1,57 @@
+import * as React from 'react';
+
+import { Button } from 'ts/components/ui/button';
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+import { colors } from 'ts/style/colors';
+import { ScreenWidths } from 'ts/types';
+
+export interface Introducing0xInstantProps {
+ screenWidth: ScreenWidths;
+ onCTAClick: () => void;
+}
+
+export const Introducing0xInstant = (props: Introducing0xInstantProps) => {
+ const isSmallScreen = props.screenWidth === ScreenWidths.Sm;
+ const zero = (
+ <Text fontColor={colors.white} fontSize="42px" fontWeight="600" fontFamily="Roboto Mono" Tag="span">
+ 0
+ </Text>
+ );
+ const title = isSmallScreen ? (
+ <div>
+ Introducing<br />
+ {zero}x Instant
+ </div>
+ ) : (
+ <div>Introducing {zero}x Instant</div>
+ );
+ return (
+ <div className="clearfix center lg-pt4 md-pt4" style={{ backgroundColor: colors.instantPrimaryBackground }}>
+ <div className="mx-auto inline-block align-middle py4" style={{ lineHeight: '44px', textAlign: 'center' }}>
+ <Container className="sm-center sm-pt3">
+ <Text fontColor={colors.white} fontSize="42px" lineHeight="52px" fontWeight="600">
+ {title}
+ </Text>
+ </Container>
+ <Container className="pb2 lg-pt2 md-pt2 sm-pt3 sm-px3 sm-center" maxWidth="600px">
+ <Text fontColor={colors.grey500} fontSize="20px" lineHeight="32px" fontFamily="Roboto Mono">
+ A free and flexible way to offer simple crypto
+ <br /> purchasing in any app or website.
+ </Text>
+ </Container>
+ <div className="py3">
+ <Button
+ type="button"
+ backgroundColor={colors.mediumBlue}
+ fontColor={colors.white}
+ fontSize="18px"
+ onClick={props.onCTAClick}
+ >
+ Get Started
+ </Button>
+ </div>
+ </div>
+ </div>
+ );
+};
diff --git a/packages/website/ts/pages/instant/need_more.tsx b/packages/website/ts/pages/instant/need_more.tsx
new file mode 100644
index 000000000..e6d5c3694
--- /dev/null
+++ b/packages/website/ts/pages/instant/need_more.tsx
@@ -0,0 +1,62 @@
+import * as React from 'react';
+
+import { Button } from 'ts/components/ui/button';
+import { Container } from 'ts/components/ui/container';
+import { Text } from 'ts/components/ui/text';
+import { colors } from 'ts/style/colors';
+import { ScreenWidths } from 'ts/types';
+import { constants } from 'ts/utils/constants';
+import { utils } from 'ts/utils/utils';
+
+export interface NeedMoreProps {
+ screenWidth: ScreenWidths;
+}
+export const NeedMore = (props: NeedMoreProps) => {
+ const isSmallScreen = props.screenWidth === ScreenWidths.Sm;
+ const backgroundColor = isSmallScreen ? colors.instantTertiaryBackground : colors.instantSecondaryBackground;
+ const className = isSmallScreen ? 'flex flex-column items-center' : 'flex';
+ const marginRight = isSmallScreen ? undefined : '200px';
+ return (
+ <Container className="flex flex-column items-center py4 px3" backgroundColor={backgroundColor}>
+ <Container className={className}>
+ <Container className="sm-center" marginRight={marginRight}>
+ <Text fontColor={colors.white} fontSize="32px" lineHeight="45px">
+ Need more flexibility?
+ </Text>
+ <Text fontColor={colors.grey500} fontSize="18px" lineHeight="27px">
+ View our full documentation or reach out if you have any questions.
+ </Text>
+ </Container>
+ <Container className="py3 flex">
+ <Container marginRight="20px">
+ <Button
+ type="button"
+ backgroundColor={colors.white}
+ fontColor={backgroundColor}
+ fontSize="18px"
+ onClick={onGetInTouchClick}
+ >
+ Get in Touch
+ </Button>
+ </Container>
+ <Button
+ type="button"
+ backgroundColor={colors.mediumBlue}
+ fontColor={colors.white}
+ fontSize="18px"
+ onClick={onDocsClick}
+ >
+ Explore the Docs
+ </Button>
+ </Container>
+ </Container>
+ </Container>
+ );
+};
+
+const onGetInTouchClick = () => {
+ utils.openUrl(constants.URL_ZEROEX_CHAT);
+};
+const onDocsClick = () => {
+ utils.openUrl(`${utils.getCurrentBaseUrl()}/wiki#Get-Started`);
+};
diff --git a/packages/website/ts/pages/instant/screenshots.tsx b/packages/website/ts/pages/instant/screenshots.tsx
new file mode 100644
index 000000000..7dcf17fd1
--- /dev/null
+++ b/packages/website/ts/pages/instant/screenshots.tsx
@@ -0,0 +1,35 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+
+import { Container } from 'ts/components/ui/container';
+import { colors } from 'ts/style/colors';
+import { ScreenWidths } from 'ts/types';
+
+export interface ScreenshotsProps {
+ screenWidth: ScreenWidths;
+}
+
+export const Screenshots = (props: ScreenshotsProps) => {
+ const isSmallScreen = props.screenWidth === ScreenWidths.Sm;
+ const images = isSmallScreen
+ ? [
+ 'images/instant/rep_screenshot.png',
+ 'images/instant/dai_screenshot.png',
+ 'images/instant/gods_screenshot.png',
+ ]
+ : [
+ 'images/instant/nmr_screenshot.png',
+ 'images/instant/kitty_screenshot.png',
+ 'images/instant/rep_screenshot.png',
+ 'images/instant/dai_screenshot.png',
+ 'images/instant/gods_screenshot.png',
+ 'images/instant/gnt_screenshot.png',
+ ];
+ return (
+ <Container backgroundColor={colors.instantPrimaryBackground} className="py3 flex justify-center">
+ {_.map(images, image => {
+ return <img className="px1 flex-none" width="300px" height="420px" src={image} key={image} />;
+ })}
+ </Container>
+ );
+};
diff --git a/packages/website/ts/style/colors.ts b/packages/website/ts/style/colors.ts
index 447762969..356d72f7e 100644
--- a/packages/website/ts/style/colors.ts
+++ b/packages/website/ts/style/colors.ts
@@ -21,6 +21,9 @@ const appColors = {
textDarkPrimary: '#000000',
textDarkSecondary: '#5C5C5C',
white: '#fff',
+ instantPrimaryBackground: '#222222',
+ instantSecondaryBackground: '#333333',
+ instantTertiaryBackground: '#444444',
};
export const colors = {
diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts
index 89c477085..9c4b8a018 100644
--- a/packages/website/ts/types.ts
+++ b/packages/website/ts/types.ts
@@ -353,6 +353,7 @@ export enum WebsitePaths {
FAQ = '/faq',
About = '/about',
LaunchKit = '/launch-kit',
+ Instant = '/instant',
Whitepaper = '/pdfs/0x_white_paper.pdf',
SmartContracts = '/docs/contracts',
Connect = '/docs/connect',
diff --git a/packages/website/tslint.json b/packages/website/tslint.json
index 50f5be4f6..3022b2c84 100644
--- a/packages/website/tslint.json
+++ b/packages/website/tslint.json
@@ -5,6 +5,7 @@
"no-object-literal-type-assertion": false,
"completed-docs": false,
"prefer-function-over-method": false,
- "custom-no-magic-numbers": false
+ "custom-no-magic-numbers": false,
+ "semicolon": [true, "always", "ignore-bound-class-methods"]
}
}
diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py
index a7c04e5e6..125de5ff7 100755
--- a/python-packages/order_utils/setup.py
+++ b/python-packages/order_utils/setup.py
@@ -187,8 +187,7 @@ setup(
"mypy_extensions",
"pycodestyle",
"pydocstyle",
- "pylint<=2.1.1", # version pinned until resolution of
- # https://github.com/PyCQA/pylint/issues/2612
+ "pylint",
"pytest",
"sphinx",
"tox",
diff --git a/python-packages/order_utils/src/zero_ex/json_schemas/__init__.py b/python-packages/order_utils/src/zero_ex/json_schemas/__init__.py
index 2a1728b8a..a76a2fa3b 100644
--- a/python-packages/order_utils/src/zero_ex/json_schemas/__init__.py
+++ b/python-packages/order_utils/src/zero_ex/json_schemas/__init__.py
@@ -8,6 +8,51 @@ from pkg_resources import resource_string
import jsonschema
+class _LocalRefResolver(jsonschema.RefResolver):
+ """Resolve package-local JSON schema id's."""
+
+ def __init__(self):
+ """Initialize a new instance."""
+ self.ref_to_file = {
+ "/addressSchema": "address_schema.json",
+ "/hexSchema": "hex_schema.json",
+ "/orderSchema": "order_schema.json",
+ "/wholeNumberSchema": "whole_number_schema.json",
+ "/ECSignature": "ec_signature_schema.json",
+ "/signedOrderSchema": "signed_order_schema.json",
+ "/ecSignatureParameterSchema": (
+ "ec_signature_parameter_schema.json" + ""
+ ),
+ }
+ jsonschema.RefResolver.__init__(self, "", "")
+
+ def resolve_from_url(self, url: str) -> str:
+ """Resolve the given URL.
+
+ :param url: a string representing the URL of the JSON schema to fetch.
+ :returns: a string representing the deserialized JSON schema
+ :raises jsonschema.ValidationError: when the resource associated with
+ `url` does not exist.
+ """
+ ref = url.replace("file://", "")
+ if ref in self.ref_to_file:
+ return json.loads(
+ resource_string(
+ "zero_ex.json_schemas", f"schemas/{self.ref_to_file[ref]}"
+ )
+ )
+ raise jsonschema.ValidationError(
+ f"Unknown ref '{ref}'. "
+ + f"Known refs: {list(self.ref_to_file.keys())}."
+ )
+
+
+# Instantiate the `_LocalRefResolver()` only once so that `assert_valid()` can
+# perform multiple schema validations without reading from disk the schema
+# every time.
+_LOCAL_RESOLVER = _LocalRefResolver()
+
+
def assert_valid(data: Mapping, schema_id: str) -> None:
"""Validate the given `data` against the specified `schema`.
@@ -24,38 +69,6 @@ def assert_valid(data: Mapping, schema_id: str) -> None:
... )
"""
# noqa
- class LocalRefResolver(jsonschema.RefResolver):
- """Resolve package-local JSON schema id's."""
-
- def __init__(self):
- self.ref_to_file = {
- "/addressSchema": "address_schema.json",
- "/hexSchema": "hex_schema.json",
- "/orderSchema": "order_schema.json",
- "/wholeNumberSchema": "whole_number_schema.json",
- "/ECSignature": "ec_signature_schema.json",
- "/ecSignatureParameterSchema": (
- "ec_signature_parameter_schema.json" + ""
- ),
- }
- jsonschema.RefResolver.__init__(self, "", "")
-
- def resolve_from_url(self, url):
- """Resolve the given URL."""
- ref = url.replace("file://", "")
- if ref in self.ref_to_file:
- return json.loads(
- resource_string(
- "zero_ex.json_schemas",
- f"schemas/{self.ref_to_file[ref]}",
- )
- )
- raise jsonschema.ValidationError(
- f"Unknown ref '{ref}'. "
- + f"Known refs: {list(self.ref_to_file.keys())}."
- )
- resolver = LocalRefResolver()
- jsonschema.validate(
- data, resolver.resolve_from_url(schema_id), resolver=resolver
- )
+ _, schema = _LOCAL_RESOLVER.resolve(schema_id)
+ jsonschema.validate(data, schema, resolver=_LOCAL_RESOLVER)
diff --git a/python-packages/order_utils/stubs/jsonschema/__init__.pyi b/python-packages/order_utils/stubs/jsonschema/__init__.pyi
index 762b58b22..442e2f65e 100644
--- a/python-packages/order_utils/stubs/jsonschema/__init__.pyi
+++ b/python-packages/order_utils/stubs/jsonschema/__init__.pyi
@@ -1,5 +1,11 @@
-from typing import Any, Dict
+from typing import Any, Dict, Tuple
-class RefResolver: pass
+
+class RefResolver:
+ def resolve(self, url: str) -> Tuple[str, Dict]:
+ ...
+
+
+class ValidationError(Exception): pass
def validate(instance: Any, schema: Dict, cls=None, *args, **kwargs) -> None: pass
diff --git a/python-packages/order_utils/test/test_doctest.py b/python-packages/order_utils/test/test_doctest.py
index f692b3b6c..297f75e75 100644
--- a/python-packages/order_utils/test/test_doctest.py
+++ b/python-packages/order_utils/test/test_doctest.py
@@ -2,16 +2,17 @@
from doctest import testmod
import pkgutil
+import importlib
import zero_ex
def test_all_doctests():
"""Gather zero_ex.* modules and doctest them."""
- for (importer, modname, _) in pkgutil.walk_packages(
+ for (_, modname, _) in pkgutil.walk_packages(
path=zero_ex.__path__, prefix="zero_ex."
):
- module = importer.find_module(modname).load_module(modname)
+ module = importlib.import_module(modname)
print(module)
(failure_count, _) = testmod(module)
assert failure_count == 0
diff --git a/python-packages/order_utils/test/test_json_schemas.py b/python-packages/order_utils/test/test_json_schemas.py
new file mode 100644
index 000000000..51cecbd4f
--- /dev/null
+++ b/python-packages/order_utils/test/test_json_schemas.py
@@ -0,0 +1,23 @@
+"""Tests of zero_ex.json_schemas"""
+
+
+from zero_ex.order_utils import make_empty_order
+from zero_ex.json_schemas import _LOCAL_RESOLVER, assert_valid
+
+
+def test_assert_valid_caches_resources():
+ """Test that the JSON ref resolver in `assert_valid()` caches resources
+
+ In order to test the cache we much access the private class of
+ `json_schemas` and reset the LRU cache on `_LocalRefResolver`.
+ For this to happen, we need to disable errror `W0212`
+ on _LOCAL_RESOLVER
+ """
+ _LOCAL_RESOLVER._remote_cache.cache_clear() # pylint: disable=W0212
+
+ assert_valid(make_empty_order(), "/orderSchema")
+ cache_info = (
+ _LOCAL_RESOLVER._remote_cache.cache_info() # pylint: disable=W0212
+ )
+ assert cache_info.currsize == 4
+ assert cache_info.hits == 10
diff --git a/tsconfig.json b/tsconfig.json
index eaaca8e4e..3d2dc6da7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -30,11 +30,10 @@
{ "path": "./packages/contract-addresses" },
{ "path": "./packages/contract-artifacts" },
{ "path": "./packages/contract-wrappers" },
- { "path": "./packages/contracts" },
+ { "path": "./contracts/core" },
{ "path": "./packages/dev-utils" },
{ "path": "./packages/ethereum-types" },
{ "path": "./packages/fill-scenarios" },
- { "path": "./packages/instant" },
{ "path": "./packages/json-schemas" },
{ "path": "./packages/metacoin" },
{ "path": "./packages/migrations" },
@@ -57,5 +56,8 @@
// Skipping website because it requires allowJs: false and this is
// incompatible with project references.
// { "path": "./packages/website" }
+ // Skipping instant because it only produces a UMD bundle
+ // which it uses webpack to create
+ // { "path": "./packages/instant" },
]
}
diff --git a/yarn.lock b/yarn.lock
index fe12cf2e3..552d31e61 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -478,7 +478,7 @@
dependencies:
npm-registry-client "7.0.9"
-"@babel/code-frame@^7.0.0-beta.35":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35":
version "7.0.0"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
dependencies:
@@ -1253,8 +1253,8 @@
bignumber.js "7.2.1"
"@types/ethereumjs-abi@^0.6.0":
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/@types/ethereumjs-abi/-/ethereumjs-abi-0.6.0.tgz#72d21083a36d9288821b62905e04b15e0c12175d"
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/@types/ethereumjs-abi/-/ethereumjs-abi-0.6.1.tgz#68dad80888a6e9f12b04f85adcb9cad2cb825e5c"
dependencies:
"@types/node" "*"
@@ -1347,7 +1347,7 @@
"@types/js-combinatorics@^0.5.29":
version "0.5.29"
- resolved "https://registry.yarnpkg.com/@types/js-combinatorics/-/js-combinatorics-0.5.29.tgz#47a7819a0b6925b6dc4bd2c2278a7e6329b29387"
+ resolved "http://registry.npmjs.org/@types/js-combinatorics/-/js-combinatorics-0.5.29.tgz#47a7819a0b6925b6dc4bd2c2278a7e6329b29387"
"@types/jsonschema@^1.1.1":
version "1.1.1"
@@ -1890,28 +1890,18 @@ acorn-globals@^4.1.0:
acorn "^6.0.1"
acorn-walk "^6.0.1"
-acorn-jsx@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
- dependencies:
- acorn "^3.0.4"
+acorn-jsx@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e"
acorn-walk@^6.0.1:
version "6.1.0"
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.0.tgz#c957f4a1460da46af4a0388ce28b4c99355b0cbc"
-acorn@^3.0.4:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
-
acorn@^5.0.0:
version "5.5.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
-acorn@^5.5.0:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
-
acorn@^5.5.3, acorn@^5.6.2:
version "5.7.3"
resolved "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
@@ -1920,6 +1910,10 @@ acorn@^6.0.1:
version "6.0.2"
resolved "https://registry.npmjs.org/acorn/-/acorn-6.0.2.tgz#6a459041c320ab17592c6317abbfdf4bbaa98ca4"
+acorn@^6.0.2:
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754"
+
aes-js@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d"
@@ -1928,6 +1922,10 @@ aes-js@^0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-0.2.4.tgz#94b881ab717286d015fa219e08fb66709dda5a3d"
+aes-js@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a"
+
agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
@@ -1944,10 +1942,6 @@ ajv-errors@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
-ajv-keywords@^2.1.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
-
ajv-keywords@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be"
@@ -1959,7 +1953,7 @@ ajv@^4.9.1:
co "^4.6.0"
json-stable-stringify "^1.0.1"
-ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0:
+ajv@^5.1.0, ajv@^5.3.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies:
@@ -1977,6 +1971,15 @@ ajv@^6.1.0:
json-schema-traverse "^0.3.0"
uri-js "^3.0.2"
+ajv@^6.5.3:
+ version "6.5.5"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.5.tgz#cf97cdade71c6399a92c6d6c4177381291b781a1"
+ dependencies:
+ fast-deep-equal "^2.0.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -2051,9 +2054,9 @@ ansi@^0.3.0, ansi@~0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21"
-antlr4@4.7.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.0.tgz#297f956ddc06f83397fc0990ecf2e0cf20bfbbee"
+antlr4@4.7.1:
+ version "4.7.1"
+ resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773"
any-observable@^0.3.0:
version "0.3.0"
@@ -3465,7 +3468,7 @@ bs-logger@0.x:
dependencies:
fast-json-stable-stringify "^2.0.0"
-bs58@=4.0.1:
+bs58@=4.0.1, bs58@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
dependencies:
@@ -3488,6 +3491,14 @@ bs58check@^1.0.8:
bs58 "^3.1.0"
create-hash "^1.1.0"
+bs58check@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
+ dependencies:
+ bs58 "^4.0.0"
+ create-hash "^1.1.0"
+ safe-buffer "^5.1.2"
+
bser@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
@@ -3689,7 +3700,7 @@ caller-path@^0.1.0:
callsites@^0.2.0:
version "0.2.0"
- resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
+ resolved "http://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
callsites@^2.0.0:
version "2.0.0"
@@ -4201,6 +4212,10 @@ commander@2.15.1, commander@^2.12.1, commander@^2.8.1:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
+commander@2.18.0:
+ version "2.18.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
+
commander@^2.15.1:
version "2.19.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
@@ -4619,14 +4634,14 @@ cross-fetch@^2.1.0:
node-fetch "2.1.1"
whatwg-fetch "2.0.3"
-cross-spawn@^4:
+cross-spawn@^4, cross-spawn@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41"
dependencies:
lru-cache "^4.0.1"
which "^1.2.9"
-cross-spawn@^5.0.1, cross-spawn@^5.1.0:
+cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
dependencies:
@@ -4926,6 +4941,12 @@ debug@3.1.0, debug@=3.1.0, debug@^3.1.0:
dependencies:
ms "2.0.0"
+debug@^4.0.1:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87"
+ dependencies:
+ ms "^2.1.1"
+
debuglog@^1.0.1:
version "1.0.1"
resolved "http://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
@@ -5116,18 +5137,6 @@ defined@^1.0.0, defined@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
-del@^2.0.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
- dependencies:
- globby "^5.0.0"
- is-path-cwd "^1.0.0"
- is-path-in-cwd "^1.0.0"
- object-assign "^4.0.1"
- pify "^2.0.0"
- pinkie-promise "^2.0.0"
- rimraf "^2.2.8"
-
del@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
@@ -5384,6 +5393,14 @@ dot-prop@^4.1.0, dot-prop@^4.2.0:
dependencies:
is-obj "^1.0.0"
+dotenv-cli@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-1.4.0.tgz#e8e80830ed88b48a03b5eb7ec26147ca717f7409"
+ dependencies:
+ cross-spawn "^4.0.0"
+ dotenv "^4.0.0"
+ minimist "^1.1.3"
+
dotenv@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
@@ -5687,13 +5704,6 @@ escodegen@^1.9.1:
optionalDependencies:
source-map "~0.6.1"
-eslint-scope@^3.7.1:
- version "3.7.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
- dependencies:
- esrecurse "^4.1.0"
- estraverse "^4.1.1"
-
eslint-scope@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172"
@@ -5701,59 +5711,70 @@ eslint-scope@^4.0.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"
+eslint-utils@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512"
+
eslint-visitor-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
-eslint@^4.19.1:
- version "4.19.1"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300"
+eslint@^5.6.0:
+ version "5.9.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.9.0.tgz#b234b6d15ef84b5849c6de2af43195a2d59d408e"
dependencies:
- ajv "^5.3.0"
- babel-code-frame "^6.22.0"
+ "@babel/code-frame" "^7.0.0"
+ ajv "^6.5.3"
chalk "^2.1.0"
- concat-stream "^1.6.0"
- cross-spawn "^5.1.0"
- debug "^3.1.0"
+ cross-spawn "^6.0.5"
+ debug "^4.0.1"
doctrine "^2.1.0"
- eslint-scope "^3.7.1"
+ eslint-scope "^4.0.0"
+ eslint-utils "^1.3.1"
eslint-visitor-keys "^1.0.0"
- espree "^3.5.4"
- esquery "^1.0.0"
+ espree "^4.0.0"
+ esquery "^1.0.1"
esutils "^2.0.2"
file-entry-cache "^2.0.0"
functional-red-black-tree "^1.0.1"
glob "^7.1.2"
- globals "^11.0.1"
- ignore "^3.3.3"
+ globals "^11.7.0"
+ ignore "^4.0.6"
imurmurhash "^0.1.4"
- inquirer "^3.0.6"
- is-resolvable "^1.0.0"
- js-yaml "^3.9.1"
+ inquirer "^6.1.0"
+ is-resolvable "^1.1.0"
+ js-yaml "^3.12.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.3.0"
- lodash "^4.17.4"
- minimatch "^3.0.2"
+ lodash "^4.17.5"
+ minimatch "^3.0.4"
mkdirp "^0.5.1"
natural-compare "^1.4.0"
optionator "^0.8.2"
path-is-inside "^1.0.2"
pluralize "^7.0.0"
progress "^2.0.0"
- regexpp "^1.0.1"
+ regexpp "^2.0.1"
require-uncached "^1.0.3"
- semver "^5.3.0"
+ semver "^5.5.1"
strip-ansi "^4.0.0"
- strip-json-comments "~2.0.1"
- table "4.0.2"
- text-table "~0.2.0"
+ strip-json-comments "^2.0.1"
+ table "^5.0.2"
+ text-table "^0.2.0"
-espree@^3.5.4:
- version "3.5.4"
- resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
+espree@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-4.1.0.tgz#728d5451e0fd156c04384a7ad89ed51ff54eb25f"
dependencies:
- acorn "^5.5.0"
- acorn-jsx "^3.0.0"
+ acorn "^6.0.2"
+ acorn-jsx "^5.0.0"
+ eslint-visitor-keys "^1.0.0"
+
+esprima-extract-comments@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/esprima-extract-comments/-/esprima-extract-comments-1.1.0.tgz#0dacab567a5900240de6d344cf18c33617becbc9"
+ dependencies:
+ esprima "^4.0.0"
esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1:
version "2.7.3"
@@ -5767,7 +5788,7 @@ esprima@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
-esquery@^1.0.0:
+esquery@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
dependencies:
@@ -6084,6 +6105,19 @@ ethereumjs-wallet@0.6.0:
utf8 "^2.1.1"
uuid "^2.0.1"
+ethereumjs-wallet@~0.6.0:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.2.tgz#67244b6af3e8113b53d709124b25477b64aeccda"
+ dependencies:
+ aes-js "^3.1.1"
+ bs58check "^2.1.2"
+ ethereumjs-util "^5.2.0"
+ hdkey "^1.0.0"
+ safe-buffer "^5.1.2"
+ scrypt.js "^0.2.0"
+ utf8 "^3.0.0"
+ uuid "^3.3.2"
+
ethers@~4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65"
@@ -6380,6 +6414,13 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
+extract-comments@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/extract-comments/-/extract-comments-1.0.0.tgz#ad4e640704d8a9a124faf8776b47735ff092a593"
+ dependencies:
+ esprima-extract-comments "^1.0.1"
+ parse-code-context "^0.2.2"
+
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
@@ -6410,6 +6451,14 @@ fast-deep-equal@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
+fast-deep-equal@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
+
+fast-diff@^1.1.2:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
+
fast-glob@^2.0.2:
version "2.2.1"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.1.tgz#686c2345be88f3741e174add0be6f2e5b6078889"
@@ -6648,12 +6697,12 @@ flagged-respawn@^1.0.0:
resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7"
flat-cache@^1.2.1:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481"
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f"
dependencies:
circular-json "^0.3.1"
- del "^2.0.2"
graceful-fs "^4.1.2"
+ rimraf "~2.6.2"
write "^0.2.1"
flatten@^1.0.2:
@@ -6892,7 +6941,7 @@ ganache-core@0xProject/ganache-core#monorepo-dep:
ethereumjs-tx "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default"
ethereumjs-util "^5.2.0"
ethereumjs-vm "2.3.5"
- ethereumjs-wallet "0.6.0"
+ ethereumjs-wallet "~0.6.0"
fake-merkle-patricia-tree "~1.0.1"
heap "~0.2.6"
js-scrypt "^0.2.0"
@@ -7187,6 +7236,17 @@ glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glo
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@7.1.3, glob@^7.1.3:
+ version "7.1.3"
+ resolved "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
glob@^4.3.1:
version "4.5.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f"
@@ -7206,17 +7266,6 @@ glob@^5.0.15:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.1.3:
- version "7.1.3"
- resolved "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.4"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
glob@~3.1.21:
version "3.1.21"
resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd"
@@ -7260,25 +7309,14 @@ global@^4.3.0, global@~4.3.0:
min-document "^2.19.0"
process "~0.5.1"
-globals@^11.0.1:
- version "11.7.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673"
+globals@^11.7.0:
+ version "11.9.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249"
globals@^9.18.0:
version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
-globby@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
- dependencies:
- array-union "^1.0.1"
- arrify "^1.0.0"
- glob "^7.0.3"
- object-assign "^4.0.1"
- pify "^2.0.0"
- pinkie-promise "^2.0.0"
-
globby@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
@@ -7617,6 +7655,14 @@ hdkey@^0.7.0, hdkey@^0.7.1:
coinstring "^2.0.0"
secp256k1 "^3.0.1"
+hdkey@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-1.1.0.tgz#e74e7b01d2c47f797fa65d1d839adb7a44639f29"
+ dependencies:
+ coinstring "^2.0.0"
+ safe-buffer "^5.1.1"
+ secp256k1 "^3.0.1"
+
he@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
@@ -7889,14 +7935,14 @@ ignore-walk@^3.0.1:
dependencies:
minimatch "^3.0.4"
-ignore@^3.3.3, ignore@^3.3.7:
- version "3.3.10"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
-
ignore@^3.3.5:
version "3.3.7"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
+ignore@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+
image-size@~0.5.0:
version "0.5.5"
resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
@@ -8011,7 +8057,7 @@ inquirer@^0.8.2:
rx "^2.4.3"
through "^2.3.6"
-inquirer@^3.0.6, inquirer@^3.3.0:
+inquirer@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
dependencies:
@@ -8048,7 +8094,7 @@ inquirer@^5.1.0:
strip-ansi "^4.0.0"
through "^2.3.6"
-inquirer@^6.2.0:
+inquirer@^6.1.0, inquirer@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8"
dependencies:
@@ -8428,7 +8474,7 @@ is-relative@^1.0.0:
dependencies:
is-unc-path "^1.0.0"
-is-resolvable@^1.0.0:
+is-resolvable@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
@@ -8984,8 +9030,8 @@ js-base64@^2.1.9:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582"
js-combinatorics@^0.5.3:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/js-combinatorics/-/js-combinatorics-0.5.3.tgz#5da5a1c4632ec59fdf8d49dccfe59ef088122b15"
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/js-combinatorics/-/js-combinatorics-0.5.4.tgz#c92916b8f8171b64ecd7c4435b72cfabc803c756"
js-scrypt@^0.2.0:
version "0.2.0"
@@ -9024,7 +9070,7 @@ js-yaml@3.x, js-yaml@^3.4.2, js-yaml@^3.6.1, js-yaml@^3.7.0:
argparse "^1.0.7"
esprima "^4.0.0"
-js-yaml@^3.12.0, js-yaml@^3.9.0, js-yaml@^3.9.1:
+js-yaml@^3.12.0, js-yaml@^3.9.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
dependencies:
@@ -9124,6 +9170,10 @@ json-schema-traverse@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
@@ -9901,7 +9951,7 @@ 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.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.11:
version "4.17.11"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
@@ -10579,7 +10629,7 @@ ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
-ms@^2.0.0:
+ms@^2.0.0, ms@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
@@ -11551,6 +11601,10 @@ parse-asn1@^5.0.0:
evp_bytestokey "^1.0.0"
pbkdf2 "^3.0.3"
+parse-code-context@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/parse-code-context/-/parse-code-context-0.2.2.tgz#144b8afb7219482d7e88c1eb6a765596f3a6ac0d"
+
parse-entities@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.1.tgz#8112d88471319f27abae4d64964b122fe4e1b890"
@@ -12118,10 +12172,28 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+prettier-linter-helpers@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ dependencies:
+ fast-diff "^1.1.2"
+
+prettier-plugin-solidity@^1.0.0-alpha.4:
+ version "1.0.0-alpha.11"
+ resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-alpha.11.tgz#ef078a80dd471437693a0508de7691dd3ba814af"
+ dependencies:
+ extract-comments "^1.0.0"
+ prettier "^1.14.2"
+ solidity-parser-antlr "^0.3.1"
+
prettier@^1.11.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
+prettier@^1.14.2, prettier@^1.14.3:
+ version "1.15.2"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.2.tgz#d31abe22afa4351efa14c7f8b94b58bb7452205e"
+
pretty-bytes@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84"
@@ -13087,9 +13159,9 @@ regex-not@^1.0.0, regex-not@^1.0.2:
extend-shallow "^3.0.2"
safe-regex "^1.1.0"
-regexpp@^1.0.1:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab"
+regexpp@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
regexpu-core@^1.0.0:
version "1.0.0"
@@ -13341,7 +13413,7 @@ require-package-name@^2.0.1:
require-uncached@^1.0.3:
version "1.0.3"
- resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
+ resolved "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
dependencies:
caller-path "^0.1.0"
resolve-from "^1.0.0"
@@ -13451,7 +13523,7 @@ right-align@^0.1.1:
dependencies:
align-text "^0.1.1"
-rimraf@2, rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2:
+rimraf@2, rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
dependencies:
@@ -13512,6 +13584,21 @@ rollbar@^2.4.7:
optionalDependencies:
decache "^3.0.5"
+rollbar@^2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.5.0.tgz#dbb513eea34f1e3bae57611810c3e6df0ef50a12"
+ dependencies:
+ async "~1.2.1"
+ console-polyfill "0.3.0"
+ debug "2.6.9"
+ error-stack-parser "1.3.3"
+ json-stringify-safe "~5.0.0"
+ lru-cache "~2.2.1"
+ request-ip "~2.0.1"
+ uuid "3.0.x"
+ optionalDependencies:
+ decache "^3.0.5"
+
rst-selector-parser@^2.2.3:
version "2.2.3"
resolved "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"
@@ -13757,6 +13844,10 @@ semver@^4.1.0:
version "4.3.6"
resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
+semver@^5.5.1:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
+
semver@~5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.1.tgz#a3292a373e6f3e0798da0b20641b9a9c5bc47e19"
@@ -14096,7 +14187,7 @@ socks@~2.2.0:
ip "^1.1.5"
smart-buffer "^4.0.1"
-solc@0.4.24, solc@^0.4.24:
+solc@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.24.tgz#354f14b269b38cbaa82a47d1ff151723502b954e"
dependencies:
@@ -14126,21 +14217,40 @@ solc@^0.4.23:
semver "^5.3.0"
yargs "^4.7.1"
-solhint@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/solhint/-/solhint-1.2.1.tgz#59a1416cef94da38d587f768a73536d6e3403dd3"
+solc@^0.4.24:
+ version "0.4.25"
+ resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.25.tgz#06b8321f7112d95b4b903639b1138a4d292f5faa"
dependencies:
- antlr4 "4.7.0"
- commander "2.11.0"
- eslint "^4.19.1"
- glob "7.1.2"
- ignore "^3.3.7"
- lodash "^4.17.10"
+ fs-extra "^0.30.0"
+ memorystream "^0.3.1"
+ require-from-string "^1.1.0"
+ semver "^5.3.0"
+ yargs "^4.7.1"
+
+solhint@^1.2.1:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/solhint/-/solhint-1.4.0.tgz#59018cfc86e2fc268c8b520322ab1e0db1fdb94b"
+ dependencies:
+ antlr4 "4.7.1"
+ commander "2.18.0"
+ eslint "^5.6.0"
+ fast-diff "^1.1.2"
+ glob "7.1.3"
+ ignore "^4.0.6"
+ lodash "^4.17.11"
+ prettier-linter-helpers "^1.0.0"
+ optionalDependencies:
+ prettier "^1.14.3"
+ prettier-plugin-solidity "^1.0.0-alpha.4"
solidity-parser-antlr@^0.2.12:
version "0.2.12"
resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.2.12.tgz#1154f183d5cdda2c7677549ee584dbdb7fb2269c"
+solidity-parser-antlr@^0.3.1:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.3.2.tgz#1cf9d019280550a31299dc380e87a310dc4ca154"
+
sort-keys@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
@@ -14571,7 +14681,7 @@ strip-indent@^2.0.0:
version "2.0.0"
resolved "http://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
-strip-json-comments@~2.0.1:
+strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -14790,14 +14900,12 @@ symbol-tree@^3.2.2:
version "3.2.2"
resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
-table@4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
+table@^5.0.2:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/table/-/table-5.1.0.tgz#69a54644f6f01ad1628f8178715b408dc6bf11f7"
dependencies:
- ajv "^5.2.3"
- ajv-keywords "^2.1.0"
- chalk "^2.1.0"
- lodash "^4.17.4"
+ ajv "^6.5.3"
+ lodash "^4.17.10"
slice-ansi "1.0.0"
string-width "^2.1.1"
@@ -14976,7 +15084,7 @@ text-extensions@^1.0.0:
version "1.7.0"
resolved "http://registry.yarnpkg.com/text-extensions/-/text-extensions-1.7.0.tgz#faaaba2625ed746d568a23e4d0aacd9bf08a8b39"
-text-table@~0.2.0:
+text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -15719,6 +15827,12 @@ uri-js@^3.0.2:
dependencies:
punycode "^2.1.0"
+uri-js@^4.2.2:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+ dependencies:
+ punycode "^2.1.0"
+
urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
@@ -15783,6 +15897,10 @@ utf8@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96"
+utf8@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
+
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"